<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Kodono</title>
	<atom:link href="https://blog.kodono.info/wordpress/feed/" rel="self" type="application/rss+xml" />
	<link>https://blog.kodono.info/wordpress</link>
	<description>Pour tous les technophiles</description>
	<lastBuildDate>Mon, 09 Feb 2026 15:10:00 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.1</generator>
	<item>
		<title>Power Automate Flow: copy folder when one of its files is updated</title>
		<link>https://blog.kodono.info/wordpress/2026/02/09/power-automate-flow-copy-folder-when-one-of-its-files-is-updated/</link>
					<comments>https://blog.kodono.info/wordpress/2026/02/09/power-automate-flow-copy-folder-when-one-of-its-files-is-updated/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Mon, 09 Feb 2026 15:10:00 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2482</guid>

					<description><![CDATA[I had the scenario where when files are updated in a specific folder of a SharePoint Documents Library, then the folder should be copied to another document library. Because we don&#8217;t want the copy process to trigger for ever changes (like if we bulk upload files, or if someone is doing changes in live in [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I had the scenario where when files are updated in a specific folder of a SharePoint Documents Library, then the folder should be copied to another document library.</p>
<p>Because we don&#8217;t want the copy process to trigger for ever changes (like if we bulk upload files, or if someone is doing changes in live in a file), so here is my solution:</p>
<ol>
<li>Create a SharePoint List called &#8220;File Processing&#8221; with two fields:
<ul>
<li>A lookup field called &#8220;File&#8221; that is based on our source Document Library – make sure to select the option <b>&#8220;Enforce unique values&#8221;</b>;</li>
<li>A Date and Time field called &#8220;LastModified&#8221;.</li>
</ul>
</li>
<li>Create a Power Automate Flow that is manually triggered – we&#8217;ll call it with &#8220;Run a Child&#8221;: this one is only used to do the copy of the folder from source to destination</li>
<li>Create a Power Automate Flow that triggers when a file is created or modified (properties only):
<ol>
<li>When we confirm the file updates happened in the correct folder, then we add an entry in the &#8220;File Processing&#8221; list with the file ID and the file Modified date;</li>
<li>Terminate the flow.</li>
</ol>
</li>
<li>Create a Power Automate Flow that triggers when an item is created or modified in the &#8220;File Process&#8221; list:
<ol>
<li>Add a <b>&#8220;Delay&#8221;</b> of 5 minutes;</li>
<li>Add the action <b>&#8220;Get file properties&#8221;</b> from our source document library using the file ID from the item;</li>
<li>Check with a condition if the field &#8220;LastModified&#8221; is greater or equal to the Modified date of the file:
<ul>
<li>If <strong>yes</strong>, then use the action <b>&#8220;Get items&#8221;</b> on the &#8220;File Processing&#8221; list to see if we have any other items in the list: if no other items, run the child process to make the copy – and, finally, delete the current item in &#8220;File Processing&#8221; whatever there is other items or not;</li>
<li>If <strong>no</strong>, then update the field &#8220;LastModified&#8221; of the current item using the Modified date of the file.</li>
</ul>
</li>
</ol>
</li>
</ol>
<p>This solution permits to only trigger the copy process when there is no more files update in the folder.</p>
<p><b>Note:</b> if we nee to monitor different folders in the source library, we can add a lookup column in &#8220;File Process&#8221; that contains the folder ID, and when checking if there is no other items for the folder, we just add a condition based on the folder ID.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2026/02/09/power-automate-flow-copy-folder-when-one-of-its-files-is-updated/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>SharePoint REST API to copy a file from one site to another one</title>
		<link>https://blog.kodono.info/wordpress/2026/02/03/sharepoint-rest-api-to-copy-a-file-from-one-site-to-another-one/</link>
					<comments>https://blog.kodono.info/wordpress/2026/02/03/sharepoint-rest-api-to-copy-a-file-from-one-site-to-another-one/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 03 Feb 2026 16:31:54 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2473</guid>

					<description><![CDATA[There is a good explanation in this Gist, and make sure to read my comment also!]]></description>
										<content:encoded><![CDATA[<p>There is a good explanation in <a href="https://gist.github.com/zplume/21248c3a8a5f840a366722442cf9ee97">this Gist</a>, and make sure to read <a href="https://gist.github.com/zplume/21248c3a8a5f840a366722442cf9ee97?permalink_comment_id=5968375#gistcomment-5968375">my comment</a> also!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2026/02/03/sharepoint-rest-api-to-copy-a-file-from-one-site-to-another-one/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Power Automate Flow: force a file deletion and force a file replacement</title>
		<link>https://blog.kodono.info/wordpress/2026/02/03/power-automate-flow-force-a-file-deletion-and-replacement/</link>
					<comments>https://blog.kodono.info/wordpress/2026/02/03/power-automate-flow-force-a-file-deletion-and-replacement/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 03 Feb 2026 10:14:00 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2468</guid>

					<description><![CDATA[To force a file to be deleted in a SharePoint Document Library, using a Power Automate Flow, we can call the action &#8220;Send an HTTP Request to SharePoint&#8220;, with the below: Method: POST Uri: _api/web/Lists/GetByTitle(&#8216;Document Library Name&#8217;)/GetItemById(@{variables(&#8216;ItemID&#8217;)})/recycle Headers: Prefer: bypass-shared-lock To force a file to be replaced with new content in a SharePoint Document Library, [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>To force a file to be deleted in a SharePoint Document Library, using a Power Automate Flow, we can call the action &#8220;<strong>Send an HTTP Request to SharePoint</strong>&#8220;, with the below:</p>
<ul>
<li>Method: <strong>POST</strong></li>
<li>Uri: <strong>_api/web/Lists/GetByTitle(&#8216;Document Library Name&#8217;)/GetItemById(@{variables(&#8216;ItemID&#8217;)})/recycle</strong></li>
<li>Headers: <strong>Prefer: bypass-shared-lock</strong></li>
</ul>
<p>To force a file to be replaced with new content in a SharePoint Document Library, using a Power Automate Flow, we can call the action &#8220;<strong>Send an HTTP Request to SharePoint</strong>&#8220;, with the below:</p>
<ul>
<li>Method: <strong>POST</strong></li>
<li>Uri: <strong>_api/web/GetFolderByServerRelativeUrl(&#8216;/sites/SiteCollectionName/SubWebSiteName/&#8217;)/Files/add(url=&#8217;DestinationFileName.csv&#8217;,overwrite=true)</strong></li>
<li>Headers: <strong>Accept: application/json;odata=nometadata</strong> and <strong>Content-Type: application/json;odata=nometadata</strong></li>
<li>Body: <em>whatever body/content you want</em></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2026/02/03/power-automate-flow-force-a-file-deletion-and-replacement/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Set People Picker field in a SharePoint List Item using Microsoft Graph</title>
		<link>https://blog.kodono.info/wordpress/2026/02/02/set-people-picker-field-in-a-sharepoint-list-item-using-microsoft-graph/</link>
					<comments>https://blog.kodono.info/wordpress/2026/02/02/set-people-picker-field-in-a-sharepoint-list-item-using-microsoft-graph/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Mon, 02 Feb 2026 11:50:50 +0000</pubDate>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2462</guid>

					<description><![CDATA[When we want to use MS Graph to add an item into a SharePoint List that contains a People Picker, then here what needs to be done: Retrieve the Site ID for the root site collection, using https://graph.microsoft.com/v1.0/sites/[CORP].sharepoint.com:/sites/MySiteCollection Retrieve the Site ID for the target end website, using https://graph.microsoft.com/v1.0/sites/[CORP].sharepoint.com:/sites/MySiteCollection/MyParentSiteWeb/MyTargetWebsite/ Use the user&#8217;s email address to [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>When we want to use MS Graph to add an item into a SharePoint List that contains a People Picker, then here what needs to be done:</p>
<ol>
<li>Retrieve the Site ID for the root site collection, using <code>https://graph.microsoft.com/v1.0/sites/[CORP].sharepoint.com:/sites/MySiteCollection</code></li>
<li>Retrieve the Site ID for the target end website, using <code>https://graph.microsoft.com/v1.0/sites/[CORP].sharepoint.com:/sites/MySiteCollection/MyParentSiteWeb/MyTargetWebsite/</code></li>
<li>Use the user&#8217;s email address to retrieve the SharePoint User ID of that person from the User Information List, using <code>https://graph.microsoft.com/v1.0/sites/[ROOT_SITE_ID]/lists/User%20Information%20List/items?expand=fields&#038;filter=fields/EMail%20eq%20'[ENCODED_EMAIL]'&#038;top=1"</code> – the email must be encoded to be passed in an URL</li>
<li>If the user doesn&#8217;t exist in this SharePoint yet, then you have to use the REST API call to <code>https://[CORP].sharepoint.com/sites/MySiteCollection/MyParentSiteWeb/MyTargetWebsite/_api/web/ensureUser</code> with the payload <code>{"logonName":"user_email@corp.com"}</code> – you should get the User ID this time</li>
<li>Then we need to get the List ID of where we want to add the item, using <code>https://graph.microsoft.com/v1.0/sites/[TARGET_SIDE_ID]/lists/[LIST_NAME_ENCODED]</code></li>
<li>Finally, we can add the item using <code>https://graph.microsoft.com/v1.0/sites/[TARGET_SIDE_ID]/lists/[TARGET_LIST_ID]/items</code> with a specific value depending if it&#8217;s a multi-selection people picker field or not – let&#8217;s say the internal name for my field is &#8220;Requestor_x0020_Name&#8221;, then I have to append &#8220;LookupId&#8221; to this name:
<ul>
<li>for a single selection field, the payload contains:
<pre class="brush:json">{"Requestor_x0020_NameLookupId": "[USER_ID_FOUND_WITH_GRAPH]"}</pre>
<li>for a multi-selection field we have to pass the odata type, and put the User ID into an array, then the payload contains:
<pre class="brush:json">{
"Requestor_x0020_NamesLookupId@odata.type":"Collection(Edm.String)",
"Requestor_x0020_NamesLookupId": [ "[USER_ID_FOUND_WITH_GRAPH]" ]
}</pre>
</li>
</ul>
</li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2026/02/02/set-people-picker-field-in-a-sharepoint-list-item-using-microsoft-graph/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Égypte : croisière sur le Nil</title>
		<link>https://blog.kodono.info/wordpress/2025/10/12/egypte-croisiere-sur-le-nil/</link>
					<comments>https://blog.kodono.info/wordpress/2025/10/12/egypte-croisiere-sur-le-nil/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 12 Oct 2025 15:39:18 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Français]]></category>
		<category><![CDATA[Voyage]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2426</guid>

					<description><![CDATA[Nous avons pris, via Voyage Privé, un séjour all inclusive en Égypte, avec croisière sur le Nil. Bons à savoir Les euros sont acceptés de partout ! Le dollar américain également. Vous pouvez bien sûr retirer des livres égyptiennes, mais ce n&#8217;est pas forcément nécessaire. Par contre les toilettes sont payantes : elles coûtent 20 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Nous avons pris, via Voyage Privé, un séjour all inclusive en Égypte, avec croisière sur le Nil.</p>
<h2>Bons à savoir</h2>
<ul>
<li><strong>Les euros sont acceptés de partout !</strong> Le dollar américain également. Vous pouvez bien sûr retirer des livres égyptiennes, mais ce n&#8217;est pas forcément nécessaire. Par contre les toilettes sont payantes : elles coûtent 20 livres, et c&#8217;est là qu&#8217;on a le plus utilisé l&#8217;argent local (possibilité de payer en euros avec genre 50 centimes ou 1 €). On peut éviter les pourboires, mais si vous dites facilement &#8220;oui&#8221;, prévoyez beaucoup d&#8217;argent liquide car vous serez TRÈS sollicités…</li>
<li>Un coca (pour les touristes) coûtent environ 3 ou 4€, soit 200 ou 300 livres. Lorsque ce n&#8217;est pas un attrape-touristes, le prix est plutôt de 50 livres (1€)</li>
<li>Les égyptiens ne sont pas gentils par bonté d&#8217;âme, mais ils cherchent uniquement à vous soutirer de l&#8217;argent. Toutes les personnes que vous croiserez vous <strong>prendront pour une &#8220;vache à lait&#8221;</strong> et vous réclameront de l&#8217;argent pour un tout ou un rien&#8230; Il ne faut donc rien accepter de leur part, même si ça semble être un acte de gentillesse !</li>
<li>On peut se balader en <strong>short et t-shirt sans problèmes</strong>, que ce soit dans les rues ou dans les temples.</li>
<li>On se lève (très) tôt tout le long du séjour… Il fait également très chaud (~40°C début septembre)</li>
<li>Prendre une <strong>casquette</strong>, une <strong>gourde</strong> (pour conserver l&#8217;eau au frais pendant les visites), et un <strong>éventail</strong> pour supporter la chaleur.</li>
<li>La traversée sur le bateau peut parfois être bruyante (entre les matelots qui manœuvrent, les bruits des moteurs, ou autre), donc des <strong>bouchons d&#8217;oreilles</strong> peuvent être utiles. Par contre on trouve des rideaux aux fenêtres pour dormir dans &#8220;le noir&#8221;.</li>
<li>D&#8217;après notre guide, <strong>la meilleure période pour visiter l&#8217;Égypte est début décembre</strong>.</li>
<li>Comme souvent, la climatisation est poussée à fond dans les aéroports, donc <strong>prévoir veste et écharpe</strong> si vous devez patienter là bas !</li>
</ul>
<p>Pour la croisière sur le Nul, on a plusieurs sortes de bateaux qui naviguent :</p>
<ul>
<li>Les &#8220;petits&#8221; : nous avions choisi cette option, avec seulement 6 cabines à bord (donc 12 personnes) – prestations luxueuses avec un personnel de bord aux petits soins. Nous étions 6 francophones, donc les sorties se faisaient entre nous seulement avec un guide dédié et présent toute la semaine. Idéal pour voyager dans de bonnes conditions.</li>
<li>Les &#8220;moyens&#8221; : plus imposants, ils restent à taille humaine et proposent plus de prestations, comme une piscine à bord. Cela peut être le bon choix, mais il faut s&#8217;assurer que le bateau ne dépasse pas les 30 ou 40 voyageurs.</li>
<li>Les &#8220;gros&#8221; : ceux-ci peuvent transporter plus de 100 personnes. On les voit passer avec un gros nuage de gasoil qui s&#8217;échappe par l&#8217;arrière et inonde les ponts supérieurs. On se retrouve à plusieurs cars pour partir visiter les sites. Expérience non recommandée.</li>
</ul>
<h2>Notre séjour</h2>
<ul>
<li><strong>Jour 1</strong> – Arrivée avec prise en charge dès la sortie de l&#8217;avion. Nous avons été conduits à notre hôtel (<a href="https://maps.app.goo.gl/FHRV6KHzKJvHe8W68">Barcelo Cairo Pyramids</a>). Un hôtel pas fou-fou, mais le rooftop offrait une vue pas trop mauvaise des pyramides, même si un peu lointaine. Il y avait une piscine. Par contre, Le Caire est une ville assez &#8220;chaotique&#8221; et pauvre, et on ne se voyait pas sortir seuls dans les rues&#8230;</li>
<li><strong>Jour 2 (matin)</strong> – Ce fut la visite des pyramides, avec un départ à 6h du matin. Des bus nous transportent sur les différents points d&#8217;intérêt du site des pyramides. Sur place on peut faire une balade de dromadaire pour 25€ (tarif très cher), ou balade en calèche (non recommandée) : mais cela ne dure qu&#8217;une quinzaine de minutes, et les bêtes ne sont pas vraiment bien traitées… Il est possible d&#8217;entrer dans certaines pyramides (selon le tarif payé à l&#8217;entrée) : il y fait très chaud et humide, à déconseiller aux claustrophobes ! On termine la visite par le sphinx. Si vous en avez l&#8217;occasion, et tant que vous êtes sur le site, allez vers le <a href="https://maps.app.goo.gl/mKi1aM7eszaSKjRW9">9 Pyramids Lounge</a>, et baladez vous dans les environs pour prendre de belles prises de vue des pyramides. Notre guide (très mauvais) nous a ensuite conduit dans un &#8220;musée du papyrus&#8221; où on nous montre comment sont faits les papyrus, mais le but est surtout de nous vendre des tableaux en papyrus !</li>
<li><strong>Jour 2 (après-midi)</strong> – notre guide nous a emmené dans un restaurant pour &#8220;étrangers&#8221;. Pas très intéressant. Après avoir payé notre repas en euros, nous avions quartier libre : nous avons <strong>commandé un Uber</strong> (l&#8217;appli est bien pratique là bas) pour nous rendre au (nouveau) <a href="https://maps.app.goo.gl/RSxF4gKhGrhrVoHg9">Grand Musée Égyptien</a> qui est magnifique ! Les tickets peuvent être achetés sur place. On y trouve aussi des consignes gratuites pour laisser bagages, perches et autres.</li>
<li><strong>Jour 2 (soir)</strong> – nous ne l&#8217;avons pas fait, mais nos compagnons de voyage ont diné au <a href="https://maps.app.goo.gl/tSfNcYEccpybzC2SA">Rootfop 7000</a> et la vue sur les pyramides au moment du coucher du soleil était apparemment extraordinaire.</li>
<li><strong>Jour 3</strong> – levé à 3h du matin pour partir à l&#8217;aéroport avec un vol en direction de Luxor. Arrivée au bateau, puis visite du <a href="https://maps.app.goo.gl/inF5yV9d3QE5F2H78">temple de Karnak</a> et du <a href="https://maps.app.goo.gl/AFjnsZuLxWMgBFES7">temple d&#8217;Amon</a> : tous les deux sont magnifiques ! Il parait que visiter Karnak de nuit est une très belle expérience grâce aux éclairages qui subliment les lieux…</li>
<li><strong>Jour 4</strong> – levé aux aurores pour aller dans la <a href="https://maps.app.goo.gl/GQmarFzwqQMNiARN7">vallée des rois</a> (63 tombes réunies au même endroit, en plein désert de rocs). Nous avons exploré les tombes de Ramsès I, Ramsès IX &#038; Mérenptah : le billet ne permet de <strong>visiter que 3 tombes</strong>, il faudra donc faire confiance aux conseils de votre guide. Pour entrer dans la tombe de Toutânkhamon, il faut acheter une option supplémentaire. L&#8217;accès aux tombeaux n&#8217;est pas du tout problématique car ce sont de larges couloirs aux murs entièrement décorés de hiéroglyphes multicouleurs.<br />À noter que ce jour-là, d&#8217;autres personnes sont parties à 4h du matin pour aller faire de la montgolfière : l&#8217;intérêt principal est qu&#8217;on vole avec une dizaine d&#8217;autres ballons, mais il y a beaucoup de monde et l&#8217;endroit n&#8217;est pas fou-fou pour cette expérience à mon avis.<br />Nous avons également visité le <a href="https://maps.app.goo.gl/t42LQqQ4d5rZTGTf7">temple funéraire de la reine Hatchepsout</a> qui est assez impressionnant.<br />Enfin nous sommes allés dans l&#8217;une des nombreuses boutiques d’albâtre, cette pierre récoltée dans les environs. Ils proposent de beaux produits, mais les tarifs sont très chers (25€ pour un petit scarabée)&#8230;</li>
<li><strong>Jour 5</strong> – Notre bateau s&#8217;est arrêté à Edfou afin de visiter le magnifique <a href="https://maps.app.goo.gl/8txBQV9dvvJe2B9YA">temple d&#8217;Horus</a>. Puis le soir, nous avons fait un stop sur une petite île au milieu du Nil afin d&#8217;y diner au barbecue. Ce fut également l&#8217;occasion <strong>de se baigner dans le fleuve</strong> : il a fallu pas mal insister auprès du capitaine, mais on ne regrette pas cette expérience.</li>
<li><strong>Jour 6</strong> – Nous arrivons au bout de la portion navigable du Nil, au pied du barrage d&#8217;Assouan. Nous avons visite le <a href="https://maps.app.goo.gl/jkS93jHMfPX6k2cDA">temple de Sobek et Haroëris</a> à Kom Ombo (pas extraodinaire), puis le magnifique <a href="https://maps.app.goo.gl/rn9pYD3cg6KYVDR4A">temple d&#8217;Isis</a> à Philae, sur son bout d&#8217;île ! Petit tour au barrage : intéressant d&#8217;en connaître l&#8217;histoire, mais pas grand chose à voir. Et pour finir, un tour de felouque (sympa pour faire passer le temps).</li>
<li><strong>Jour 7</strong> – levé dans la nuit pour se rendre aux extraordinaires <a href="https://maps.app.goo.gl/ArDrp6KCMbAYzaZq7">temples d&#8217;Abou Simbel</a> ! La route est longue (3h30 au milieu du désert), mais le lieu est spectaculaire.</li>
</ul>
<p>On considère que le voyage a été bien complet. L&#8217;expérience sur le bateau fut particulièrement agréable, même si avec les 40°C au quotidien, on aurait apprécié une piscine pour se rafraichir.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/10/12/egypte-croisiere-sur-le-nil/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to pass a parameter from the URL to a SharePoint Form (2025)</title>
		<link>https://blog.kodono.info/wordpress/2025/09/28/how-to-pass-a-parameter-from-the-url-to-a-sharepoint-form-2025/</link>
					<comments>https://blog.kodono.info/wordpress/2025/09/28/how-to-pass-a-parameter-from-the-url-to-a-sharepoint-form-2025/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 28 Sep 2025 16:33:07 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2439</guid>

					<description><![CDATA[For a few months, there is a new way to display a new form for a SharePoint List, using Microsoft Forms: This kind of form can be useful for intake requests or surveys. But how to pass a parameter in the URL to apply it to a field in the form? Here is the solution [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>For a few months, there is a new way to display a new form for a SharePoint List, using Microsoft Forms:<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar.png"><img decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar-1024x54.png" alt="" width="660" height="35" class="aligncenter size-large wp-image-2440" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar-1024x54.png 1024w, https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar-300x16.png 300w, https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar-768x41.png 768w, https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/sp_toolbar.png 1056w" sizes="(max-width: 660px) 100vw, 660px" /></a></p>
<p>This kind of form can be useful for intake requests or surveys. But how to pass a parameter in the URL to apply it to a field in the form?</p>
<p>Here is the solution I deployed:</p>
<ol>
<li>Create a Power Automate Flow that will replace the default value of the field with a defined value</li>
<li>Create a Power App to intercept the parameter from the URL, to trigger the above flow, and to redirect the user to the form</li>
<li>The user will see the defined value in the field because we use the default value</li>
<li>A few seconds later, the flow will change back the default value to blank</li>
</ol>
<p>It works well. The only downside is that we cannot have more than 1 person clicking on the link at the same time (and within a range of 15 seconds).</p>
<h2>1) The Power Automate Flow</h2>
<p><a href="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/flow.png"><img fetchpriority="high" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/flow.png" alt="" width="327" height="590" class="aligncenter size-full wp-image-2442" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/flow.png 327w, https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/flow-166x300.png 166w" sizes="(max-width: 327px) 100vw, 327px" /></a></p>
<p>The flow is called by a Power App with a string parameter.</p>
<p>Then we do an HTTP call to SharePoint with the below details:</p>
<ul>
<li><strong>Method</strong>: PATCH</li>
<li><strong>Uri</strong>: _api/web/lists/GetByTitle(&#8216;SHAREPOINT LIST NAME&#8217;)/Fields/GetByInternalNameOrTitle(&#8216;FIELD_ID&#8217;)</li>
<li><b>Headers</b>: &#8220;Accept: application/json;odata=nometadata&#8221;, &#8220;Content-Type: application/json;odata=nometadata&#8221;, and &#8220;IF-MATCH: *&#8221;</li>
<li><b>Body</b>: we use a JSON <code>{"DefaultValue": "VALUE PROVIDED BY APP"}</code></li>
</ul>
<p>By default value, I mean this information (legacy SharePoint interface):<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/defaultvalue.png"><img decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/defaultvalue.png" alt="" width="220" height="74" class="aligncenter size-full wp-image-2447" /></a></p>
<p>Once the default value of the field has been updated, we respond back to the Power App.</p>
<p>We add a 15-second delay.</p>
<p>And finally, we do another HTTP call to SharePoint, with the same info, except for the body that becomes <code>{"DefaultValue":""}</code> to reset the default value.</p>
<h2>2) The Power App</h2>
<p><a href="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/powerapp.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/powerapp.png" alt="" width="491" height="129" class="aligncenter size-full wp-image-2444" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/powerapp.png 491w, https://blog.kodono.info/wordpress/wp-content/uploads/2025/09/powerapp-300x79.png 300w" sizes="auto, (max-width: 491px) 100vw, 491px" /></a></p>
<p>Instead of giving the direct link to the form to the user, we&#8217;ll give them a link to the Power App. The URL must contains the param value (e.g. &#8220;&#038;Title=Code_123456&#8221;).</p>
<p>The App can have a short text (e.g. &#8220;click the below button to get to the survey&#8221;) with a button &#8220;Go to the form&#8221;.</p>
<p>When the user clicks on the button, it will trigger the flow we created in step 1, and then it will redirect the user to the form.</p>
<p>To do so, for <code>OnSelect</code> on the button:</p>
<pre>
// we can change the aspect of the button to indicate something is happening
Set(buttonLabel, "Loading survey…");
Set(buttonMode, DisplayMode.Disabled);

// decode the "Title" param that is passed in the URL
Set(
  titleParam,
  Substitute(
    Substitute(
        Substitute(
        Substitute(
            Substitute(Param("Title"), "%3A", ":"),
            "%2F", "/"
        ),
        "%3F", "?"
        ),
        "%3D", "="
    ),
    "%26", "&#038;"
    )
);

// call the Power Automate Flow
'PowerApps–SetURLParamasDefaultValue'.Run(titleParam);

// then redirect to the form
Launch(urlToForm, {}, LaunchTarget.Replace);
</pre>
<h2>3) Let&#8217;s the magic happen</h2>
<p>When the user clicks on the button:</p>
<ul>
<li>the flow runs,</li>
<li>the flow changes the default value using the provided param,</li>
<li>the flow says to the app to proceed,</li>
<li>the app redirects the user to the form,</li>
<li>the form shows up with the field that already contains the provided value</li>
<li>15 seconds later (the time for the form to load), the flow resets the default value</li>
<ol>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/09/28/how-to-pass-a-parameter-from-the-url-to-a-sharepoint-form-2025/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mise à jour d’un serveur Kimsufi (OVH) depuis Debian 11 (Bullseye) vers Debian 12 (Bookworm)</title>
		<link>https://blog.kodono.info/wordpress/2025/05/04/mise-a-jour-dun-serveur-kimsufi-ovh-depuis-debian-11-bullseye-vers-debian-12-bookworm/</link>
					<comments>https://blog.kodono.info/wordpress/2025/05/04/mise-a-jour-dun-serveur-kimsufi-ovh-depuis-debian-11-bullseye-vers-debian-12-bookworm/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 04 May 2025 10:50:10 +0000</pubDate>
				<category><![CDATA[Debug]]></category>
		<category><![CDATA[Français]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2413</guid>

					<description><![CDATA[Cette mise à jour est similaire à ce qui a pu être fait précédemment. Je vais essayer d’expliquer brièvement les étapes à suivre. D&#8217;abord s&#8217;assurer de la version actuelle de Debian avec la commande suivante : lsb_release -a On vérifie les problèmes liés à la mise à jour. Il est recommandé d&#8217;utiliser le programme /usr/bin/script [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Cette mise à jour est similaire à ce qui a pu être fait précédemment.</p>
<p>Je vais essayer d’expliquer brièvement <a href="https://www.debian.org/releases/stable/i386/release-notes/ch-upgrading.fr.html">les étapes à suivre</a>.</p>
<ol>
<li>D&#8217;abord s&#8217;assurer de la version actuelle de Debian avec la commande suivante :
<pre class="brush:powershell">lsb_release -a</pre>
</li>
<li>On vérifie <a href="https://www.debian.org/releases/stable/i386/release-notes/ch-information.fr.html">les problèmes liés à la mise à jour</a>.</li>
<li>Il est recommandé d&#8217;utiliser le programme <code>/usr/bin/script</code> pour enregistrer une transcription de la session de mise à niveau. Ainsi, quand un problème survient, on a un enregistrement de ce qui s&#8217;est passé. Pour démarrer un enregistrement, taper :
<pre class="brush:powershell">script -t 2>~/upgrade-debian.time -a ~/upgrade-debian.script</pre>
</li>
<li>On va effectuer une mise à jour des paquets avec <code>apt-get update &#038;& apt-get upgrade</code></li>
<li>On va sauvegarder les données :
<pre class="brush:powershell">mkdir /root/svg_special; cp -R /var/lib/dpkg /root/svg_special/; cp /var/lib/apt/extended_states /root/svg_special/; dpkg --get-selections "*" > /root/svg_special/dpkg_get_selection; cp -R /etc /root/svg_special/etc</pre>
</li>
<li>Si vous utilisez MariaDB, il peut être bien de faire une sauvegarde complète des bases de données :
<pre class="brush:powershell">mysqldump -u root -p --all-databases > /root/svg_special/backup-bdd.sql</pre>
</li>
<li>Ensuite il est conseillé d&#8217;utiliser <code>screen</code> pour pouvoir se reconnecter (avec <code>screen -r</code>) à en cas de déconnexion :
<pre class="brush:powershell">screen</pre>
</li>
</li>
<li>Le processus de mise à niveau décrit <a href="https://www.debian.org/releases/stable/i386/release-notes/ch-upgrading.fr.html">sur le site de Debian</a> a été conçu pour des mises à niveau des systèmes « purs » sans paquet provenant d&#8217;autres sources. Pour une meilleure fiabilité du processus de mise à niveau, vous pourriez vouloir supprimer ces paquets tiers du système avant de commencer la mise à niveau :
<pre class="brush:bash">aptitude search '~i(!~ODebian)'</pre>
</li>
<li>On peut éventuellement purger les vieux paquets obsolètes. Pour cela on va d&#8217;abord les lister, puis on peut les purger si tout semble bon:
<pre class="brush:bash">aptitude search '~o'
aptitude purge '~o'</pre>
</li>
<li>On peut lancer la commande <code>dpkg --audit</code> pour s&#8217;assurer que tout est bon avant la migration. On peut également taper <code>dpkg --get-selections "*" | more</code> et vérifier qu&#8217;aucun paquet n&#8217;est en <em>on hold</em></li>
<li>Maintenant il faut remplacer tous les &#8220;bullseye&#8221; de <code>/etc/apt/sources.list</code> par des &#8220;bookworm&#8221;.<br />
On pourra par exemple utiliser la commande suivante : </p>
<pre class="brush:powershell">sed -i 's/bullseye/bookworm/g' /etc/apt/sources.list</pre>
<p>Si vous utilisez le dépôt &#8220;non-free&#8221; de Debian 11 (ouvrez le fichier <code>/etc/apt/sources.list</code> pour voir si vous avez des lignes qui contiennent le mot-clé &#8220;non-free&#8221;), alors il y a une modification supplémentaire à effectuer : il faut ajouter &#8220;<strong>non-free-firmware</strong>&#8221; à la suite du mot-clé &#8220;non-free&#8221;.<br />
Par exemple, la ligne <code>deb http://security.debian.org/debian-security bookworm-security main contrib non-free</code> deviendra <code>deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware</code><br />
On pourra par exemple utiliser la commande suivante : </p>
<pre class="brush:powershell">sed -i 's/non-free/non-free non-free-firmware/g' /etc/apt/sources.list</pre>
<p>On vérifiera aussi les fichiers qui se trouvent dans <code>/etc/apt/sources.list.d</code> afin <strong>d&#8217;y effectuer les mêmes modifications</strong>.</li>
<li>On passe aux choses sérieuses, en commençant par mettre à jour les listes des paquets :
<pre class="brush:powershell">apt-get update</pre>
</li>
<li>On va vérifier qu&#8217;on a la place suffisante (un message explicite apparait sinon) :
<pre class="brush:powershell">apt -o APT::Get::Trivial-Only=true full-upgrade</pre>
</li>
<li>On va maintenant faire une mise à jour minimale :
<pre class="brush:powershell">apt-get upgrade</pre>
</li>
<li>Et à partir de là le système va vous questionner&#8230; en général choisir l&#8217;option par défaut si vous ne savez pas quoi répondre</li>
<li>Puis on continue avec
<pre class="brush:powershell">apt full-upgrade</pre>
</li>
</ol>
<p>Cette dernière étape va durer un certain temps. Une fois terminé, vous pouvez redémarrer le serveur pour s&#8217;assurer que tout va bien.</p>
<h3>PHP</h3>
<p>Il est bien de vérifier que la version actuelle de PHP est correctement utilisée par Apache et qu&#8217;elle correspond à ce qu&#8217;on veut. Pour cela on vérifie la version avec:</p>
<pre class="brush:powershell">php -v</pre>
<p>Ensuite on regarde les versions de PHP disponibles dans les modules d&#8217;Apache :</p>
<pre class="brush:powershell">ls -l /etc/apache2/mods-available/php*</pre>
<p>Et on regarde celle activée :</p>
<pre class="brush:powershell">ls -l /etc/apache2/mods-enabled/php*</pre>
<p>On regarde également dans le dossier des modules pour vérifier quelle version on a : </p>
<pre class="brush:powershell">ls -l /etc/apache2/modules/libphp*</pre>
<p>Si la version souhaitée est manquante dans les modules, alors on l&#8217;installe, par exemple pour la 8.4 : </p>
<pre class="brush:powershell">apt-get install php8.4 php8.4-mysql</pre>
<p>On s&#8217;assure ensuite de bien activer la bonne version, par exemple en passant de la v7.4 à v8.4 :</p>
<pre class="brush:powershel">a2dismod php7.4
a2enmod php8.4</pre>
<p>Et on redémarre Apache : </p>
<pre class="brush:powershel">systemctl restart apache2</pre>
<h3>MariaDB</h3>
<p>Cela peut être l&#8217;occasion de <a href="https://mariadb.org/download/?t=repo-config&#038;d=Debian+12+%22Bookworm%22&#038;v=11.4&#038;r_m=mva">mettre à jour MariaDB</a>. Si vous aviez un fichier <code>/etc/apt/sources.list.d/mariadb.list</code>, vous pouvez le supprimer et le remplacer par le fichier <code>mariadb.sources</code> comme indiqué dans la documentation de MariaDB.</p>
<p>Puis, comme indiqué dans la doc : </p>
<pre class="brush:powershel">apt-get update
apt-get install mariadb-server</pre>
<h3>Pour finir</h3>
<p>Une fois les erreurs corrigées, on va nettoyer tous les paquets avec :</p>
<pre class="brush:powershell">apt-get autoremove</pre>
<p>Note : pour arrêter <code>screen</code> on fait <code>CTRL + A</code> puis <code>k</code>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/05/04/mise-a-jour-dun-serveur-kimsufi-ovh-depuis-debian-11-bullseye-vers-debian-12-bookworm/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Quoi faire en Thaïlande (Bangkok, Chiang Mai et Koh Yao Yoi) ?</title>
		<link>https://blog.kodono.info/wordpress/2025/05/03/quoi-faire-en-thailande-bangkok-chiang-mai-et-koh-yao-yoi/</link>
					<comments>https://blog.kodono.info/wordpress/2025/05/03/quoi-faire-en-thailande-bangkok-chiang-mai-et-koh-yao-yoi/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sat, 03 May 2025 14:14:26 +0000</pubDate>
				<category><![CDATA[Français]]></category>
		<category><![CDATA[Voyage]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2401</guid>

					<description><![CDATA[Voici un retour d&#8217;expérience suite à notre séjour en janvier 2025. À savoir La carte bancaire est peu acceptée là bas, il faudra donc prévoir du cash. Prévoir des chaussures faciles à enlever/mettre car on doit se déchausser dans tous les temples ! Concernant les tenues dans les temples, il est en général accepté que [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Voici un retour d&#8217;expérience suite à notre séjour en janvier 2025.</p>
<p><strong>À savoir</strong></p>
<ul>
<li><strong>La carte bancaire est peu acceptée là bas, il faudra donc prévoir du cash</strong>.</li>
<li><strong>Prévoir des chaussures faciles à enlever/mettre</strong> car on doit se déchausser dans tous les temples !</li>
<li><strong>Concernant les tenues dans les temples</strong>, il est en général accepté que les hommes soient en short et éventuellement débardeur (mais t-shirt recommandé), par contre les femmes devront avoir un pantalon et couvrir leurs épaules. La seule exception qu&#8217;on ait rencontré est le <a href="https://maps.app.goo.gl/fddiMSsDcQHzsvUCA">Grand Palais de Bangkok</a> où les hommes doivent aussi porter un pantalon.</li>
<li><strong>Pour les déplacements</strong>, l&#8217;application Grab (pour <a href="https://play.google.com/store/apps/details?id=com.grabtaxi.passenger&#038;hl=fr">Android</a> ou <a href="https://apps.apple.com/fr/app/grab-taxi-ride-food-delivery/id647268330">iOS</a>) remplace Uber et vous permet de prendre facilement des VTC sans vous faire arnaquer ni devoir négocier.</li>
</ul>
<p><strong>Bangkok</strong></p>
<ul>
<li>Traversée du fleuve : des bateaux-bus assurent plusieurs arrêts le long de la rivière – vous pouvez par exemple prendre le bateau à <a href="https://maps.app.goo.gl/DmWn825xvUeHfgA29">Phra Arthit</a> et vous arrêter au <a href="https://maps.app.goo.gl/zdkAGFM8cpSeNkbs6">Sathorn Pier</a>. Vous prenez les tickets directement sur place en indiquant votre destination – <strong>les trajets sont plutôt rapides (entre 15 et 45 minutes)</strong>.</li>
<li>Le rooftop de <a href="https://maps.app.goo.gl/BMta1rEHC5dfe7ki6">la Tour MahaNakhon</a> : allez-y au moment du coucher du soleil pour profiter d&#8217;une vue incroyable sur la ville. On y retrouve un sol en verre (il faut prendre des chaussons pour y aller et les photos sont interdites sur le sol en lui-même mais on peut vous prendre en photo depuis l&#8217;extérieur de la zone délimitée). Au rez de chaussée on y trouve aussi des restaurants et des animations en réalité virtuelle – <strong>prévoir une à deux heures de visite</strong>.</li>
<li>Les temples que nous avons aimés : <a href="https://maps.app.goo.gl/fddiMSsDcQHzsvUCA">le Grand Palais</a> (mais énormément de monde donc prévoir une visite tôt le matin) et le <a href="https://maps.app.goo.gl/V4Q5R5JVNd3tyWBG6">Wat Arun</a> (bien qu&#8217;il soit le temple du lever, pas de nécessiter de s&#8217;y rendre tôt le matin – il faut bien explorer les alentours pour ne pas louper les différentes dépendances), et le <a href="https://maps.app.goo.gl/mA4BkggVt4jFsZiu5">Wat Pho</a> avec son bouddha allongé – <strong>prévoir une à deux heures par temple</strong></li>
<li>Temples que nous n&#8217;avons pas pu faire : <a href="https://maps.app.goo.gl/zPa1n9kEiLeJdKVXA">Wat Khuin Chan</a> (on l&#8217;a vu depuis la rivière et ça avait l&#8217;air joli) et le <a href="https://maps.app.goo.gl/TvtwDjU8LPYnrJZY6">Temple du Dragon</a> (vu sur Internet)</li>
<li><a href="https://maps.app.goo.gl/BEpPaqr49TdWjiJb7">Expédition à Ayutthaya</a> : l&#8217;ancienne capitale n&#8217;est plus que ruines, mais on y découvre quelques temples sympas, dont celui avec la tête de bouddha (le <a href="https://maps.app.goo.gl/r8SHu7FTLeL8eE966">Wat Mahathat</a>). Nous avons utilisé <a href="https://www.getyourguide.com/fr-fr/bangkok-l169/au-depart-de-bangkok-visite-guidee-des-temples-d-ayutthaya-avec-dejeuner-t383879/">GetYourGuide</a> pour organiser une journée là bas avec un petit groupe et repas du midi – <strong>prévoir une journée de visite depuis Bangkok</strong>.</li>
</ul>
<p><strong>Chiang Mai</strong></p>
<ul>
<li>Le <a href="https://maps.app.goo.gl/yWBPd9Hfg85NdurVA">Silver Temple</a> ne figure pas sur les guides, alors qu&#8217;il s&#8217;agit d&#8217;un des plus beaux temples ! Sûrement qu&#8217;il n&#8217;est pas mentionné car il est interdit aux femmes de pénétrer dans ce petit temple : elles pourront cependant entrer dans le sanctuaire et faire le tour du temple pour admirer les très nombreuses gravures – <strong>prévoir une trentaine de minutes sur place</strong></li>
<li><a href="https://maps.app.goo.gl/bTwVEBUBXZ7HPubP7">Mae Kha Canal</a> n&#8217;est pas non plus dans les guides : il s&#8217;agit d&#8217;une petit canal le long duquel plusieurs échoppes d&#8217;inspiration nippones se sont installées. La balade est à faire de nuit pour profiter des beaux éclairages et pour déguster quelques mets japonais – <strong>prévoir une heure, si vous y mangez</strong></li>
<li>Le temple <a href="https://maps.app.goo.gl/tBYL3CiNToaA122BA">Wat Lok Moli</a>, et le <a href="https://maps.app.goo.gl/un9dSChkD2Ngj5bKA">Wat Rajamontean</a> qui se trouve juste en face, valent le détour ! Nous les avons trouvé différents des autres temples que l&#8217;on peut voir dans la région – <strong>prévoir une grosse demi-heure</strong></li>
<li>Sur <a href="https://www.getyourguide.com/booking/CYJB9GUBM42MP7FR4TU53SG9WIAHFEW5">GetYourGuide</a> on peut trouver des expéditions de quelques heures pour visiter le temple <a href="https://maps.app.goo.gl/drTs3eZghd4wkESJ6">Wat Pha Lat</a> (très beau car plongé au milieu de la nature) ainsi que le temple <a href="https://maps.app.goo.gl/pGRhVbekhgALCTNX6">Wat Phrathat Doi Suthep</a> (lieu hautement spirituelle avec son immense escalier et sa vue panoramique sur la région) ; notre pack comprenait un arrêt à la <a href="https://maps.app.goo.gl/eGkqJyfRiRWspVTr9">cascade de Huay Keaw</a> : un lieu charmant mais pas exceptionnel – <strong>prévoir une grosse demi-journée pour l&#8217;expédition</strong></li>
<li>Si vous cherchez un lieu plus authentique que le <a href="https://maps.app.goo.gl/vSGA2aSBBP9noXLx8">Night Bazaar</a>, alors visitez le <a href="https://maps.app.goo.gl/bTTapHw4GVsPeMvL6">Warorot Market</a> en journée : des tonnes de stands de nourriture et autres bricoles pour les locaux. Possibilité de grignoter quelques plats sur place – <strong>prévoir 45 minutes si vous trainez un peu</strong></li>
<li>Pour les visites, il y a bien sûr les classiques comme <a href="https://maps.app.goo.gl/ojMB1RZiLV3JrnP3A">Wat Phra Singh</a> et <a href="https://maps.app.goo.gl/6p6CAHC1Ge5dbwy89">Wat Chedi Luang</a> qui sont à voir – <strong>prévoir 2 heures pour la visite des deux</strong></li>
<li>Si vous n&#8217;êtes pas loin du temple <a href="https://maps.app.goo.gl/tfaKohqazt1dDV7S8">Wat Chiang Man</a>, arrêtez-y vous quelques minutes : il est paisible et agréable – <strong>prévoir moins de 30 minutes</strong></li>
<li>Pour finir, vous avez quelques bars/restaurants au bord du fleuve qui sont agréables le soir, avec toutes leurs guirlandes, comme le <a href="https://maps.app.goo.gl/AR4iNuv7HNRUivEs7">River View Bar</a>.</li>
</ul>
<p><strong>Baie de Phang Nga (Province de Phuket)</strong></p>
<p>Nous avons séjourné dans le <a href="https://maps.app.goo.gl/KYs13YwkWsJgEzgD6">Santhiya Koh Yao Yai Resort</a> : un hôtel absolument magique, mais que je ne recommanderais pas car l&#8217;île de Koh Yao Yai ne propose pas grand chose à voir… Si vous partez en mode &#8220;farniente&#8221; et que votre but est de traîner au bord de la piscine/mer toute la journée, alors vous y serez sûrement tranquilles, mais si vous souhaitez visiter les nombreuses îles de la baie, ou explorer les environs, ce n&#8217;est pas l&#8217;idéal.<br />
Sans l&#8217;avoir faite, l&#8217;île jumelle (<a href="https://maps.app.goo.gl/ti1oVqoWNjcxkKkn8">Koh Yao Noi</a>) semble être plus animée.</p>
<p>L&#8217;unique journée sortie que nous avons faite est pour aller jusqu&#8217;à <a href="https://maps.app.goo.gl/46t1KwZcg4v8VwUW7">Hong Island</a> (Ko Hong) qui possède un observatoire à 360 degrés. Un endroit sublime – de là notre bateau nous a déposé dans plusieurs plages/ilots où nous avons pu admirer des singes, chauves-souris, oiseaux, et poissons en tout genre.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/05/03/quoi-faire-en-thailande-bangkok-chiang-mai-et-koh-yao-yoi/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AndroidManifest.xml from ADB</title>
		<link>https://blog.kodono.info/wordpress/2025/04/27/androidmanifest-xml-from-adb/</link>
					<comments>https://blog.kodono.info/wordpress/2025/04/27/androidmanifest-xml-from-adb/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 27 Apr 2025 14:33:12 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2406</guid>

					<description><![CDATA[It&#8217;s possible to find the AndroidManifest.xml file for an app using its APK (e.g. from APKMirror) and then with the help of a tool like jadx, but we can also connect to the Android device using adb connect, and then we use adb shell dumpsys package PACKAGE_NAME (e.g. &#8220;com.netflix.ninja&#8221; is the package name for Netflix). [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>It&#8217;s possible to find the AndroidManifest.xml file for an app using its APK (e.g. from APKMirror) and then with the help of a tool like <a href="https://github.com/skylot/jadx">jadx</a>, but we can also connect to the Android device using <code>adb connect</code>, and then we use <code>adb shell dumpsys package PACKAGE_NAME</code> (e.g. &#8220;com.netflix.ninja&#8221; is the package name for Netflix).</p>
<p>This command will return many information similar to what the AndroidManifest.xml can provide.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/04/27/androidmanifest-xml-from-adb/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Power FX: Dynamically access an object property with a variable</title>
		<link>https://blog.kodono.info/wordpress/2025/04/03/power-fx-dynamically-access-an-object-propery-with-a-variable/</link>
					<comments>https://blog.kodono.info/wordpress/2025/04/03/power-fx-dynamically-access-an-object-propery-with-a-variable/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Thu, 03 Apr 2025 16:31:58 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2390</guid>

					<description><![CDATA[I&#8217;m kind of new with Power Apps, and it&#8217;s been very difficult to find how to access an object property using a variable. In a nutshell, here are to access it: With( { tempData: ParseJSON(JSON(ThisItem, JSONFormat.IgnoreBinaryData &#038; JSONFormat.IgnoreUnsupportedTypes)) }, Concat( ForAll( ColumnArray As ColumnName, ColumnName.Value &#038; "=>" &#038; Column( tempData, ColumnName.Value ) &#038; "&#60;br>" ), [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I&#8217;m kind of new with Power Apps, and it&#8217;s been very difficult to find how to access an object property using a variable.</p>
<p>In a nutshell, here are to access it:</p>
<pre class="brush:R">
With(
  { tempData: ParseJSON(JSON(ThisItem, JSONFormat.IgnoreBinaryData &#038; JSONFormat.IgnoreUnsupportedTypes)) },
  Concat(
    ForAll(
      ColumnArray As ColumnName,
      ColumnName.Value &#038; "=>" &#038; Column( tempData, ColumnName.Value ) &#038; "&lt;br>"
    ),
    Value
  )
)
</pre>
<p>I&#8217;m transforming my record <code>ThisItem</code> to an UntypedObject to make it work with <code>Column()</code>, using <b>JSON</b> and by removing the problematic properties (thanks to <code>JSONFormat.IgnoreBinaryData &#038; JSONFormat.IgnoreUnsupportedTypes</code>).</p>
<p>Then I iterate my array (a.k.a <code>ColumnArray</code>) that contains the property I need, and I use <code>Column( tempData, ColumnName.Value )</code> to extract the column from the record!</p>
<p><strong>My use case</strong>: I defined a collection with a list of columns, then I have a Gallery in which I have an HTMLText component that must display the columns from <code>ThisItem</code> based on the collection I defined before.</p>
<p>So, in <code>App</code> screen, I defined my collection like that:</p>
<pre class="brush:R">
ClearCollect(ColumnArray,["ID", "Title", "Description", "Target_x0020_Start_x0020_Date", "AssignedTo"]);
ClearCollect(UserColumns, ["AssignedTo"]);
</pre>
<p>Then in my Gallery, I have an HTMLText object with the below content:</p>
<pre class="brush:R">
"&lt;div style='display:grid;grid-template-columns: 100px 50px 1fr 1fr 100px 150px;gap: 0px;height:65px;max-height:65px;overflo:hidden'>&lt;div style='"&#038;cssTdStyle&#038;";display:flex;justify-content:center;'>&lt;button type='button'>Edit&lt;/button>&lt;/div>" &#038;
With(
  { tempData: ParseJSON(JSON(ThisItem, JSONFormat.IgnoreBinaryData &#038; JSONFormat.IgnoreUnsupportedTypes)) },
  Concat(
    ForAll(
      ColumnArray As ColumnName,
      "&lt;div style='"&#038;cssTdStyle&#038;"'>" &#038;
  If(
    ColumnName.Value in UserColumns,
    Column( tempData, ColumnName.Value ).DisplayName,
    Column( tempData, ColumnName.Value )
  )
  &#038; "&lt;/div>"
    ),
    Value
  )
)
&#038; "&lt;/div>"
</pre>
<p>This way I can make my display dynamic, based on a list of columns (that can be populated using a Power Automate Flow). I&#8217;ll have to identify which columns are User type, or similar ones, in order to access to the next property (like <b>.DisplayName</b>).</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/04/03/power-fx-dynamically-access-an-object-propery-with-a-variable/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Quoi faire à Singapour ?</title>
		<link>https://blog.kodono.info/wordpress/2025/04/02/quoi-faire-a-singapour/</link>
					<comments>https://blog.kodono.info/wordpress/2025/04/02/quoi-faire-a-singapour/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Wed, 02 Apr 2025 16:22:08 +0000</pubDate>
				<category><![CDATA[Français]]></category>
		<category><![CDATA[Voyage]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2375</guid>

					<description><![CDATA[Voici quelques recommandations de visites à Singapour : Clarke Quay : placé au bord d&#8217;un canal, vous y trouverez plein de restaurants et bars, une sorte de food court avec de petits bâtiments colorés et qui est très animé, surtout le soir. Depuis ce port, il est possible de prendre un bateau qui vous emmènera [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Voici quelques recommandations de visites à Singapour :</p>
<ul>
<li><a href="https://maps.app.goo.gl/FqrFcmD4eZU6eYqb9">Clarke Quay</a> : placé au bord d&#8217;un canal, vous y trouverez plein de restaurants et bars, une sorte de <em>food court</em> avec de petits bâtiments colorés et qui est très animé, surtout le soir. Depuis ce port, il est possible de <a href="https://maps.app.goo.gl/s45Ga5Vvi9pAYnFP6">prendre un bateau</a> qui vous emmènera sur une petite croisière jusqu&#8217;au <a href="https://maps.app.goo.gl/77xhK6vTUWYUHM8w8">Marina Bay Sands</a> – il est recommandé de faire cette croisière en début de soirée afin de pouvoir assister <a href="https://www.marinabaysands.com/attractions/spectra.html">au spectacle des fontaines</a> (voir horaires sur le lien) de l&#8217;hôtel emblématique de Singapour. – <strong>prévoir environ 2 heures sur place (selon si vous mangez ou si vous faites la croisière)</strong></li>
<li>Symbole de la ville, le <a href="https://maps.app.goo.gl/PtswLhYbED3SwbgeA">Merlion</a> est une grande fontaine représentant une créature mi-lion mi-sirène. Par ailleurs vous aurez un beau point de vue sur l&#8217;hôtel-bâteau <a href="https://maps.app.goo.gl/77xhK6vTUWYUHM8w8">Marina Bay Sands</a>. – <strong>prévoir environ 15 minutes sur place (il peut être intéressant de marcher depuis Clarke Quay jusqu&#8217;à Merlion puis de continuer jusqu&#8217;à l&#8217;intérieur du Marina Bay Sands)</strong></li>
<li>L&#8217;emblématique <a href="https://maps.app.goo.gl/ChgP4Fj9nsHfYa1i7">Gardens by the Bay</a>, avec ses super-arbres, est un lieu à ne pas manquer. Vous pouvez vous promener gratuitement dans les grands jardins et aux pieds des super-arbres, par contre il faudra <a href="https://www.gardensbythebay.com.sg/">acheter des tickets</a> (en vente également sur place) pour visiter le <a href="https://maps.app.goo.gl/dCACUmEPH2Mp6TJr8">Flower Dome</a> (un grand dôme dans lequel on trouve beaucoup d&#8217;espèces de plantes – <em>pas très intéressant</em>) et le <a href="https://maps.app.goo.gl/A9UPsUHBPgA2k2PQ6">Cloud Forest</a> (un grand dôme avec une forêt équatoriale et des passerelles en hauteur – <em>très sympa à faire</em>), ainsi que pour monter en haut des super-arbres. – <strong>prévoir environ 3h sur place</strong></li>
<li>À ne pas louper, il y a le <a href="https://maps.app.goo.gl/2a5mXgquNrfxw1m98">Buddha Tooth Relic Temple</a> qui est un très beau temple bouddhiste, qui s&#8217;illumine la nuit. L&#8217;entrée est gratuite pour le visiter. De là vous avez le <a href="https://maps.app.goo.gl/Pdagdz59rJTBkD9B6">Maxwell Food Centre</a> avec un grand choix de nourriture locale, ainsi que le très coloré temple hindou <a href="https://maps.app.goo.gl/mP48n8KyjxJnQbmE8">Sri Mariamman Temple</a>. Bien sûr, <a href="https://maps.app.goo.gl/NowUP886knLPWjfLA">Chinatown</a> s&#8217;étend tout autour avec de quoi faire quelques emplettes ! – <strong>les temples se visitent en une quinzaine de minutes, pour le reste, tout dépend à quel point vous souhaitez flâner</strong></li>
<li>Si vous avez du temps, allez voir le peu connu <a href="https://maps.app.goo.gl/UvAPfEFasHGgRZw88">Science Center Singapore</a> qui regorge de très nombreuses expériences scientifiques et ludiques qui raviront petits et grands (palais des miroir, jeux vidéos, illusions d&#8217;optique, le <a href="https://www.science.edu.sg/whats-on/shows-demonstrations/fire-tornado-show">Fire Tornado</a>, etc.).  – <strong>prévoir au moins 3 heures, selon si vous êtes curieux, avez des enfants, et comprenez bien l&#8217;anglais</strong></li>
<li>Avant de repartir, pensez à arriver très tôt à l&#8217;aéroport afin de pouvoir profiter de l&#8217;incroyable <a href="http://Jewel Changi Airport">centre commercial Jewel Changi</a> qui jouxte les terminaux, avec son dôme, sa forêt tropical et sa chute d&#8217;eau complètement dingue ! Des boutiques par dizaines et même le dernier étage avec ses activités (payantes) pour occuper vos dernières heures. C&#8217;est clairement à voir pour vivre un moment inoubliable face à ces milliers de litres d&#8217;eau qui tombent du ciel, tandis que le monorail traverse le dôme… – <strong>prévoir au moins 1 heure pour admirer les beautés du lieu et explorer les différentes boutiques</strong></li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/04/02/quoi-faire-a-singapour/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Recevoir des alertes lors de la diffusion d&#8217;un programme</title>
		<link>https://blog.kodono.info/wordpress/2025/02/23/recevoir-des-alertes-lors-de-la-diffusion-dun-programme/</link>
					<comments>https://blog.kodono.info/wordpress/2025/02/23/recevoir-des-alertes-lors-de-la-diffusion-dun-programme/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 23 Feb 2025 08:35:11 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Divers]]></category>
		<category><![CDATA[Français]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2370</guid>

					<description><![CDATA[Trop souvent je loupe des émissions TV que j&#8217;aime bien (comme &#8220;Qui veut être mon associé ?&#8221;, &#8220;Rendez-vous en terre inconnue&#8221;, …). J&#8217;ai cherché un service sur le Net qui me permettrait de recevoir un email lorsque mon émission préférée était diffusée, mais je n&#8217;ai rien trouvé. J&#8217;ai donc créé https://alerte-tv.kodono.info/ qui permet d&#8217;être tenu [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Trop souvent je loupe des émissions TV que j&#8217;aime bien (comme &#8220;Qui veut être mon associé ?&#8221;, &#8220;Rendez-vous en terre inconnue&#8221;, …). J&#8217;ai cherché un service sur le Net qui me permettrait de recevoir un email lorsque mon émission préférée était diffusée, mais je n&#8217;ai rien trouvé.</p>
<p>J&#8217;ai donc créé <a href="https://alerte-tv.kodono.info/">https://alerte-tv.kodono.info/</a> qui permet d&#8217;être tenu au courant lorsqu&#8217;un programme est diffusé à la télévision française.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/02/23/recevoir-des-alertes-lors-de-la-diffusion-dun-programme/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Utiliser un VPN sur l&#8217;iPad via un serveur Debian</title>
		<link>https://blog.kodono.info/wordpress/2025/02/08/utiliser-un-vpn-sur-lipad-via-un-serveur-debian/</link>
					<comments>https://blog.kodono.info/wordpress/2025/02/08/utiliser-un-vpn-sur-lipad-via-un-serveur-debian/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sat, 08 Feb 2025 17:42:21 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Français]]></category>
		<category><![CDATA[Linux]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2352</guid>

					<description><![CDATA[Je vais expliquer ici comment installer un serveur VPN WireGuard sur Debian et l&#8217;utiliser depuis un iPad. 1. Serveur Debian Sur le serveur, on installe WireGuard avec apt install wireguard -y. 2. Générer la clé privée et publique du serveur Pour cela, on va faire : sudo wg genkey &#124; sudo tee /etc/wireguard/server_private.key sudo cat [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Je vais expliquer ici comment installer un serveur VPN WireGuard sur Debian et l&#8217;utiliser depuis un iPad.</p>
<p><strong>1. Serveur Debian</strong></p>
<p>Sur le serveur, on installe WireGuard avec <code>apt install wireguard -y</code>.</p>
<p><strong>2. Générer la clé privée et publique du serveur</strong></p>
<p>Pour cela, on va faire : </p>
<pre class="brush:bash">
sudo wg genkey | sudo tee /etc/wireguard/server_private.key
sudo cat /etc/wireguard/server_private.key | sudo wg pubkey | sudo tee /etc/wireguard/server_public.key
</pre>
<p>On retrouvera nos clés dans les fichiers <code>/etc/wireguard/server_private.key</code> et <code>/etc/wireguard/server_public.key</code></p>
<p><strong>3. Générer la clé privée et publique du client</strong></p>
<p>Sur le serveur Debian, on va générer des clés pour le client :</p>
<pre class="brush:bash">
wg genkey | tee client_private.key
cat client_private.key | wg pubkey | tee client_public.key
</pre>
<p>On retrouvera nos clés dans les fichiers <code>./client_private.key</code> et <code>./client_public.key</code></p>
<p><strong>4. Configuration du serveur</strong></p>
<p>On va entrer la configuration suivante dans le fichier <code>/etc/wireguard/wg0.conf</code> :</p>
<pre>
[Interface]
Address = 10.0.0.1/24
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
ListenPort = 51820
PrivateKey = PRIVATE_KEY_DU_SERVEUR

[Peer]
PublicKey = PUBLIC_KEY_DU_CLIENT
AllowedIPs = 10.0.0.2/32
PersistentKeepalive = 25
</pre>
<p><strong>5. Activer le transfert IP sur le serveur</strong></p>
<p>Éditer le fichier <code>/etc/sysctl.conf</code> afin d&#8217;avoir :</p>
<pre>
net.ipv4.ip_forward=1
net.ipv6.conf.all.forwarding=1
</pre>
<p>Et on applique les changements avec la commande <code>sudo sysctl -p</code></p>
<p><strong>6. Démarrage de WireGuard</strong></p>
<p>Pour démarrer le serveur VPN on tape <code>sudo systemctl start wg-quick@wg0</code></p>
<p>On peut voir le statut avec la commande <code>sudo systemctl status wg-quick@wg0</code></p>
<p><strong>7. Configuration du client</strong></p>
<p>Sur notre client (ici un iPad), on installe l&#8217;application WireGuard depuis le App Store.</p>
<p>Ensuite, sur notre serveur, on va créer le fichier de configuration <code>client.conf</code> qui sera utilisé par le client, avec le contenu suivant :</p>
<pre>
[Interface]
PrivateKey = PRIVATE_KEY_DU_CLIENT
Address = 10.0.0.2/32
DNS = 8.8.8.8, 8.8.4.4 # on utilise les DNS de Google

[Peer]
PublicKey = PUBLIC_KEY_DU_SERVEUR
Endpoint = mon_serveur.debian.home:51820 # on indique l'IP/hostname de notre serveur VPN
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
</pre>
<p>Afin de transmettre cette configuration à l&#8217;iPad, on peut générer un QR code. Pour cela on installe ce qu&#8217;il faut : <code>sudo apt install qrencode</code>, puis on génère avec <code>qrencode -t ansiutf8 < client.conf</code></p>
<p>Sur l'iPad, on ouvre l'application WireGuard puis on ajoute un client en utilisant le QR Code généré.</p>
<p><strong>8. Vérification</strong></p>
<p>En activant le VPN sur l'iPad, on peut tester si tout fonctionne comme prévu en vérifiant l'adresse IP de l'iPad.<br />
Sur le serveur, on peut utiliser la commande <code>wg show</code> pour voir un peu ce qu'il se passe.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2025/02/08/utiliser-un-vpn-sur-lipad-via-un-serveur-debian/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Animated favicon in Chrome/Edge</title>
		<link>https://blog.kodono.info/wordpress/2024/12/30/animated-favicon-in-chrome-edge/</link>
					<comments>https://blog.kodono.info/wordpress/2024/12/30/animated-favicon-in-chrome-edge/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Mon, 30 Dec 2024 18:39:49 +0000</pubDate>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Navigateur]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2348</guid>

					<description><![CDATA[To animate a favicon with Chrome / Edge, we can create an animated canvas and a web worker. See the Github repository I created about it.]]></description>
										<content:encoded><![CDATA[<p>To animate a favicon with Chrome / Edge, we can create an animated canvas and a web worker.</p>
<p>See the <a href="https://github.com/Aymkdn/animated-favicon">Github repository</a> I created about it.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/12/30/animated-favicon-in-chrome-edge/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Remove lazy loading for a custom SPFx webpart</title>
		<link>https://blog.kodono.info/wordpress/2024/12/15/remove-lazy-loading-for-a-custom-spfx-webpart/</link>
					<comments>https://blog.kodono.info/wordpress/2024/12/15/remove-lazy-loading-for-a-custom-spfx-webpart/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 15 Dec 2024 22:09:45 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2344</guid>

					<description><![CDATA[SharePoint Online is doing lazy loading when we hit a page with webparts. If you created a SPFx webpart, and if it&#8217;s not visible right after the page load, then it will only be loaded once the user scrolls to it&#8230; To remove this behavior, and load the webpart as soon as possible, you must [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>SharePoint Online is doing lazy loading when we hit a page with webparts. If you created a SPFx webpart, and if it&#8217;s not visible right after the page load, then it will only be loaded once the user scrolls to it&#8230;</p>
<p>To remove this behavior, and load the webpart as soon as possible, <a href="https://github.com/SharePoint/sp-dev-docs/discussions/7918">you must declare your webpart as a dynamic data source</a>.</p>
<p>Here is the minimal code example (in pure JS, not in TS):</p>
<pre class="brush:javascript">
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import { DisplayMode } from '@microsoft/sp-core-library';

export default class HtmlViewerWebPart extends BaseClientSideWebPart {
  constructor() {
    super();
    // the below is used by the dynamic data source to avoid lazy loading
    this._sourceId = 'my_webpart';
  }

  onInit() {
    // to avoid lazy loading of the webpart, we register it as a dynamic data source
    // we only need to do it during the Edit of the page
    if (this.isPageInEditMode() &#038;& this.context.dynamicDataSourceManager) {
      this.context.dynamicDataSourceManager.initializeSource(this);
      console.log('Dynamic Data Source initialized to avoid lazy loading.');
    }
    
    return Promise.resolve();
  }

  isPageInEditMode() {
    return this.displayMode === DisplayMode.Edit;
  }

  // we need to declare this method because it's used by the data source to avoid lazy loading
  getPropertyDefinitions() {
    return []
  }

  // we need to declare this method because it's used by the data source to avoid lazy loading
  getPropertyValue(propertyId) {
    return propertyId;
  }
}
</pre>
<p>Then, edit the page, which will trigger the dynamic data source registration, and publish it.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/12/15/remove-lazy-loading-for-a-custom-spfx-webpart/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Determine an element `height` and `width` in CSS only and reuse it within another CSS rule</title>
		<link>https://blog.kodono.info/wordpress/2024/12/13/determine-an-element-height-and-width-in-css-only-and-reuse-it-within-another-css-rule/</link>
					<comments>https://blog.kodono.info/wordpress/2024/12/13/determine-an-element-height-and-width-in-css-only-and-reuse-it-within-another-css-rule/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Fri, 13 Dec 2024 15:44:36 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau intermédiaire]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2333</guid>

					<description><![CDATA[I found this technic on https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/ – it only works on modern browsers (mainly Chrome and Edge) Let&#8217;s define the properties: @property --_x { syntax: "&#60;number>"; inherits: true; initial-value: 0; } @property --_y { syntax: "&#60;number>"; inherits: true; initial-value: 0; } @property --w { syntax: "&#60;integer>"; inherits: true; initial-value: 0; } @property --h { syntax: [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I found this technic on <a href="https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/">https://frontendmasters.com/blog/how-to-get-the-width-height-of-any-element-in-only-css/</a> – <strong>it only works on modern browsers (mainly Chrome and Edge)</strong></p>
<p>Let&#8217;s define the properties:</p>
<pre class="brush:css">
@property --_x {
  syntax: "&lt;number>";
  inherits: true;
  initial-value: 0; 
}
@property --_y {
  syntax: "&lt;number>";
  inherits: true;
  initial-value: 0; 
}
@property --w {
  syntax: "&lt;integer>";
  inherits: true;
  initial-value: 0; 
}
@property --h {
  syntax: "&lt;integer>";
  inherits: true;
  initial-value: 0; 
}
</pre>
<p>Then, let&#8217;s find an element that is a common parent for both the element where we&#8217;ll calculate the height/width, and the element that will use this height/width, and we apply some CSS:</p>
<pre class="brush:css">
.parent {
  timeline-scope: --cx,--cy;
  --w:calc(1/(1 - var(--_x)));
  --h:calc(1/(1 - var(--_y)));
  animation: x linear,y linear;
  animation-timeline: --cx,--cy !important;
  animation-range: entry 100% exit 100%;
}
</pre>
<p>We finish up with some additional CSS:</p>
<pre class="brush:css">
.get-dimension {
  overflow: hidden;
  position: relative;
}
.get-dimension:before {
  content:"";
  position: absolute;
  left: 0;
  top: 0;
  width: 1px;
  aspect-ratio: 1;
  view-timeline: --cx inline,--cy block;
}
@keyframes x {to{--_x:1}}
@keyframes y {to{--_y:1}}
</pre>
<p>And finally, we can use <code>--w</code> and <code>--h</code> in the other element:</p>
<pre class="brush:css">
.apply-dimension {
  height: calc(var(--h)*1px);
  width: calc(var(--w)*1px);
}
</pre>
<p>Here is a demo:<br />
<p class='codepen'  data-height='500' data-theme-id='1' data-slug-hash='vEByKod' data-default-tab='result' data-animations='run' data-editable='' data-embed-version='2'>

</p>
</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/12/13/determine-an-element-height-and-width-in-css-only-and-reuse-it-within-another-css-rule/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to remove an account / leave an organization in MS Teams?</title>
		<link>https://blog.kodono.info/wordpress/2024/10/11/how-to-remove-an-account-leave-an-organization-in-ms-teams/</link>
					<comments>https://blog.kodono.info/wordpress/2024/10/11/how-to-remove-an-account-leave-an-organization-in-ms-teams/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Fri, 11 Oct 2024 15:44:15 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2330</guid>

					<description><![CDATA[If in the top right corner of MS Teams, when you click on your profile picture, you see other organizations/accounts and you want to delete them: Go to the MS Teams Settings by clicking on the three dots next to your profile picture Go to Accounts and orgs Turn off or leave the org/account that [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>If in the top right corner of MS Teams, when you click on your profile picture, you see other organizations/accounts and you want to delete them:</p>
<ol>
<li>Go to the MS Teams Settings by clicking on the three dots next to your profile picture</li>
<li>Go to <strong>Accounts and orgs</strong></li>
<li>Turn off or leave the org/account that is problematic</li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/10/11/how-to-remove-an-account-leave-an-organization-in-ms-teams/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Investir dans des actions en bourse</title>
		<link>https://blog.kodono.info/wordpress/2024/10/05/investir-dans-des-actions-en-bourse/</link>
					<comments>https://blog.kodono.info/wordpress/2024/10/05/investir-dans-des-actions-en-bourse/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sat, 05 Oct 2024 09:56:11 +0000</pubDate>
				<category><![CDATA[Divers]]></category>
		<category><![CDATA[Français]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2328</guid>

					<description><![CDATA[J&#8217;ai regardé la vidéo de Nas Daily : https://www.instagram.com/reel/DAlPq4IO_Jh/ Et voici ce que je retiens : Préférer les entreprises qui sont là depuis longtemps, ou qu&#8217;on sait qu&#8217;elles vont être là pour encore 10 ans Préférer les entreprises dont le dirigeant est aussi le fondateur, et que vous appréciez (des services que vous utilisez, des [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>J&#8217;ai regardé la vidéo de Nas Daily : <a href="https://www.instagram.com/reel/DAlPq4IO_Jh/">https://www.instagram.com/reel/DAlPq4IO_Jh/</a></p>
<p>Et voici ce que je retiens :</p>
<ul>
<li>Préférer les entreprises qui sont là depuis longtemps, ou qu&#8217;on sait qu&#8217;elles vont être là pour encore 10 ans</li>
<li>Préférer les entreprises dont le dirigeant est aussi le fondateur, et que vous appréciez (des services que vous utilisez, des restaurants où vous allez, etc)</li>
<li>On peut regarder les entreprises du S&#038;P 500 ou CAC 40 qui sont une bonne référence</li>
<li>Le &#8220;nombre magique&#8221; pour le PE Raio (Price/Earning Ratio) doit être entre 20 et 30</li>
<li>Les dividendes que reversent l&#8217;entreprise aux actionnaires</li>
<li>Il ne faut pas faire beaucoup de transactions, mais plutôt investir sur la longueur (plusieurs années)</li>
<li>Pour plus de simplicités, on peut aussi acheter &#8220;un groupe d&#8217;actions&#8221; dit <a href="https://www.amf-france.org/fr/espace-epargnants/comprendre-les-produits-financiers/placements-collectifs/trackers-etf">&#8220;ETF&#8221;</a> (sur l&#8217;application Sumeria on les trouve sous le nom de &#8220;Top 40 French Stocks&#8221; pour le CAC 40 et &#8220;Top 500 US Stocks&#8221; pour le S&#038;P 500)</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/10/05/investir-dans-des-actions-en-bourse/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Faire des enregistrements récurrents avec OQEE/FreeTV</title>
		<link>https://blog.kodono.info/wordpress/2024/09/18/faire-des-enregistrements-recurrents-avec-oqee/</link>
					<comments>https://blog.kodono.info/wordpress/2024/09/18/faire-des-enregistrements-recurrents-avec-oqee/#comments</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Wed, 18 Sep 2024 07:36:37 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Français]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2322</guid>

					<description><![CDATA[J&#8217;ai créé un service en ligne qui permet de simplifier les enregistrements avec OQEE/FreeTV : https://eeqo.kodono.info Il vous faudra être connecté au réseau de votre Freebox afin d&#8217;être reconnu. Ensuite, l&#8217;interface est simple à utiliser et vous permet d&#8217;enregistrement plusieurs programmes à la suite d&#8217;un coup, et créer un enregistrement récurrent sur au moins les [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2024/09/logo.png" alt="" width="449" height="199" class="aligncenter size-full wp-image-2325" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2024/09/logo.png 449w, https://blog.kodono.info/wordpress/wp-content/uploads/2024/09/logo-300x133.png 300w" sizes="auto, (max-width: 449px) 100vw, 449px" /></p>
<p>J&#8217;ai créé un service en ligne qui permet de simplifier les enregistrements avec OQEE/FreeTV : <a href="https://eeqo.kodono.info">https://eeqo.kodono.info</a></p>
<p>Il vous faudra être connecté au réseau de votre Freebox afin d&#8217;être reconnu. Ensuite, l&#8217;interface est simple à utiliser et vous permet d&#8217;enregistrement plusieurs programmes à la suite d&#8217;un coup, et créer un enregistrement récurrent sur au moins les prochains 10 jours. Vous pouvez recevoir un email à la fin de la période pour réactiver l&#8217;enregistrement.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/09/18/faire-des-enregistrements-recurrents-avec-oqee/feed/</wfw:commentRss>
			<slash:comments>5</slash:comments>
		
		
			</item>
		<item>
		<title>How to run Android TV 9 on Windows</title>
		<link>https://blog.kodono.info/wordpress/2024/09/02/how-to-run-android-tv-9-on-windows/</link>
					<comments>https://blog.kodono.info/wordpress/2024/09/02/how-to-run-android-tv-9-on-windows/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Mon, 02 Sep 2024 09:34:00 +0000</pubDate>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[android]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2311</guid>

					<description><![CDATA[I used to use Android Studio with the various emulators to test my Android apps, however for Android TV 9 it doesn&#8217;t work properly because it doesn&#8217;t have Google Play Store on it. To have a good version of Android TV 9: Downlad a VirtualBox image of it – I used the one shared in [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>I used to use Android Studio with the various emulators to test my Android apps, however for Android TV 9 it doesn&#8217;t work properly because it doesn&#8217;t have Google Play Store on it.</p>
<p>To have a good version of Android TV 9:</p>
<ol>
<li><a href="https://mega.nz/file/XQ9zWaoC#PajlBLEU-43g239wAB0vpMsnpYpEU6seSqz36lXv5go">Downlad a VirtualBox image of it</a> – I used the one shared <a href="https://www.youtube.com/watch?v=_dfOESBBTHM">in this video</a>
<li>Install <a href="https://www.virtualbox.org/">VirtualBox</a> if you don&#8217;t have it yet</li>
<li>Load the downloaded image with VirtualBox</li>
<li>Configure it by going to the network section and select &#8220;Bridged Adapter&#8221; instead of &#8220;NAT&#8221;</li>
<li>Once Android TV is started, you&#8217;ll have to connect to your Google account, but using the keyboard might be challenging – you can right click on the USB cable icon that appears at the bottom right of the VirtualBox window, and select your keyboard (be aware it&#8217;s a QWERTY map that is used)</li>
</ol>
<p>After that, go to the Android TV settings, in the &#8220;About&#8221; section, and click several times on the Build Version to enable the Developer Mode. From the developer menu, you can enable the &#8220;Debug USB&#8221;.</p>
<p>Once everything is correctly configured, the Android TV should be visible on your local network, meaning you can use the ADB command to test your app.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/09/02/how-to-run-android-tv-9-on-windows/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Connect to SharePoint Online from NodeJS using an impersonate (application) Bearer Token</title>
		<link>https://blog.kodono.info/wordpress/2024/04/29/connect-to-sharepoint-online-from-nodejs-using-an-impersonate-application-bearer-token/</link>
					<comments>https://blog.kodono.info/wordpress/2024/04/29/connect-to-sharepoint-online-from-nodejs-using-an-impersonate-application-bearer-token/#comments</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Mon, 29 Apr 2024 13:07:56 +0000</pubDate>
				<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2291</guid>

					<description><![CDATA[(duplicate of this StackOverflow answer) Start a new project In a new folder, type npm init to start a new project. Make sure to use a Node &#62;= v14 (I use Node v18 – and Volta can be useful to manage several versions of Node for Windows) Install some dependencies: npm install axios @azure/msal-node uuid [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>(duplicate of this <a href="https://stackoverflow.com/questions/78402212/connect-to-sharepoint-online-from-nodejs-using-an-impersonate-application-bear/78402213#78402213">StackOverflow answer</a>)</p>
<h1 id="start-a-new-project">Start a new project</h1>
<ol>
<li>In a new folder, type <code>npm init</code> to start a new project.</li>
<li>Make sure to use a Node &gt;= v14 (I use Node v18 – and <a href="https://volta.sh/">Volta</a> can be useful to manage several versions of Node for Windows) </li>
<li>Install some dependencies: <code>npm install axios @azure/msal-node uuid</code>  </li>
</ol>
<h1 id="certificate">Certificate</h1>
<p>In the past, we could use the add-in application feature to get a token, but Microsoft <a href="https://learn.microsoft.com/fr-fr/sharepoint/dev/sp-add-ins/retirement-announcement-for-add-ins">announced</a> it will be retired.</p>
<p>We now need to pass through an Azure application to get it. But before creating the Azure app, we need to create a Private Certificate key.</p>
<ol>
<li>Create the file <code>Create-SelfSignedCertificate.ps1</code> using the below code (<a href="https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread#setting-up-an-azure-ad-app-for-app-only-access">source</a>):   </li>
</ol>
<pre class="brush:powershell">#Requires -RunAsAdministrator
<#
.SYNOPSIS
Creates a Self Signed Certificate for use in server to server authentication
.DESCRIPTION
Source: https://learn.microsoft.com/en-us/sharepoint/dev/solution-guidance/security-apponly-azuread#setting-up-an-azure-ad-app-for-app-only-access
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key.
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Password (ConvertTo-SecureString -String "MyPassword" -AsPlainText -Force)
This will create a new self signed certificate with the common name "CN=MyCert". The password as specified in the Password parameter will be used to protect the private key
.EXAMPLE
.\Create-SelfSignedCertificate.ps1 -CommonName "MyCert" -StartDate 2015-11-21 -EndDate 2017-11-21 -Force
This will create a new self signed certificate with the common name "CN=MyCert". During creation you will be asked to provide a password to protect the private key. If there is already a certificate with the common name you specified, it will be removed first.
#>
Param(

[Parameter(Mandatory=$true)]
   [string]$CommonName,

[Parameter(Mandatory=$true)]
   [DateTime]$StartDate,

[Parameter(Mandatory=$true)]
   [DateTime]$EndDate,

[Parameter(Mandatory=$false, HelpMessage="Will overwrite existing certificates")]
   [Switch]$Force,

[Parameter(Mandatory=$false)]
   [SecureString]$Password
)

# DO NOT MODIFY BELOW
function CreateSelfSignedCertificate(){
  #Remove and existing certificates with the same common name from personal and root stores
    #Need to be very wary of this as could break something
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    if($certs -ne $null -and $certs.Length -gt 0)
    {
        if($Force)
        {

foreach($c in $certs)
            {
                remove-item $c.PSPath
            }
        } else {
            Write-Host -ForegroundColor Red "One or more certificates with the same common name (CN=$CommonName) are already located in the local certificate store. Use -Force to remove them";
            return $false
        }
    }

$name = new-object -com "X509Enrollment.CX500DistinguishedName.1"
    $name.Encode("CN=$CommonName", 0)

$key = new-object -com "X509Enrollment.CX509PrivateKey.1"
    $key.ProviderName = "Microsoft RSA SChannel Cryptographic Provider"
    $key.KeySpec = 1
    $key.Length = 2048
    $key.SecurityDescriptor = "D:PAI(A;;0xd01f01ff;;;SY)(A;;0xd01f01ff;;;BA)(A;;0x80120089;;;NS)"
    $key.MachineContext = 1
    $key.ExportPolicy = 1 # This is required to allow the private key to be exported
    $key.Create()

$serverauthoid = new-object -com "X509Enrollment.CObjectId.1"
    $serverauthoid.InitializeFromValue("1.3.6.1.5.5.7.3.1") # Server Authentication
    $ekuoids = new-object -com "X509Enrollment.CObjectIds.1"
    $ekuoids.add($serverauthoid)
    $ekuext = new-object -com "X509Enrollment.CX509ExtensionEnhancedKeyUsage.1"
    $ekuext.InitializeEncode($ekuoids)

$cert = new-object -com "X509Enrollment.CX509CertificateRequestCertificate.1"
    $cert.InitializeFromPrivateKey(2, $key, "")
    $cert.Subject = $name
    $cert.Issuer = $cert.Subject
    $cert.NotBefore = $StartDate
    $cert.NotAfter = $EndDate
    $cert.X509Extensions.Add($ekuext)
    $cert.Encode()

$enrollment = new-object -com "X509Enrollment.CX509Enrollment.1"
    $enrollment.InitializeFromRequest($cert)
    $certdata = $enrollment.CreateRequest(0)
    $enrollment.InstallResponse(2, $certdata, 0, "")
    return $true
}

function ExportPFXFile()
{
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    if($Password -eq $null)
    {
        $Password = Read-Host -Prompt "Enter Password to protect private key" -AsSecureString
    }
    $cert = Get-ChildItem -Path Cert:\LocalMachine\my | where-object{$_.Subject -eq "CN=$CommonName"}

    Export-PfxCertificate -Cert $cert -Password $Password -FilePath "$($CommonName).pfx"
    Export-Certificate -Cert $cert -Type CERT -FilePath "$CommonName.cer"
}

function RemoveCertsFromStore()
{
    # Once the certificates have been been exported we can safely remove them from the store
    if($CommonName.ToLower().StartsWith("cn="))
    {
        # Remove CN from common name
        $CommonName = $CommonName.Substring(3)
    }
    $certs = Get-ChildItem -Path Cert:\LocalMachine\my | Where-Object{$_.Subject -eq "CN=$CommonName"}
    foreach($c in $certs)
    {
        remove-item $c.PSPath
    }
}

if(CreateSelfSignedCertificate)
{
    ExportPFXFile
    RemoveCertsFromStore
}</pre>
<ol start="2">
<li>Open a PowerShell console as an <em>administrator</em></li>
<li>Create the certificate with a command like <code>.\Create-SelfSignedCertificate.ps1 -CommonName &quot;SharePointOnlinePrivateKey&quot; -StartDate 2024-04-01 -EndDate 2035-01-01</code> (if you receive an error, make sure to allow running this kind of script with the command <code>Set-ExecutionPolicy RemoteSigned</code>)</li>
<li>A password is required (e.g. &quot;HereIsMyPass1223&quot;)</li>
<li>Two files are created: <code>SharePointOnlinePrivateKey.pfx</code> and <code>SharePointOnlinePrivateKey.cer</code></li>
</ol>
<p>We&#39;re going to install OpenSSL to convert the <code>pfx</code> file to a <code>pem</code>:</p>
<ol>
<li>Install <a href="https://www.openssl.org/">OpenSSL</a> (e.g. the light version for Win64) as an <em>administrator</em></li>
<li>Find the OpenSSL installation directory (e.g. <code>C:\Program Files\OpenSSL-Win64\</code>)</li>
<li>Open the <code>start.bat</code> file from this OpenSSL directory</li>
<li>A command window opens – go to the directory where the <code>SharePointOnlinePrivateKey.pfx</code> file is (e.g. <code>cd C:\Users\Aymeric\Documents\nodejs\spo-experiments</code>)</li>
<li>In the OpenSSL command window, type <code>openssl pkcs12 -in SharePointOnlinePrivateKey.pfx -out SharePointOnlinePrivateKey.pem</code> (the password entered in the previous steps will be asked three times)</li>
</ol>
<p>We should now have a file called <code>SharePointOnlinePrivateKey.pem</code></p>
<h1 id="azure-application">Azure Application</h1>
<p>It&#39;s time to create the related Azure application:</p>
<ol>
<li>Go to <a href="https://portal.azure.com">https://portal.azure.com</a></li>
<li>Go to the Microsoft Entra ID section</li>
<li>Go to the &quot;App Registrations&quot;, and click on &quot;New registration&quot;</li>
<li>I won&#39;t detail all the steps to create the app</li>
<li>Give a name to the app (e.g. &quot;<strong>SharePoint Online Remote Access</strong>&quot;)</li>
</ol>
<h2 id="get-thumbprint">Get Thumbprint</h2>
<p>We need the thumbprint:</p>
<ol>
<li>Go to &quot;<strong>Certificates &amp; secrets</strong>&quot; section</li>
<li>Go to &quot;<strong>Certificates</strong>&quot; tab (<a href="https://github.com/SharePoint/sp-dev-docs/issues/5889#issuecomment-645225813">see how</a>) and upload the <code>SharePointOnlinePrivateKey.cer</code> file you created before</li>
<li>Once uploaded, it will provide &quot;<strong>Thumbprint</strong>&quot; (e.g. &quot;F7D8D4F2F140E79B215899BD93A14D0790947789&quot;) – copy this value for a later use.</li>
</ol>
<h2 id="get-client-id-and-tenant-id">Get Client Id and Tenant Id</h2>
<p>We need the <code>clientId</code> and the <code>tenantId</code>:</p>
<ol>
<li>From the overview page of your app, copy the &quot;<strong>Application (client) Id</strong>&quot; (e.g. &quot;75284292-7515-4e2f-aae9-d1777527dd7b&quot;) and the &quot;<strong>Directory (tenant) ID</strong>&quot; (e.g. &quot;945c177a-83a2-4e80-9f8c-5a91be5752dd&quot;)</li>
</ol>
<h2 id="platform-configuration">Platform configuration</h2>
<p>Additional configuration required for the application:</p>
<ol>
<li>Go to &quot;<strong>Authentication</strong>&quot; menu, and under &quot;<strong>Platform configurations</strong>&quot;, click on &quot;<strong>Add a Platform</strong>&quot;</li>
<li>Choose &quot;<strong>Web</strong>&quot; and enter <code>https://localhost</code> for &quot;<strong>Redirect URLs</strong>&quot;</li>
<li>Choose &quot;<strong>Access Token</strong>&quot; and &quot;<strong>ID Token</strong>&quot; in the &quot;<strong>Implicit grant and hybrid flows</strong>&quot; section</li>
</ol>
<h2 id="api-permissions">API Permissions</h2>
<p>We want to give the permissions to the Azure app to get access to only one (or more) specific Sites Collection, but not all of the tenant site collections. To do so, we only need the <code>Sites.Selected</code> permissions (<a href="https://devblogs.microsoft.com/microsoft365dev/controlling-app-access-on-specific-sharepoint-site-collections/">ref1</a> and <a href="https://techcommunity.microsoft.com/t5/microsoft-sharepoint-blog/develop-applications-that-use-sites-selected-permissions-for-spo/ba-p/3790476">ref2</a>).</p>
<p>First, let&#39;s select it from a Microsoft Graph perspective:</p>
<ol>
<li>Go to the &quot;<strong>API Permissions</strong>&quot; section from the left navigation</li>
<li>Click on &quot;<strong>Add a permission</strong>&quot;</li>
<li>Select &quot;<strong>Microsoft Graph</strong>&quot;</li>
<li>Then &quot;<strong>Application Permissions</strong>&quot;</li>
<li>Select &quot;<strong>Sites.Selected</strong>&quot;</li>
</ol>
<p>Then, let&#39;s select it for SharePoint Online REST perspective:</p>
<ol>
<li>From the same section, select &quot;<strong>SharePoint</strong>&quot;</li>
<li>Then &quot;<strong>Application Permissions</strong>&quot;</li>
<li>Select &quot;<strong>Sites.Selected</strong>&quot;</li>
</ol>
<p><a href="https://i.sstatic.net/19SV6jA3.png"><img decoding="async" src="https://i.sstatic.net/19SV6jA3.png" alt="API Permissions screen"></a><br />
Remark: you might need to contact the IT team to get your API Permissions approved/granted.</p>
<h2 id="get-client-secret">Get Client Secret</h2>
<p>We need the <code>clientSecret</code>:</p>
<ol>
<li>Go to the &quot;<strong>Certificates and Secrets</strong>&quot; section from the left navigation</li>
<li>Go to &quot;<strong>Client Secret</strong>&quot; and click on &quot;<strong>New client secret</strong>&quot;</li>
<li>In &quot;<strong>Description</strong>&quot; you can put something like &quot;SharePoint Online Remote Access&quot;, and choose 365 days for the expiration</li>
<li>Once done, make sure to copy <code>Value</code> (e.g. &quot;rVE7Q~Z1BhRXaljbj7SPg~U2HYJRR-feckrxKbCt&quot;) that is our <code>clientSecret</code></li>
</ol>
<h2 id="app-permissions-on-site-collection">App permissions on Site Collection</h2>
<p>It&#39;s time to indicate that the Azure application can have access to a specific Site Collection (e.g. <a href="https://mycompany.sharepoint.com/sites/contoso">https://mycompany.sharepoint.com/sites/contoso</a>).</p>
<p>To proceed, we need the <code>siteId</code>:</p>
<ol>
<li>Go to the SharePoint website (e.g. <a href="https://mycompany.sharepoint.com/sites/contoso">https://mycompany.sharepoint.com/sites/contoso</a>)</li>
<li>Click right in the page to see the source (or <code>CTRL U</code>)</li>
<li>Search for the variable <code>siteId</code> (e.g. &quot;7d6d9e18-e8de-4b7c-9582-3f978726f356&quot;)</li>
</ol>
<p>To give the permissions to the app, a special command must be entered (see <a href="https://www.youtube.com/watch?v=SNIF3zCYNUk">this video</a> for more information). In theory, the site collection admin can do it, but it might be restricted in your organization, and you&#39;ll need the assistance of a tenant admin.</p>
<p>We need the <code>AppId</code> (as known as <code>clientId</code>) and <code>DisplayName</code> (as known as the Azure app name):</p>
<pre class="brush:powershell">
$targetSiteUri = 'https://mycompany.sharepoint.com/sites/contoso'
Connect-PnpOnline $targetSiteUri
Grant-PnPAzureADAppSitePermission -AppId '75284292-7515-4e2f-aae9-d1777527dd7b' -DisplayName 'SharePoint Online Remote Access' -Site $targetSiteUri -Permissions Write
</pre>
<h1 id="interact-with-sharepoint">Interact with SharePoint</h1>
<p>Microsoft Graph only needs a <code>client_secret</code>, while SharePoint REST API needs a <code>client_assertion</code> from the Certificate Private key.</p>
<p>So, let&#39;s start with Microsoft Graph to verify our Azure app has the correct access permissions.</p>
<h2 id="microsoft-graph">Microsoft Graph</h2>
<p>Below is the script we can use:</p>
<pre class="brush:javascript">
const axios = require('axios');

// our constants that we found previously
// they should not be hard-coded in your script, but store in a safe place
const tenantId = '945c177a-83a2-4e80-9f8c-5a91be5752dd';
const clientId = '75284292-7515-4e2f-aae9-d1777527dd7b';
const clientSecret = 'rVE7Q~Z1BhRXaljbj7SPg~U2HYJRR-feckrxKbCt';
const siteId = '7d6d9e18-e8de-4b7c-9582-3f978726f356';

async function getAccessToken() {
  const resource = 'https://graph.microsoft.com';

  try {
    const response = await axios.post(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, new URLSearchParams({
      grant_type: 'client_credentials',
      client_id: clientId,
      client_secret: clientSecret,
      scope: `${resource}/.default`
    }), {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error getting access token:', error);
    throw error;
  }
}

// get a SharePoint item using Graph
getAccessToken()
.then(token => {
  // we need to find the siteId for each level
  // we could use `https://graph.microsoft.com/v1.0/sites/${siteId}/sites` to find the children sites Id
  // mycompany.sharepoint.com/sites/contoso/Toolbox -> 919f3ff8-2cfd-469d-ac2c-cf58475ee72a
  // mycompany.sharepoint.com/sites/contoso/Toolbox/Demo -> 37af7205-ebd1-49e5-a770-cdb182d2ae81
  return axios.get(`https://graph.microsoft.com/v1.0/sites/${siteId}/sites/919f3ff8-2cfd-469d-ac2c-cf58475ee72a/sites/37af7205-ebd1-49e5-a770-cdb182d2ae81/lists/Assets/items/123?expand=fields(select=Title)`, {
    headers:{
      'Authorization':'Bearer '+token,
      'Accept': 'application/json'
    }
  })
})
.then(res => {
  console.log(res.data);
})
.catch(err => {
  console.log("err => ", err.response.data)
})
</pre>
<h2 id="sharepoint-online-rest-api">SharePoint Online REST API</h2>
<p>If it worked with the Graph version, it means we can now test with the SharePoint REST API:</p>
<pre class="brush:javascript">
const axios = require('axios');
const fs = require("fs");
const crypto = require("crypto");
const msal = require("@azure/msal-node");
const { v4: uuid } = require('uuid');

// our constants that we found previously
// they should not be hard-coded in your script, but store in a safe place
const tenantId = '945c177a-83a2-4e80-9f8c-5a91be5752dd';
const clientId = '75284292-7515-4e2f-aae9-d1777527dd7b';
const clientSecret = 'rVE7Q~Z1BhRXaljbj7SPg~U2HYJRR-feckrxKbCt';
const resource = 'https://mycompany.sharepoint.com';
const privateKeyPassPhrase = "HereIsMyPass1223";
const thumbprint = "F7D8D4F2F140E79B215899BD93A14D0790947789";

// generate the client_assertion
function getClientAssertion () {
  // source: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-node/docs/certificate-credentials.md
  // decrypt the private key
  const privateKeySource = fs.readFileSync("./SharePointOnlinePrivateKey.pem");
  const privateKeyObject = crypto.createPrivateKey({
      key: privateKeySource,
      passphrase: privateKeyPassPhrase,
      format: "pem",
  });

  const privateKey = privateKeyObject.export({
      format: "pem",
      type: "pkcs8",
  });
  
  const config = {
    auth: {
      clientId: clientId,
      authority: `https://login.microsoftonline.com/${tenantId}`,
      clientCertificate: {
        thumbprint: thumbprint, // a 40-digit hexadecimal string
        privateKey: privateKey,
      },
    },
  };

  // Create msal application object
  const cca = new msal.ConfidentialClientApplication(config);
  const helper = {
    createNewGuid:uuid
  }
  const issuer = clientId;
  const jwtAudience = `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`;
  return cca.clientAssertion.getJwt(helper, issuer, jwtAudience);
}

async function getAccessToken() {
  // see https://github.com/SharePoint/sp-dev-docs/issues/5889 and https://learn.microsoft.com/en-us/entra/identity-platform/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate
  try {
    const response = await axios.post(`https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`, new URLSearchParams({
      client_id: clientId,
      scope: `${resource}/.default`,
      client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
      client_assertion: getClientAssertion(),
      grant_type: 'client_credentials',
    }), {
      headers: {
        'Content-Type': 'application/x-www-form-urlencoded'
      }
    });

    return response.data.access_token;
  } catch (error) {
    console.error('Error getting access token:', error);
    throw error;
  }
}

// run the script
getAccessToken()
.then(token => {
  return axios.get(`https://mycompany.sharepoint.com/sites/contoso/Toolbox/Demo/_api/web/lists/getbytitle('Assets')/items(123)?$select=Title`, {
    headers:{
      'Authorization':'Bearer '+token,
      'Accept': 'application/json; odata=verbose'
    }
  })
})
.then(response => {
  console.log(response.data)
})
.catch(console.log)
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/04/29/connect-to-sharepoint-online-from-nodejs-using-an-impersonate-application-bearer-token/feed/</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
		<item>
		<title>Manual Installation of ExplorerPatcher</title>
		<link>https://blog.kodono.info/wordpress/2024/03/21/manual-installation-of-explorerpatcher/</link>
					<comments>https://blog.kodono.info/wordpress/2024/03/21/manual-installation-of-explorerpatcher/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Thu, 21 Mar 2024 08:59:15 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2288</guid>

					<description><![CDATA[ExplorerPatcher permits to change the behavior of the Windows taskbar. If for some reasons the automatic install doesn&#8217;t work, you can proceed manually: Install it on a computer where the automatic install works On the target computer, download ep_setup.exe from the official website Open a Terminal and extract the files from the setup using the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><a href="https://github.com/valinet/ExplorerPatcher">ExplorerPatcher</a> permits to change the behavior of the Windows taskbar. If for some reasons the automatic install doesn&#8217;t work, you can proceed manually:</p>
<ol>
<li>Install it on a computer where the automatic install works</li>
<li>On the target computer, download <code>ep_setup.exe</code> from the official website</li>
<li>Open a Terminal and extract the files from the setup using the command <code>ep_setup.exe /extract</code></li>
<li>Several files have been extracted from the setup</li>
<li>Move all the files to <code>C:\Program Files\ExplorerPatcher</code></li>
<li>From the computer in step 1, transfer the file <code>C:\Windows\dxgi.dll</code> to your other computer</li>
<li>Use a CMD as an Administrator, then kill Windows Explorer, paste the <code>dxgi.dll</code> into <code>C:\Windows</code>, and relaunch Windows Explorer:
<pre class="brush:shell">taskkill /f /im explorer.exe &#038;& copy dxgi.dll C:\Windows\ &#038;& explorer.exe</pre>
</li>
</ol>
<p>To manually <strong>uninstall</strong> it:</p>
<ol>
<li>Open a CMD as an Administrator, then type: <code>taskkill /f /im explorer.exe &#038;& del C:\Windows\dxgi.dll &#038;& rmdir /s /q "C:\Program Files\ExplorerPatcher" &#038;& explorer.exe</code></li>
<li>Open <code>regedit</code> as an Administrator, and delete <code>HKEY_CURRENT_USER\Software\ExplorerPatcher</code>, and search for any other references of <b>ExplorerPatcher</b> to delete</li>
<li>Delete the forlder <code>%APPDATA%\ExplorerPatcher</code></li>
<li>Restart the computer</li>
</ol>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/03/21/manual-installation-of-explorerpatcher/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Power Automate: how to verify if a property belongs to an object (apply to SharePoint Date too)</title>
		<link>https://blog.kodono.info/wordpress/2024/01/16/power-automate-how-to-verify-if-a-property-belongs-to-an-object/</link>
					<comments>https://blog.kodono.info/wordpress/2024/01/16/power-automate-how-to-verify-if-a-property-belongs-to-an-object/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 16 Jan 2024 17:36:30 +0000</pubDate>
				<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2282</guid>

					<description><![CDATA[We can use this kind of formula (notice the questionmark): if(empty(variables('params')?[variables('fieldName')]), 'fieldName is not part of the object params', variables('params')?[variables('fieldName')]) Then it either returns &#8220;fieldName is not part of the object params&#8221;, or the value. We can use it to check if a date field is empty in a SharePoint List, because when getting the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>We can use this kind of formula (notice the questionmark):<br />
<code>if(empty(variables('params')?[variables('fieldName')]), 'fieldName is not part of the object params', variables('params')?[variables('fieldName')])</code></p>
<p>Then it either returns &#8220;fieldName is not part of the object params&#8221;, or the value.</p>
<p>We can use it to check if a date field is empty in a SharePoint List, because when getting the data from the SP List, when the date field is empty, it&#8217;s not in the result returned – the below code will return <b>true</b> if the date field is empty:<br />
<code>empty(item()?['dateFieldNameId'])</code></p>
<p>For a Currency/Number field you need to use <code>string()</code> as well:<br />
<code>empty(string(item()?['floatFieldNameId']))</code></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/01/16/power-automate-how-to-verify-if-a-property-belongs-to-an-object/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Power Automate returns an error about &#8220;InvokerConnectionOverrideFailed&#8221; and &#8220;header.X-MS-APIM-Tokens&#8221;</title>
		<link>https://blog.kodono.info/wordpress/2024/01/16/power-automate-returns-an-error-about-invokerconnectionoverridefailed-and-header-x-ms-apim-tokens/</link>
					<comments>https://blog.kodono.info/wordpress/2024/01/16/power-automate-returns-an-error-about-invokerconnectionoverridefailed-and-header-x-ms-apim-tokens/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 16 Jan 2024 14:42:28 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Debug]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[english]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2278</guid>

					<description><![CDATA[While calling a &#8220;Run a Child Flow&#8221; from a Power Automate Flow, you could get an error about &#8220;InvokerConnectionOverrideFailed&#8221; and &#8220;header.X-MS-APIM-Tokens&#8221;. After investigating, to resolve this issue you need to open the &#8220;details&#8221; view of your child flow, and click on the &#8220;Edit&#8221; button from the &#8220;Run only users&#8221; card: Then in the Connections Used [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>While calling a &#8220;Run a Child Flow&#8221; from a Power Automate Flow, you could get an error about &#8220;InvokerConnectionOverrideFailed&#8221; and &#8220;header.X-MS-APIM-Tokens&#8221;.</p>
<p>After investigating, to resolve this issue you need to open the &#8220;details&#8221; view of your child flow, and click on the &#8220;Edit&#8221; button from the <strong>&#8220;Run only users&#8221; card</strong>:<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2024/01/run_only_users.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2024/01/run_only_users.png" alt="" width="501" height="423" class="aligncenter size-full wp-image-2279" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2024/01/run_only_users.png 501w, https://blog.kodono.info/wordpress/wp-content/uploads/2024/01/run_only_users-300x253.png 300w" sizes="auto, (max-width: 501px) 100vw, 501px" /></a></p>
<p>Then in the <strong>Connections Used</strong> section, make sure to select <strong>&#8220;Use this connection (your_username@domain.com)&#8221;</strong> instead of the default &#8220;Provided by run-only user&#8221;. It will permit to run this child flow with your rights.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/01/16/power-automate-returns-an-error-about-invokerconnectionoverridefailed-and-header-x-ms-apim-tokens/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Supprimer la commande Ctrl+Alt+Supp pour ouvrir Windows 11</title>
		<link>https://blog.kodono.info/wordpress/2024/01/02/supprimer-la-commande-ctrlaltsupp-pour-ouvrir-windows-11/</link>
					<comments>https://blog.kodono.info/wordpress/2024/01/02/supprimer-la-commande-ctrlaltsupp-pour-ouvrir-windows-11/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 02 Jan 2024 08:01:34 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[Français]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[France]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2275</guid>

					<description><![CDATA[Il suffit de modifier le registre de Windows (regedit), en passant à 1 la variable DisableCad dans ces deux emplacements : HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon]]></description>
										<content:encoded><![CDATA[<p>Il suffit de modifier le registre de Windows (<code>regedit</code>), en passant à <b>1</b> la variable <b>DisableCad</b> dans ces deux emplacements :</p>
<ul>
<li>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System</li>
<li>HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon</li>
</ul>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2024/01/02/supprimer-la-commande-ctrlaltsupp-pour-ouvrir-windows-11/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Search and restore an item from a SharePoint Online Recycle Bin</title>
		<link>https://blog.kodono.info/wordpress/2023/11/07/search-and-restore-an-item-from-a-sharepoint-online-recycle-bin/</link>
					<comments>https://blog.kodono.info/wordpress/2023/11/07/search-and-restore-an-item-from-a-sharepoint-online-recycle-bin/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 07 Nov 2023 11:09:37 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[english]]></category>
		<category><![CDATA[sharepoint]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2267</guid>

					<description><![CDATA[It might be difficult to search for an item in a SharePoint recycle bin. I was using the end point _api/site/RecycleBin as a Site Collection Administrator, but in some cases it returns an error &#8220;The attempted operation is prohibited because it exceeds the list view threshold.&#8221;. The solution is to use another end point _api/site/getrecyclebinitems [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>It might be difficult to search for an item in a SharePoint recycle bin. I was using the end point <code>_api/site/RecycleBin</code> as a <b>Site Collection Administrator</b>, but in some cases it returns an error &#8220;The attempted operation is prohibited because it exceeds the list view threshold.&#8221;.</p>
<p>The solution is to use another end point <code>_api/site/getrecyclebinitems</code> (<a href="https://learn.microsoft.com/en-us/dotnet/api/microsoft.sharepoint.client.site.getrecyclebinitems?view=sharepoint-csom">see the documentation</a> for the various parameters):</p>
<pre class="brush:javascript">
fetch("https://tenant.sharepoint.com/sites/YourSite/_api/site/getrecyclebinitems?rowLimit=%2770000%27&#038;itemState=0&#038;orderby=3&#038;isAscending=false", {
  "headers": {
    "accept": "application/json",
    "content-type": "application/json",
    "x-requestdigest": document.querySelector("#__REQUESTDIGEST").value
  },
  "method": "GET",
})
.then(res => res.json())
.then(json => {
  console.log(json.value);
  // it will show an array with an object that contains several interesting properties:
  // AuthorEmail, AuthorName, DeletedByEmail, DeletedDate, DirName, Id, LeafName, Title, …
})
</pre>
<p>You can then filter the result to get what you need, for example if you&#8217;re looking for an item from the list “Projects” that has the ID 1981:</p>
<pre class="brush:javascript">
json.value.filter(item => item.DirName.includes("Lists/Projects") &#038;& item.LeafName === "1981_.000");
</pre>
<p>Then, to restore an item, we need the <code>Id</code> from the previous result. The famous endpoint to restore an item is <code>_api/site/RecycleBin('Id')/restore</code>, however it could also return the error &#8220;The attempted operation is prohibited because it exceeds the list view threshold&#8221;. In that case, we can use this other endpoint <code>_api/site/RecycleBin/RestoreByIds</code>:</p>
<pre class="brush:javascript">
fetch("https://tenant.sharepoint.com/sites/YourSite/_api/site/RecycleBin/RestoreByIds", {
  "headers": {
    "accept": "application/json",
    "content-type": "application/json",
    "x-requestdigest": document.querySelector("#__REQUESTDIGEST").value
  },
  "method": "POST",
  "body":JSON.stringify({
    "ids": [
      "4f855ee7-472b-414a-a482-4317a114c1a2" // Id to restore
    ],
    "bRenameExistingItems": true
  })
})
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2023/11/07/search-and-restore-an-item-from-a-sharepoint-online-recycle-bin/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Calculating HMAC SHA-1 in the Browser</title>
		<link>https://blog.kodono.info/wordpress/2023/04/16/calculating-hmac-sha-1-in-the-browser/</link>
					<comments>https://blog.kodono.info/wordpress/2023/04/16/calculating-hmac-sha-1-in-the-browser/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Sun, 16 Apr 2023 17:23:55 +0000</pubDate>
				<category><![CDATA[Astuce]]></category>
		<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau expert]]></category>
		<category><![CDATA[Programmation]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2258</guid>

					<description><![CDATA[If you&#8217;re looking for the equivalent of hash_hmac('sha1', 'string', 'secret'); in JavaScript, then here you go: async function hmac_sha1 (str, secret) { // see https://stackoverflow.com/a/47332317/1134119 let enc = new TextEncoder("utf-8"); let key = await window.crypto.subtle.importKey( "raw", // raw format of the key - should be Uint8Array enc.encode(secret), { // algorithm details name: "HMAC", hash: {name: [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>If you&#8217;re looking for the equivalent of <code>hash_hmac('sha1', 'string', 'secret');</code> in JavaScript, then here you go:</p>
<pre class="brush:js">
async function hmac_sha1 (str, secret) {
  // see https://stackoverflow.com/a/47332317/1134119
  let enc = new TextEncoder("utf-8");
  let key = await window.crypto.subtle.importKey(
    "raw", // raw format of the key - should be Uint8Array
    enc.encode(secret),
    { // algorithm details
      name: "HMAC",
      hash: {name: "SHA-1"}
    },
    false, // export = false
    ["sign", "verify"] // what this key can do
  );
  let signature = await window.crypto.subtle.sign(
    "HMAC",
    key,
    enc.encode(str)
  );
  let b = new Uint8Array(signature);
  return Array.prototype.map.call(b, x => x.toString(16).padStart(2, '0')).join("");
}
</pre>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2023/04/16/calculating-hmac-sha-1-in-the-browser/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Connect to SharePoint Online using an app clientId and clientSecret</title>
		<link>https://blog.kodono.info/wordpress/2023/03/21/connect-to-sharepoint-online-using-an-app-clientid-and-clientsecret/</link>
					<comments>https://blog.kodono.info/wordpress/2023/03/21/connect-to-sharepoint-online-using-an-app-clientid-and-clientsecret/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Tue, 21 Mar 2023 09:56:30 +0000</pubDate>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[english]]></category>
		<category><![CDATA[sharepoint]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2234</guid>

					<description><![CDATA[Get `clientId` and `clientSecret` (source) You&#8217;ll need credentials: `clientId` &#8211; required string, client id obtained when registering the addin `clientSecret` &#8211; required string, client secret obtained when registering the addin `realm` &#8211; your SharePoint Online tenant id. The easiest way to find tenant is to open SharePoint Online site collection, click Site Settings &#8594; Site [&#8230;]]]></description>
										<content:encoded><![CDATA[<h3>Get <code>`clientId`</code> and <code>`clientSecret`</code></h3>
<p>(<a href="https://github.com/s-KaiNet/node-sp-auth/wiki/SharePoint-Online-addin-only-authentication">source</a>)</p>
<p>You&#8217;ll need credentials: </p>
<ul>
<li><code>`clientId`</code> &#8211; required string, client id obtained when registering the addin</li>
<li><code>`clientSecret`</code> &#8211; required string, client secret obtained when registering the addin</li>
<li><code>`realm`</code> &#8211; your SharePoint Online tenant id. The easiest way to find tenant is to open SharePoint Online site collection, click <b>Site Settings</b> &rarr; <b>Site App Permissions</b>. Under this page you wll see at least one app &#8220;Microsoft.SharePoint&#8221;. The tenant id (realm) is highlighted in the image below:<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm-1024x183.png" alt="" width="1024" height="183" class="aligncenter size-large wp-image-2236" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm-1024x183.png 1024w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm-300x54.png 300w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm-768x137.png 768w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/realm.png 1286w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></li>
</ul>
<p>Example of the expected result: </p>
<pre class="brush:javascript">
{
  clientId: '28bq7e56-8c3a-487d-hbfb-ef1a74539cbe',
  clientSecret: 's6LZ4VvoeKOS+MyAhklcavsyJBF4XhWo06OgY6czYJ0=',
  realm: '85e5f09b-4c17-4d80-afea-260bb171c456'
}
</pre>
<p>To get the credentials, you need to register a new addin inside SharePoint Online, by fellowing these steps:</p>
<ol>
<li>Open SharePoint Online app registration page, e.g. <code>https://contoso.sharepoint.com/sites/dev/_layouts/15/appregnew.aspx</code></li>
<li>Click on <b>&#8220;Generate&#8221;</b> for Client id and Client Secret, fill in Title, App Domain, Redirect URI (you can type in any values you want)<br />
         <a href="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_reg.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_reg.png" alt="" width="374" height="364" class="aligncenter size-full wp-image-2237" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_reg.png 374w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_reg-300x292.png 300w" sizes="auto, (max-width: 374px) 100vw, 374px" /></a></li>
<li>Click on <b>&#8220;Create&#8221;</b> and save generated Client Id and Client Secret</li>
<li><strong>[IF YOU HAVE TENANT RIGHTS]</strong> Now you need to apply permissions to the newly registered app. If you want to register the app once and use it for any site collection, it&#8217;s better to apply <strong>tenant scope permissions</strong>, so you can use the credentials everywhere inside your SharePoint tenant. To apply tenant scoped permissions, open <code>AppInv.aspx</code> page under SharePoint adminstration web site, e.g. <code>https://[YOUR_ORGANIZATION]-admin.sharepoint.com/_layouts/15/appinv.aspx</code>, copy paste Client Id from step n°3 into App Id field and click <b>&#8220;Lookup&#8221;</b>.</li>
<li><strong>[IF YOU HAVE TENANT RIGHTS]</strong> You will see your registered app, paste in the following XML into the <b>&#8220;Permission Request XML&#8221;</b> field and click <b>&#8220;Create&#8221;</b>:
<pre class="rush:xml">
    &lt;AppPermissionRequests AllowAppOnlyPolicy="true">
      &lt;AppPermissionRequest Scope="http://sharepoint/content/tenant" Right="FullControl" />
    &lt;/AppPermissionRequests>
</pre>
<p><a href="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_permission.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_permission.png" alt="" width="614" height="580" class="aligncenter size-full wp-image-2238" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_permission.png 614w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_permission-300x283.png 300w" sizes="auto, (max-width: 614px) 100vw, 614px" /></a></li>
<li><strong>[IF YOU ARE NOT A TENANT]</strong> If you only want to give permissions on 1 site collection, you can register the app on a regular site collection by using url <code>https://contoso.sharepoint.com/sites/dev/_layouts/15/appinv.aspx</code>. In this case you are not able to use tenant scoped permissions and can only apply site collection permissions:
<pre class="rush:xml">
    &lt;AppPermissionRequests AllowAppOnlyPolicy="true">
      &lt;AppPermissionRequest Scope="http://sharepoint/content/sitecollection" Right="FullControl" />
    &lt;/AppPermissionRequests>
</pre>
<li>You will see addin &#8220;Trust&#8221; confirmation, click on <b>&#8220;Trust It&#8221;</b>:<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_trust.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_trust.png" alt="" width="767" height="322" class="aligncenter size-full wp-image-2239" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_trust.png 767w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/03/online_addinonly_trust-300x126.png 300w" sizes="auto, (max-width: 767px) 100vw, 767px" /></a><br />
 <em>if trust-it button is not enabled and you get a red label saying tenant admin needs to trust the app, go back and try again in a few minutes.</em></li>
<li>Now you can use client id and client secret to send authenticated http requests.</li>
</ol>
<p>To know more about the XML permissions, you can check the <a href="http://(see https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/add-in-permissions-in-sharepoint)">Microsoft documentation</a>.</p>
<h3>Get Access Token</h3>
<p>(you can find a <a href="https://social.technet.microsoft.com/wiki/contents/articles/51982.sharepoint-read-online-list-data-from-c-console-application-using-access-token.aspx">C# code</a> as an example)</p>
<p>You need to do a <b>POST</b> request to <code>https://accounts.accesscontrol.windows.net/[YOUR_TENANT_REALM]/tokens/OAuth/2</code> with a <b>&#8220;Content-Type&#8221;</b> header that has the value <b>&#8220;application/x-www-form-urlencoded&#8221;</b>, and the body parameters that must be:</p>
<ul>
<li>&quot;grant_type&quot;:&quot;client_credentials&quot;</li>
<li>&quot;client_id&quot;:&quot;[YOUR_CLIENT_ID]@[YOUR_TENANT_REALM]&quot;</li>
<li>&quot;client_secret&quot;:&quot;[YOUR_CLIENT_SECRET]&quot;</li>
<li>&quot;resource&quot;:&quot;00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]&quot;</li>
</ul>
<p>See below an example in PHP:</p>
<pre class=brush:php">
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, "https://accounts.accesscontrol.windows.net/[YOUR_TENANT_REALM]/tokens/OAuth/2");
curl_setopt($curl, CURLOPT_HTTPHEADER, [ "Content-Type: application/x-www-form-urlencoded" ]);
curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query([
  "grant_type" => "client_credentials",
  "client_id" => "[YOUR_CLIENT_ID]@[YOUR_TENANT_REALM]",
  "client_secret" => "[YOUR_CLIENT_SECRET]",
  "resource" => "00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]"
]));
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($curl));
curl_close($curl);

echo $response->access_token;
</pre>
<p>The response should contain an access token. Example:</p>
<pre class="brush:javascript">
{
  "token_type":"Bearer",
  "expires_in":"86399",
  "not_before":"1679393911",
  "expires_on":"1679480611",
  "resource":"00000003-0000-0ff1-ce00-000000000000/dell.sharepoint.com@[YOUR_TENANT_REALM]",
  "access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSU[...]SxXA5Lqbk1OcOVdwQ"
}
</pre>
<p>Finally, you can do your REST API request to SharePoint Online with passing the header <b>&#8220;Authorization&#8221;</b> that has the value <b>&#8220;Bearer [YOUR_ACCESS_TOKEN]&#8221;</b>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2023/03/21/connect-to-sharepoint-online-using-an-app-clientid-and-clientsecret/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Mount a SMB Network Drive with Pivotal Cloud Foundry (pcf) and access it using NodeJS</title>
		<link>https://blog.kodono.info/wordpress/2023/01/20/mount-a-smb-network-drive-with-pivotal-cloud-foundry-pcf-and-access-it-using-nodejs/</link>
					<comments>https://blog.kodono.info/wordpress/2023/01/20/mount-a-smb-network-drive-with-pivotal-cloud-foundry-pcf-and-access-it-using-nodejs/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Fri, 20 Jan 2023 15:27:56 +0000</pubDate>
				<category><![CDATA[Divers]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2229</guid>

					<description><![CDATA[Connect to the Apps Manager and navigate to the space where you want to mount the drive. Look at the members, and make sure your user has the correct roles: Then in a console, connect and log into the pcf space (something like cf login and cf -s APPNAME). Next, try the command cf create-service [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Connect to the Apps Manager and navigate to the space where you want to mount the drive. Look at the members, and make sure your user has the correct roles:<br />
<a href="https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space-1024x278.png" alt="" width="1024" height="278" class="aligncenter size-large wp-image-2230" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space-1024x278.png 1024w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space-300x82.png 300w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space-768x209.png 768w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space-1536x417.png 1536w, https://blog.kodono.info/wordpress/wp-content/uploads/2023/01/pcf-app-manager-space.png 1549w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
<p>Then in a console, connect and log into the pcf space (something like <code>cf login</code> and <code>cf -s APPNAME</code>).</p>
<p>Next, try the command <code>cf create-service smb Existing Files_Share -c '{\"share\":\"//server.name.domain.com/Files_Share\"}'</code> (the backslashes are important if you&#8217;re on Windows).</p>
<p>Now you can bind the service with <code>cf bind-service APPNAME Files_Share -c '{\"username\":\"username_without_domain\",\"password\":\"network_password\",\"mount\":\"/mnt/Files_Share\"}'</code></p>
<p>For more info, see <a href="https://docs.cloudfoundry.org/devguide/services/using-vol-services.html#smb">https://docs.cloudfoundry.org/devguide/services/using-vol-services.html#smb</a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2023/01/20/mount-a-smb-network-drive-with-pivotal-cloud-foundry-pcf-and-access-it-using-nodejs/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>spfx error: No development certificate found. Generate a new certificate manually, or set the `canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`</title>
		<link>https://blog.kodono.info/wordpress/2022/12/30/spfx-error-no-development-certificate-found-generate-a-new-certificate-manually-or-set-the-cangeneratenewcertificate-parameter-to-true-when-calling-ensurecertificateasync/</link>
					<comments>https://blog.kodono.info/wordpress/2022/12/30/spfx-error-no-development-certificate-found-generate-a-new-certificate-manually-or-set-the-cangeneratenewcertificate-parameter-to-true-when-calling-ensurecertificateasync/#respond</comments>
		
		<dc:creator><![CDATA[Aymeric]]></dc:creator>
		<pubDate>Fri, 30 Dec 2022 14:37:21 +0000</pubDate>
				<category><![CDATA[English]]></category>
		<category><![CDATA[Niveau débutant]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[Anglais]]></category>
		<guid isPermaLink="false">https://blog.kodono.info/wordpress/?p=2225</guid>

					<description><![CDATA[When using the command gulp serve, you could receive the below error: No development certificate found. Generate a new certificate manually, or set the `canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync` To resolve, you can type gulp trust-dev-cert.]]></description>
										<content:encoded><![CDATA[<p>When using the command <code>gulp serve</code>, you could receive the below error:</p>
<blockquote><p>No development certificate found. Generate a new certificate manually, or set the `canGenerateNewCertificate` parameter to `true` when calling `ensureCertificateAsync`</p></blockquote>
<p>To resolve, you can type <code>gulp trust-dev-cert</code>.</p>
<p><a href="https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error.png"><img loading="lazy" decoding="async" src="https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error-1024x332.png" alt="" width="1024" height="332" class="aligncenter size-large wp-image-2226" srcset="https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error-1024x332.png 1024w, https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error-300x97.png 300w, https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error-768x249.png 768w, https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error-1536x499.png 1536w, https://blog.kodono.info/wordpress/wp-content/uploads/2022/12/spfx_error.png 1891w" sizes="auto, (max-width: 1024px) 100vw, 1024px" /></a></p>
]]></content:encoded>
					
					<wfw:commentRss>https://blog.kodono.info/wordpress/2022/12/30/spfx-error-no-development-certificate-found-generate-a-new-certificate-manually-or-set-the-cangeneratenewcertificate-parameter-to-true-when-calling-ensurecertificateasync/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
