# articles

feed_id: http://www.yosko.net/?d=2013/06/20/17/00/00-mon-firefox
title: Mon Firefox
uri: Mon-Firefox
url: http://www.yosko.net/?d=2013/06/20/17/00/00-mon-firefox
content:
<b>Les addons et réglages dont je ne peux me passer dans mon navigateur fétiche.</b><br/><img class="right" src="media/e3a9fb95.firefox.png" data-original-source="http://www.yosko.net/img/firefox.png" />
En cette pénurie d'article (je travaille sur des gros trucs qui prennent du temps, mais promis, vous en verrez le bout du nez un de ces 4. Et Faut vraiment que je passe mes derniers devs sur DDb en release), j'ai décidé de vous parler de mon utilisation de Firefox.<br/>
<br/>
En effet, <a href="http://sebsauvage.net/links/?IGXAgw">sebsauvage nous indique aujourd'hui les addons que lui utilise</a>. Je me suis dit qu'il pourrait être intéressant de vous partager ma configuration aussi&nbsp;:<br/>
<ul>
  <li><span style="text-decoration: underline;">Mes addons préférés</span> (par ordre d'importance... presque pas du tout subjectif)&nbsp;:<br/>
    <ul>
      <li><a href="https://addons.mozilla.org/en-US/firefox/addon/tree-style-tab/">Tree Style Tab</a>&nbsp;: avec un minimum de 50 onglets ouvert tout le temps, je ne vois pas comment je pourrais me passer de cette petite perle, qui rend la recherche sur le web beaucoup plus facile. En gros, il affiche les onglets à gauche, en colonne, et les hiérarchise (si un onglet est ouvert depuis un autre, il devient son enfant).</li>
      <li><a href="https://addons.mozilla.org/en-US/firefox/addon/duckduckgo-for-firefox/?src=ss">DuckDuckGo Plus</a>&nbsp;: pratique pour surfer sur la vague de succès de ce moteur de recherche, et pour débuter avec la syntaxe&nbsp;!bang</li>
      <li><a href="https://addons.mozilla.org/en-US/firefox/addon/keefox/?src=search">KeeFox</a>&nbsp;: associé à KeePass et ownCloud, me permet de backuper tous mes mots de passe en ligne de façon sécurisée et maitrisée (pas que je ne fasse pas confiance à Firefox Sync ou LastPass, mais on n'est jamais mieux servi que par soi-même).</li>
      <li>Les classiques de la tranquillité&nbsp;: WOT, Ghostery (<a href="http://www.yosko.net/?d=2012/12/17/15/00/00-ghostery-saved-my-internet">pour rappel</a>), AdBlock Plus, Flashblock</li>
    </ul>
  </li>
  <li><span style="text-decoration: underline;">Les modifs à la main dans <code>about:config</code>&nbsp;:</span><br/>
    <ul>
      <li>Toujours afficher "http://" dans la barre d'adresse&nbsp;: <code>browser.urlbar.trimURLs</code> à <code>false</code></li>
      <li>Ouvrir les recherches dans un nouvel onglet&nbsp;: <code>browser.search.openintab</code> à <code>true</code> (à combiner avec <code>Ctrl+E</code> pour mettre le focus sur la barre de recherche).</li>
    </ul>
  </li>
  <li><span style="text-decoration: underline;">Autres modifs utiles&nbsp;:</span><br/>
    <ul>
      <li>J'ai remis le bouton RSS dans la barre d'adresse (et il n'y a vraiment pas besoin d'addon pour ça, contrairement à ce que dit sebsauvage). Autre solution&nbsp;: aller dans "Marques-pages > S'abonner à cette page". Inutile de se prendre le chou pour rien&nbsp;:P</li>
    </ul>
  </li>
</ul>
<br/>
J'évite la liste exhaustive des addons, sinon on en aurait pour deux jours&nbsp;:-D


feed_id: http://www.yosko.net/?d=2013/05/14/16/00/00-snippet-03---php-pagination
title: [Snippet #03 - PHP] pagination
uri: Snippet-03-PHP-pagination
url: http://www.yosko.net/?d=2013/05/14/16/00/00-snippet-03---php-pagination
content:
<b></b><br/>Lorsqu'on réalise un site web, on a toujours, à un moment ou à un autre, besoin de réaliser une pagination avec des liens vers les autres pages.<br/>
<br/>
Comme ce genre de code se ressemble toujours, et qu'on a vite tendance à y oublier un +1/-1, j'ai décidé de me faire une petite fonction dédiée et de vous la partager&nbsp;:<br/>
<pre class="brush: php;">
/**<br/>
 * Generate an array of pagination link where the key is a page number<br/>
 * and the value is a type of link (current page, normal link, first/last page link)<br/>
 * @param  integer $currentPage   the current displayed page<br/>
 * @param  integer $totalPages    the total number of pages (can be replaced by the two following params)<br/>
 * @param  integer $itemPerPage   the number of item displayed on each page<br/>
 * @param  integer $totalItems    the total number of existing items<br/>
 * @param  integer $nbPagesAround the maximum number of links (excluding first/last) that should be displayed before or after the current page<br/>
 * @return array                  the pagination array (key = page to link to, value = type of link)<br/>
 */<br/>
function generatePagination(<br/>
	$currentPage,<br/>
	$totalPages = 0,<br/>
	$itemPerPage = 0,<br/>
	$totalItems = 0,<br/>
	$nbPagesAround = 2<br/>
) {<br/>
	$pagination = array();<br/>
<br/>
	if($totalPages == 0) {<br/>
		if($itemPerPage == 0 || $totalItems == 0) {<br/>
			return false;<br/>
		} else {<br/>
			$totalPages = (int)ceil($totalItems / $itemPerPage);<br/>
		}<br/>
	}<br/>
<br/>
	if($currentPage > $nbPagesAround + 2) {<br/>
		$pagination[1] = self::PAGINATION_FIRST;<br/>
	} elseif($currentPage > $nbPagesAround + 1) {<br/>
		$pagination[1] = self::PAGINATION_LINK;<br/>
	}<br/>
	for($i = ($currentPage - $nbPagesAround); $i < $currentPage; $i++) {<br/>
		if($i > 1 || ($i == 1 && $currentPage <= $nbPagesAround + 1)) {<br/>
			$pagination[$i] = self::PAGINATION_LINK;<br/>
		}<br/>
	}<br/>
	$pagination[$currentPage] = self::PAGINATION_CURRENT;<br/>
	for($i = ($currentPage + 1); $i < ($currentPage + $nbPagesAround + 1); $i++) {<br/>
		if($i < $totalPages<br/>
			|| ($i == $totalPages && $currentPage >= $totalPages - $nbPagesAround)<br/>
		) {<br/>
			$pagination[$i] = self::PAGINATION_LINK;<br/>
		}<br/>
	}<br/>
	if($currentPage < ($totalPages - $nbPagesAround - 1)) {<br/>
		$pagination[$totalPages] = self::PAGINATION_LAST;<br/>
	} elseif($currentPage < ($totalPages - $nbPagesAround)) {<br/>
		$pagination[$totalPages] = self::PAGINATION_LINK;<br/>
	}<br/>
<br/>
	// ksort($pagination);<br/>
<br/>
	return $pagination;<br/>
}<br/>
</pre>
Ce code vous retournera un tableau avec la liste des pages vers lesquels vous devrez faire des liens. Pour chaque élément du tableau<br/>
<ul>
  <li><span style="font-weight: bold;"><span style="text-decoration: underline;">La clé&nbsp;:</span></span> numéro de la page</li>
  <li><span style="font-weight: bold;"><span style="text-decoration: underline;">La valeur&nbsp;:</span></span> type de lien, suivant le modèle suivant&nbsp;:<br/>
    <ul>
      <li><span style="font-weight: bold;"><span style="text-decoration: underline;">link&nbsp;:</span></span> lien classique vers une autre page</li>
      <li><span style="font-weight: bold;"><span style="text-decoration: underline;">current&nbsp;:</span></span> item pour la page courante (généralement affiché dans un thème différent, et sans lien</li>
      <li><span style="font-weight: bold;"><span style="text-decoration: underline;">first&nbsp;:</span></span> lien vers la toute première page (généralement affiché avec <span style="font-weight: bold;"><span style="text-decoration: underline;">&laquo;</span></span>).</li>
      <li><span style="font-weight: bold;"><span style="text-decoration: underline;">last&nbsp;:</span></span> lien vers la toute dernière page (généralement affiché avec <span style="font-weight: bold;"><span style="text-decoration: underline;">&raquo;</span></span>).</li>
    </ul>
  </li>
</ul>
Il n'y a ensuite plus qu'à gérer l'affichage dans votre vue (ici, exemple réalisé avec le gestionnaire de template rainTPL)&nbsp;:<br/>
<pre class="brush: php;">
<div class="pagination">{loop="pagination"}<br/>
    {if="$value == 'current'"}<br/>
        <span>{$key}</span>
    {else}<br/>
        {if="$value == 'last'"}&hellip;{/if}<br/>
        <a href="http://www.yosko.net//path/to/my/list/{$key}">
            {if="$value == 'first'"}<br/>
                &laquo;<br/>
            {elseif="$value == 'last'"}<br/>
                &raquo;<br/>
            {else}<br/>
                {$key}<br/>
            {/if}<br/>
        </a>
    {/if}<br/>
    {if="$value == 'first'"}&hellip;{/if}<br/>
    {/loop}<br/>
</div>
</pre>
<br/>
Avec un peu de CSS, on arrive à un résultat sympa&nbsp;:<br/>
<div style="text-align:center;"><img src="media/1ad96e8f.pagination.png" data-original-source="http://www.yosko.net/img/pagination.png" /></div><br/>
<br/>
En espérant que ça puisse être utile à quelqu'un.<br/>
<br/>
Keep cool & rock on.


feed_id: http://www.yosko.net/?d=2013/04/07/12/00/00-noter-ses-mp3-dans-foobar2000
title: Noter ses MP3 dans foobar2000
uri: Noter-ses-MP3-dans-foobar2000
url: http://www.yosko.net/?d=2013/04/07/12/00/00-noter-ses-mp3-dans-foobar2000
content:
<b></b><br/>Je vous évoquais il y a quelques temps les <a href="http://www.yosko.net/?d=2012/12/21/18/14/42-noter-ses-mp3-de-facon-perenne">logiciels de musique permettant de noter vos MP3 de façon pérenne</a>. Parmis ceux-ci, seuls 2 sortaient du lot, dont un certain "foobar2000".<br/>
<br/>
Je vous propose aujourd'hui de vous guider pas-à-pas pour configurer foobar2000 afin qu'il vous permette de&nbsp;:<br/>
<ul>
  <li>Noter vos MP3 de façon pérenne</li>
  <li>Afficher joliment vos notes dans la liste de lecture</li>
</ul>
Note&nbsp;: cet article est en partie une traduction de <a href="http://www.hydrogenaudio.org/forums/index.php?showtopic=71224">ce tutoriel</a> en anglais, que j'ai adapté à mes besoin et complété de mes remarques.<br/>
<h3>Installation</h3>
<ol>
<li><span style="font-weight: bold;">Téléchargez et installez <a href="http://www.foobar2000.org/">foobar2000</a></span> (si ce n'est déjà fait).</li>
<li><span style="font-weight: bold;">Téléchargez le plugin <a href="http://wiki.hydrogenaudio.org/index.php?title=Foobar2000:Components_0.9/Quick_Tagger_%28foo_quicktag%29">foo_quicktag</a> &nbsp;:</span> nous permettra d'écrire aisément la note dans le tag "rating" via raccourcis clavier ou menu contextuel (peut être remplacé par <a href="http://wiki.hydrogenaudio.org/index.php?title=Foobar2000:Components_0.9/Masstagger_%28foo_masstag%29">foo_masstag</a> selon les préférences. Cependant, foo_quicktag offre une "sécurité" via un avertissement lorsqu'on note plusieurs fichiers en même temps, pour éviter toute fausse manipulation).</li>
<li><span style="font-weight: bold;">Installez le plugin&nbsp;:</span> copiez simplement le fichier DLL correspondant dans le dossier "component" de foobar2000 (par défaut sous Seven&nbsp;: C:\Program Files (x86)\foobar2000\components)</li>
<li><span style="font-weight: bold;">(Re-)Démarrez foobar2000</span>.</li>
</ol>
<br/>
Pour l'instant, voici l'affichage que nous avons&nbsp;:<br/>
<br/>
<div style="text-align:center;"><a href="media/1e190b8e.fb2k-rating-01.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-01.jpg" class="fancybox" rel="fb2k-rating"><img src="media/1e190b8e.fb2k-rating-01.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-01.jpg" /></a></div><br/>
<h3>Configuration</h3>
<ol>
<li><span style="font-weight: bold;">Créer la colonne "Rating"&nbsp;:</span> Allez dans "<span style="text-decoration: underline;">Library > Configure</span>" puis dans "<span style="text-decoration: underline;">Display > Default User Interface > PlaylistView</span>", cliquez sur "<span style="text-decoration: underline;">Add New</span>", nommez le nouveau champs comme bon vous semble et dans pattern, entrez le code suivant&nbsp;:<br/>
<pre class="brush:bash">
$if(%rating%,$repeat(●,%rating%)$repeat(o,$sub(5,%rating%)),ooooo)<br/>
</pre>
<div style="text-align:center;"><a href="media/01f044a2.fb2k-rating-02.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-02.jpg" class="fancybox" rel="fb2k-rating"><img src="media/50896af1.fb2k-rating-02-thb.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-02-thb.jpg" /></a></div></li>
<li><span style="font-weight: bold;">Affichez la colonne "Rating"&nbsp;:</span> dans la liste de lecture, faites un clic droit sur les en-têtes de colonnes, et dans "<span style="text-decoration: underline;">Columns</span>", cochez votre nouvelle colonne.<br/>
<div style="text-align:center;"><a href="media/fcaec31e.fb2k-rating-03.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-03.jpg" class="fancybox" rel="fb2k-rating"><img src="media/19caf6d4.fb2k-rating-03-thb.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-03-thb.jpg" /></a></div><br/>
La nouvelle colonne s'affiche enfin&nbsp;:<br/>
<div style="text-align:center;"><a href="media/3c626a4e.fb2k-rating-04.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-04.jpg" class="fancybox" rel="fb2k-rating"><img src="media/652bf512.fb2k-rating-04-thb.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-04-thb.jpg" /></a></div></li>
<li><span style="font-weight: bold;">Définir les valeurs possibles&nbsp;:</span> retournez dans "<span style="text-decoration: underline;">Library > Configure</span>" et allez cette-fois-ci dans "<span style="text-decoration: underline;">Tools > Tagging > Quick Tagger</span>". Ajoutez le champ "<span style="text-decoration: underline;">rating</span>" avec les valeurs possibles&nbsp;: <span style="text-decoration: underline;">0;1;2;3;4;5</span><br/>
<div style="text-align:center;"><a href="media/b5854413.fb2k-rating-05.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-05.jpg" class="fancybox" rel="fb2k-rating"><img src="media/40bbfc8d.fb2k-rating-05-thb.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-05-thb.jpg" /></a></div></li>
<li><span style="font-weight: bold;">Avertissement en cas de notation multiple</span>&nbsp;: pour éviter de noter accidentellement l'ensemble de votre bibliothèque si celle-ci est sélectionnée en entier lorsque vous appliquerez une note, je vous conseille de demander la confirmation à chaque fois que la note s'applique à plus d'un MP3. Pour cela, et comme dans l'image ci-dessus, indiquez "1" dans le champ situé au bas de l'écran de Quick Tagger.</li>
<li><span style="font-weight: bold;">Noter manuellement un mp3&nbsp;:</span> vous pouvez dès à présent noter vos mp3, bien que ça ne soit pas très pratique. Pour cela, faites un clic droit sur le morceau, et allez dans "<span style="text-decoration: underline;">Tagging > Quick Tagger > set <rating> to ></span>" et choissisez une valeur.</li>
<li><span style="font-weight: bold;">Mettre en place les raccourcis clavier&nbsp;:</span> retournez une dernière fois dans "<span style="text-decoration: underline;">Library > Configure</span>", allez sur "<span style="text-decoration: underline;">Keyboard Shortcuts</span>" et saisissez les raccourcis qui vous conviennent.<br/>
<div style="text-align:center;"><a href="media/86860efa.fb2k-rating-06.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-06.jpg" class="fancybox" rel="fb2k-rating"><img src="media/8078733f.fb2k-rating-06-thb.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-06-thb.jpg" /></a></div><br/>
Comme on le voit dans l'image ci-dessus, je les ai personnellement configurés sur <span style="text-decoration: underline;"><code>Ctrl+Alt+{Chiffre}</code></span>, et j'ai coché "Global hotkey" pour que le raccourci fonctionne même quand foobar2000 n'a plus le focus.</li>
</ol>
<br/>
Et voilà, tout est prêt pour que vous puissiez noter vos MP3 aisément dans foobar2000.<br/>
<br/>
<div style="text-align:center;"><a href="media/2512680b.fb2k-rating-07.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-07.jpg" class="fancybox" rel="fb2k-rating"><img src="media/2512680b.fb2k-rating-07.jpg" data-original-source="http://www.yosko.net/img/fb2k-rating-07.jpg" /></a></div><br/>
<h3>Look</h3>
Vous pouvez varier la façon d'afficher la note selon vos envies. Remplacez par exemple les ronds (●o) par des étoiles (★☆) ou des carrés (■□). Ou changez complètement la mise en forme de la colonne "rating"&nbsp;:<br/>
<pre class="brush:bash">
$if(%rating%,$repeat(●,%rating%))   #n'affiche que les ronds pleins, pas les vides<br/>
%rating%                            #affiche le chiffre directement<br/>
</pre>
Attention cependant, certains caractères spéciaux ne fonctionneront peut-être pas sur votre machine.


feed_id: http://www.yosko.net/?d=2013/04/01/15/00/00-ddb-is-back-v13
title: DDb is back ! (v1.3)
uri: DDb-is-back-(v1-3)
url: http://www.yosko.net/?d=2013/04/01/15/00/00-ddb-is-back-v13
content:
<b></b><br/><img class="right" src="media/34e0ae93.ddb-logo.png" data-original-source="http://www.yosko.net/img/ddb-logo.png" />
DDb est un petit site web en PHP à héberger sur votre serveur, permettant de noter vos rêves et ceux de votre entourage.<br/>
<br/>
Depuis la <a href="http://www.yosko.net/?d=2012/11/13/21/00/00-ddb-travaillez-votre-memoire-onirique">dernière version</a>, il a bien évolué. En partie grâce à l'aide de <a href="https://github.com/BoboTiG/">BoboTig</a>, tant en idée qu'en code (thème mobile, thème de nuit, tags à icône, etc...).<br/>
<h3>Téléchargement</h3>
Vous pouvez récupérer DDb depuis l'une de ces deux sources&nbsp;:<br/>
<ul>
  <li><a href="https://github.com/yosko/ddb">DDb sur Github</a>&nbsp;: les dernières versions à jour</li>
  <li><a href="http://dev.yosko.net/wiki/doku.php?id=web:php:ddb">DDb sur mon wiki</a>&nbsp;: changelog et téléchargement de versions plus anciennes</li>
</ul>
<h3>Nouveautés de la 1.3</h3>
<ul>
  <li>Gestion multi-utilisateurs</li>
  <li>Gestion des autorisations pour chaque utilisateur.</li>
  <li>Intégration de <a href="http://www.yosko.net/index.php?d=2013/01/04/12/00/00-update-site-php-se-souvenir-de-moi">YosLogin</a> pour gérer l'authentification</li>
  <li>Ajout d'une clé à l'URL du flux RSS (pour éviter que le premier venu puisse accéder à vos rêves)</li>
  <li>Ajout d'un thème de nuit facultatif, qui sera utilisé aux heures voulues (exemple&nbsp;: de 20h à 7h)</li>
  <li>Adaptation du thème pour les mobiles</li>
  <li>Ajout de tags prédéfinis avec icônes (cauchemar, adulte, lucide)</li>
  <li>Refonte complète de l'administration/configuration de DDb et ajout de nombreuses fonctionnalités mineures</li>
  <li>Corrections de bugs et de failles de sécurité</li>
</ul>
<h3>Aperçu de la 1.3</h3>
<div style="text-align:center;"><a href="media/0ecff966.ddb13-screen01.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen01.jpg" class="fancybox" rel="ddb1.3"><img src="media/e94c4ef4.ddb13-screen01-thb.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen01-thb.jpg" /></a> <a href="media/c21a9b38.ddb13-screen02.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen02.jpg" class="fancybox" rel="ddb1.3"><img src="media/cc755d6b.ddb13-screen02-thb.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen02-thb.jpg" /></a> <a href="media/d44b39d4.ddb13-screen03.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen03.jpg" class="fancybox" rel="ddb1.3"><img src="media/13ea7d45.ddb13-screen03-thb.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen03-thb.jpg" /></a> <a href="media/7b0e2a01.ddb13-screen04.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen04.jpg" class="fancybox" rel="ddb1.3"><img src="media/4051ddcb.ddb13-screen04-thb.jpg" data-original-source="http://www.yosko.net/img/ddb13-screen04-thb.jpg" /></a></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">Cliquez pour agrandir</span></div><br/>
<h3>Futur</h3>
Encore d'autres choses sont prévues pour DDb. Si le but sera de s'éloigner le moins possible du <a href="http://en.wikipedia.org/wiki/KISS_principle">modèle KISS</a>, on envisage certaines autres fonctionnalités, pour une future 1.4. Parmi celles-ci, on retrouvera&nbsp;:<br/>
<ul>
  <li>Commenter les rêves (+ flux RSS des commentaires)</li>
  <li>Garder un rêve en mode "brouillon"/"privé"</li>
  <li>De nombreux nouveaux petits réglages supplémentaires possibles</li>
  <li>Ajouter/Modifier les tags comportant des icônes</li>
  <li>... et bien d'autres encore</li>
</ul>
<br/>
Encore et toujours, si vous rencontrez le moindre bug ou si vous avez la moindre idée à partager sur DDb, n'hésitez pas à le faire (de préférence sur la page Github du projet).<br/>
<br/>
Keep dreamin' & Rock on&nbsp;!


feed_id: http://www.yosko.net/?d=2012/10/06/01/03/26-one-piece-two-piece-three
title: One Piece, Two Piece, Three...
uri: One-Piece-Two-Piece-Three
url: http://www.yosko.net/?d=2012/10/06/01/03/26-one-piece-two-piece-three
content:
<b></b><br/><img src="media/4b98086c.One-Piece-Flag.jpg" data-original-source="http://www.yosko.net/img/One-Piece-Flag.jpg" class="right" />
Vous êtes vous déjà amusé à rechercher des modèles mathématique aux sein d'œuvres artistiques, de comportement aléatoire ou de toute autre forme d'activité humaine ou naturelle ? Non ?<br/>
<br/>
Parce que moi, si&nbsp;!<br/>
<br/>
Vous pensez qu'il faut se mettre à consulter quand on se demande quelles sont la moyenne et son écart-type du nombre de chocapics dans un paquet de 750g ? (je vous rassure tout de suite, je ne suis pas allé jusqu'à compter... mais j'ai quand même longuement hésité). Ou encore du temps qu'il faudrait à une navette spatiale de notre époque pour rallier Bételgeuse (A ~500 années-lumière. La réponse est environ 20 400 000 années à 28 000km/h) ?<br/>
<br/>
Récemment, je me suis replongé dans le début de la saga One Piece. En effet, les tomes se faisant trop attendre (le 66 arrive le 3 avril, soit dans 8 jours), et je suis l'édition française plutôt que les scans. Après l'arrivée de Nami comme 3ème membre dans l'équipage de Chapeau de Paille, je me suis posé une question&nbsp;: à quelle fréquence les nouveau membres rejoignent-ils l'équipage ? Existe-t-il un modèle mathématique pour décrire cela ? Peut-on prédire quand devrait arriver le suivant ?<br/>
<br/>
<h3>One Piece</h3>
Pour ceux qui ne connaissent pas, One Piece est un manga sur les pirates. Le héro, Luffy, compte devenir le Seigneur des pirates, et part donc à l'aventure, recrutant des hommes (ou des femmes) dans son équipage au fil de ses pérégrinations [/mode:présentation à l'arrache].<br/>
<br/>
<div style="text-align:center;"><img src="media/ed66af57.One-Piece-Luffy.jpg" data-original-source="http://www.yosko.net/img/One-Piece-Luffy.jpg" /></div><br/>
<br/>
Au tout début, Luffy évoque l'idée de monter un équipage d'au moins 10 personnes ("un bon début", selon lui). Or à l'heure actuelle (tome 65), ils ne sont toujours que 9 dans l'équipage. Il est donc fort probable qu'un 10ème membre les rejoigne bientôt.<br/>
<br/>
A noter aussi&nbsp;: l'auteur a évoqué, aux alentours du tome 50, qu'il en était approximativement à la moitié de l'histoire. On peut donc supposer que One Piece durera environ 100 tomes (peut-être un peu surestimé).<br/>
<br/>
<h3>Les données</h3>
<span style="font-weight: bold;"><span style="color:red;">Attention&nbsp;: gros risque de spoil</span></span> pour les gens en retard sur la publication française. Je vais parler du contenu de One Piece jusqu'au tome 65 (chapitre 646) inclus. Si vous n'en êtes pas encore là (et que vous vous intéressez à l'histoire), ne lisez pas le reste de l'article.<br/>
<br/>
Toutes les données sont rassemblées dans un fichier Excel (<a href="http://www.yosko.net/files/One-Piece-Math.xls">One-Piece-Math.xls</a>)&nbsp;:<br/>
<ul>
  <li>Tableau listant les apparitions des membres d'équipage et les pages auxquelles ils rejoignent l'équipage</li>
  <li>Tableau décrivant la longueur de chaque tome (pages et chapitres)</li>
</ul>
<br/>
<h3>Statistiques</h3>
Quelques statistiques en vrac pour ceux que ça intéresse<br/>
<ul>
  <li>Le tome moyen de One Piece comporte 10 chapitre pour 197 pages de lecture (soit près de 20 pages par chapitre)</li>
  <li>Le tome le plus long de One Piece est le 63 pour 12 chapitres et 225 pages</li>
  <li>Les plus courts sont le 4 et le 13 pour 9 chapitres et 175 pages</li>
  <li>Le chapitre le plus long est le tout premier, avec 54 pages (rien de surprenant à cela)</li>
  <li>A la fin du tome 65, nous en sommes à 12 810 pages lues</li>
  <li>Le chapitre 502 ne semble pas exister dans ma version papier&nbsp;: dans le tome 51 il est indiqué comme le dernier chapitre du tome, mais n'apparait pas. Et le tome suivant reprend bien au chapitre 503. Etrange...</li>
</ul>
<br/>
<h3>Etude sur l'équipage</h3>
<br/>
Observons nos données&nbsp;:<br/>
<br/>
<div style="text-align:center;"><img src="media/c2b8fe14.One-Piece-Graph.png" data-original-source="http://www.yosko.net/img/One-Piece-Graph.png" /></div><br/>
<br/>
A gauche&nbsp;: montre la page à laquelle arrive chaque membre selon une échelle linéaire. Ainsi, le 7ème membre (7ème point sur la courbe) correspond à Robin, qui intègre l'équipage à la page 4386 (page 34 du tome 24).<br/>
<br/>
A droite&nbsp;: mêmes infos selon une échelle logarithmique. En effet, le début ressemble fortement à une droite. Mais on constate par la suite que les arrivées s'accélèrent (logarithmiquement parlant).<br/>
<br/>
Tentons à présent d'émettre plusieurs hypothèses visiblement cohérentes (au doigt mouillé, parce que je suis une grosse flemmasse) sur l'arrivée prochaine du 10ème membre d'équipage&nbsp;:<br/>
<ul>
  <li>Hypothèse 1&nbsp;: Page 13000, tome 66. Soit le plus tôt possible</li>
  <li>Hypothèse 2&nbsp;: Page 15000, tome 77. Soit dans le prochain arc ou le suivant</li>
  <li>Hypothèse 3&nbsp;: Page 20000, tome 102. Soit très probablement près de la fin de One Piece.</li>
</ul>
Je pensais aussi ajouter une hypothèse vers 12000 pages, mais ça n'avait pas sens&nbsp;: nous en sommes à 12810 pages, et le 10ème perso n'est toujours pas là.<br/>
<br/>
Représentons maintenant ces hypothèses sur notre graphique&nbsp;:<br/>
<br/>
<div style="text-align:center;"><img src="media/a1d314fb.One-Piece-Graph-Hypothese.png" data-original-source="http://www.yosko.net/img/One-Piece-Graph-Hypothese.png" /></div><br/>
<br/>
<h3>Conclusion en mousse</h3>
N'ayant pas accès à un bon outil d'interpolation, je me trouve forcé de m'arrêter là.<br/>
<br/>
Vu l'irrégularité de l'arrivée des derniers membres, il est difficile d'émettre une hypothèse précise sur le 10ème membres. Les 3 proposées semblent toutes cohérentes, bien que très différentes.<br/>
<br/>
Mathématiquement parlant, je serais tenté de dire que l'hypothèse 3 est la plus juste. Mais il faut bien reconnaître que scénaristiquement parlant, elle n'a pas vraiment de sens. En fait, c'est même l'hypothèse 1 qui sera la plus probable, si l'on considère qu'après l'arc en cours, nos héros rejoignent le nouveau monde, et que l'auteur aura probablement voulu que l'équipage soit complet avant cela.<br/>
<br/>
Keep pirating & read on<br/>
<br/>
<div style="text-align:center;"><img src="media/c8f5bbc5.One-Piece-Equipage-du-Chapeau-de-Paille.jpg" data-original-source="http://www.yosko.net/img/One-Piece-Equipage-du-Chapeau-de-Paille.jpg" /></div><br/>
<br/>
PS&nbsp;: j'ai réussi à me spoiler la réponse en faisant des recherches pour cette étude. C'est quand même con, une semaine avant de le découvrir pour de vrai... Je vous en dis plus dans 8 jours&nbsp;:-)<br/>
<br/>
PPS&nbsp;: pour ceux que ça intéresse, les graphiques ont été réalisés avec JPGraph. <a href="http://www.yosko.net/files/One-Piece-Graph.zip" />Voici le code PHP utilisé</a>.


feed_id: http://www.yosko.net/?d=2013/01/04/12/00/00-update-site-php-se-souvenir-de-moi
title: [Update] Site PHP : "se souvenir de moi" ?
uri: Update-Site-PHP-se-souvenir-de-moi
url: http://www.yosko.net/?d=2013/01/04/12/00/00-update-site-php-se-souvenir-de-moi
content:
<b>Refonte du système de connexion proposé en Novembre 2012.</b><br/><img class="right" src="media/c7803af5.icon-lock.png" data-original-source="http://www.yosko.net/img/icon-lock.png">
Force est de constater que le système de connexion long-terme <a href="http://www.yosko.net/index.php?d=2012/11/25/16/00/00-site-php-se-souvenir-de-moi">que j'ai proposé en Novembre dernier</a> comporte quelques petits défauts. Je vous propose donc une version remaniée et améliorée.<br/>
<br/>
En effet, il restait encore quelques failles, et l'utilisation d'adresses IP pour identifier l'utilisateur à long terme posait problème pour ceux qui en changent régulièrement (comme moi et ma connexion Free).<br/>
<br/>
<h3>Problèmes et solutions</h3>
Toutes les modifications apportée au code sont décrite ici. Je ne cite pas le code, difficile à présenter hors contexte.<br/>
<ol>
  <li>Réécriture du code, orienté objet.</li>
  <li>Hash des mots-de-passe&nbsp;: utilisation de bcrypt (PHP 5.3) avec <a href="https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/">Secure-random-bytes-in-PHP</a></li>
  <li>Abandon de la vérification d'IP pour la session LT (long-terme)</li>
  <li>Utilisation d'une authentification à 2 états&nbsp;: "non sécurisé" / "sécurisé" (l'utilisateur a rentré son mdp récemment et n'a pas changé d'IP). Cela s'utilise typiquement en redemandant son mot de passe à l'utilisateur pour des actions critiques comme changer d'email/mot-de-passe ou accéder à l'administration d'un site.</li>
  <li>Identifiant LT à usage unique&nbsp;: dès qu'il sert à regénérer la session CT (court-terme), il change de valeur.</li>
  <li>Multi-connexions&nbsp;:  pouvoir utiliser l'identifiant de session LT pour différentes sessions. Dans ce cas, l'identifiant unique serait (nom d'utilisateur+identifiant de session LT)</li>
  <li>Déconnexion volontaire&nbsp;: invalide toutes les sessions (LT et CT) de l'utilisateur, sur tous ses navigateurs/ordinateurs.</li>
</ol>
<br/>
<h3>Télécharger YosLogin</h3>
<br/>
<div style="text-align:center;"><h4><a href="https://github.com/yosko/yoslogin">Téléchargez directement YosLogin sur GitHub</a>.</h4></div><br/>
Par sécurité, je conserve aussi une <a href="http://dev.yosko.net/wiki/doku.php?id=web:php:yoslogin">page de téléchargement sur mon wiki</a>.<br/>
<br/>
<h3>Conclusion</h3>
Malgré tous mes efforts, je n'ai sans doute toujours pas atteint la perfection. Si vous remarquez le moindre défaut dans cette version, n'hésitez pas à m'en faire part sur la page GitHub du projet.<br/>
<br/>
Sources utilisées pour réaliser cette version&nbsp;:<br/>
<br/>
<ul>
  <li><span style="font-style: italic;">Secure random bytes in PHP</span>&nbsp;: <a href="https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/">github.com</a></li>
  <li><span style="font-style: italic;">You're probably storing passwords incorrectly</span>&nbsp;: <a href="http://www.codinghorror.com/blog/2007/09/youre-probably-storing-passwords-incorrectly.html">codinghorror.com</a></li>
  <li><span style="font-style: italic;">The definitive Guide to forms based website authentication</span>&nbsp;: <a href="http://stackoverflow.com/questions/549/the-definitive-guide-to-forms-based-website-authentication">stackoverflow.com</a></li>
  <li><span style="font-style: italic;">Persistant login cookie best practice</span>&nbsp;: <a href="http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/">fishbowl.pastiche.org</a></li>
</ul>
<br/>
Keep safe & log on.<br/>
<br/>
PS&nbsp;: je vais enfin pouvoir reprendre mes travaux sur DDb, dont je proposerai les nouvelles versions sur GitHub aussi&nbsp;:)


feed_id: http://www.yosko.net/?d=2013/03/13/15/00/00-codez-enfin-libre-et-open-source
title: CodeZ : enfin libre (et open-source)
uri: CodeZ-enfin-libre-(et-open-source)
url: http://www.yosko.net/?d=2013/03/13/15/00/00-codez-enfin-libre-et-open-source
content:
<b></b><br/><img src="media/d02fda3e.codez-logo.png" data-original-source="http://www.yosko.net/img/codez-logo.png" class="right" />
<br/>
Je vous avais parlé il y a quelques mois de <a href="http://www.yosko.net/?d=2012/08/23/10/26/00-codons-codez">CodeZ</a>, un projet de gestionnaire de snippet réalisé avec Qt pour le client, et avec une ébauche de site web pour centralisation et partage.<br/>
<br/>
Le projet restant un peu en suspend depuis quelques temps, nous avons décidé de libérer entièrement le code pour laisser la possibilité aux personnes intéressées de pouvoir en profiter, voir éventuellement de contribuer au projet.<br/>
<br/>
<div style="text-align:center;"><img src="media/03d5ef25.Codez-Screenshot-01.png" data-original-source="http://www.yosko.net/img/Codez-Screenshot-01.png" alt="Codez-Screenshot-01.png" width="236" height="209" style="max-width: 100%; height: auto;" /></div><br/>
<br/>
Vous pouvez donc retrouver tout ça <a href="https://github.com/LazyDreamTeam/CodeZ">sur GitHub</a>. Notez aussi la <a href="http://snippet.idleman.fr/">page dédiée au projet</a> chez idleman.<br/>
<br/>
PS&nbsp;: vous noterez le nom donné à notre équipe, tout à fait à notre image&nbsp;: LazyDreamTeam.


feed_id: http://www.yosko.net/?d=2013/02/22/15/00/00-yalig-mon-script-de-galerie-en-php
title: Yalig, mon script de galerie en PHP
uri: Yalig-mon-script-de-galerie-en-PHP
url: http://www.yosko.net/?d=2013/02/22/15/00/00-yalig-mon-script-de-galerie-en-php
content:
<b>Galerie d'images en PHP et en toute simplicité.</b><br/>Yalig (acronyme de "Yet Another Lightweight Image Gallery") est un petit script PHP permettant d'afficher une galerie d'image le plus simplement possible.<br/>
<br/>
Comme son nom l'indique, cela n'a rien d'original. Il existe déjà des dizaines de scripts réalisant cette fonction, même si beaucoup sont un peu lourd et encombrés de fonctionnalités inutiles. J'avais récemment utilisé <a href="http://sebsauvage.net/wiki/doku.php?id=minigal_nano_en">un patch de MiniGal Nano</a>, mais le manque de courage de mon plonger dans le code d'autrui associé à l'envie de m'essayer à l'exercice m'ont poussé à réaliser mon propre script.<br/>
<br/>
<div style="text-align:center;"><a class="fancybox" rel="gal" href="media/5598f18c.yalig-screen-01.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-01.jpg"><img src="media/d39a3379.yalig-screen-01-thb.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-01-thb.jpg" alt="yalig-screen-01.jpg" /></a> <a class="fancybox" rel="gal" href="media/c5308732.yalig-screen-02.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-02.jpg"><img src="media/cf9260c2.yalig-screen-02-thb.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-02-thb.jpg" alt="yalig-screen-02.jpg" /></a> <a class="fancybox" rel="gal" href="media/4f3381a5.yalig-screen-03.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-03.jpg"><img src="media/d0b04a85.yalig-screen-03-thb.jpg" data-original-source="http://www.yosko.net/img/yalig-screen-03-thb.jpg" alt="yalig-screen-03.jpg" /></a></div><br/>
<br/>
Fonctionnalités et points forts&nbsp;:<br/>
<ul>
  <li>Léger.</li>
  <li>Pas de base de données.</li>
  <li>Génération automatique de miniatures (via GD).</li>
  <li>Pas d'interface de gestion&nbsp;: placez simplement vos images dans le sous-dossier "img" (par défaut).</li>
  <li>Utilisation <span style="text-decoration: underline;">facultative</span> de javascript pour afficher les images (via <a href="http://fancybox.net/">FancyBox</a>). Peut être désactivé dans "config.php".</li>
</ul>
<br/>
Mon objectif a été de faire au plus court. Le moins de Code possible. Pas de gestion de template, mais le CSS est libre d'être modifié. Le dossier pour les miniatures sera créé automatiquement.<br/>
<br/>
<span style="font-weight: bold;"><div style="text-align:center;"><h4>Téléchargement et infos complémentaires&nbsp;: <a href="http://dev.yosko.net/wiki/doku.php?id=web:php:yalig">Page de Yalig</a><h4></div></span><br/>
<br/>
PS&nbsp;: je vais migrer mes pages de projets sur dokuwiki. Ainsi, ce sera plus simple à gérer, et il y en aura moins dans tous les sens...<br/>
<br/>
PPS&nbsp;: promis, je vais finir par arriver à bosser sur la DDb. Vous aurez bientôt une version. Mais y'a pas mal de boulot, et faut déjà que j'arrive à refondre mon système de login, que je vous posterai en premier.


feed_id: http://www.yosko.net/?d=2013/01/23/18/14/20-colo-syntaxique-du-blog
title: Colo syntaxique du blog
uri: Colo-syntaxique-du-blog
url: http://www.yosko.net/?d=2013/01/23/18/14/20-colo-syntaxique-du-blog
content:
<b>Quelques infos sur les thèmes de coloration syntaxique en général.</b><br/><pre class="brush:php">
function youpi() {
    $phrase = "La nouvelle coloration pète sa mère !";
    echo $phrase;
    return true;
}
</pre>

<p>Je viens de modifier la manière dont le code est coloré sur le blog. J'ai tout d'abord changé de script pour réaliser ça (je suis passé à <a href="http://alexgorbatchev.com/SyntaxHighlighter/manual/configuration/">SyntaxHighlighter</a>), mais j'ai surtout porté un thème vim/sublime dont je suis tombé amoureux&nbsp;: <a href="https://github.com/daylerees/colour-schemes/">Earthsong, par Dayle Rees</a> (tous les thèmes de cet auteur sont compatible vim, TextMate, Sublime et intellij).</p>

<p>Vous allez me dire&nbsp;: "<span style="font-style: italic;">Qu'est-ce que ça peut bien nous foutre ?</span>". Et outre le fait que vous soyez un grossier personnage, vous aurez raison.</p>
Si je vous en parle, c'est pour vous partager les quelques petits trucs que j'ai appris quand aux thèmes de coloration syntaxique&nbsp;:<br/>
<ol>
  <li>Si vous voulez convertir un thème prévu pour TextMate/Sublime (format "*.tmTheme"), pour l'utiliser dans Notepad++, passez par <a href="http://framework.lojcomm.com.br/tmTheme2nppStyler/">cet outil</a> (résultat correct, bien qu'il mériterait quelques retouches).</li>
  <li>De même, si vous voulez l'utiliser dans Ace Editor, utilisez simplement <a href="https://github.com/ajaxorg/ace/wiki/Importing-.tmtheme-and-.tmlanguage-Files-into-Ace">l'outil intégré à Ace</a>.</li>
</ol>

<p>Du coup, je m'éclate avec SyntaxHighlighter. On peut même surligner une ligne pour la mettre en valeur&nbsp;:</p>
<pre class="brush:php;highlight:[4];html-script:true;">
<script type="text/javascript">
	SyntaxHighlighter.all();
</script>
<pre class="brush:php;highlight:[1];"> <!-- cette ligne est surlignée -->
//ici bientôt :
//	une fonction pour répondre à la grande
//	question sur la vie, l'univers et le reste.
</pre>
</pre>
Enfin, à l'attention d'<a href="http://blog.idleman.fr">idleman</a>, qui utilise aussi ce script, mais avec quelques difficultés&nbsp;: oui, SyntaxHighlighter peut virer les < br > ajoutés par Wordpress (ou autre) à la fin des lignes de code. Il suffit d'activer <a href="http://alexgorbatchev.com/SyntaxHighlighter/manual/configuration/">l'option qui convient</a>&nbsp;:<br/>
<pre class="brush:php">
<script type="text/javascript">
	SyntaxHighlighter.config.stripBrs = true;
	SyntaxHighlighter.all();
</script>
</pre>

<p>Keep natural & rock on</p>


feed_id: http://www.yosko.net/?d=2013/01/07/12/47/53-snippet-02---php-hasher-un-mot-de-passe
title: [Snippet #02 - PHP] hasher un mot-de-passe
uri: Snippet-02-PHP-hasher-un-mot-de-passe
url: http://www.yosko.net/?d=2013/01/07/12/47/53-snippet-02---php-hasher-un-mot-de-passe
content:
<b>Différentes solutions pour hasher vos mots-de-passe de façon sécurisée.</b><br/>Le hashage est devenu une méthode incontournable pour éviter de refiler tous les mots-de-passe de nos utilisateurs au premier hacker venu (si vous ne savez pas, je vous conseille de vous documenter). Mais quelle méthode utiliser ? Lesquelles sont sécurisées ? Lesquelles sont disponibles sur votre système.<br/>
<br/>
De plus, même si vous mettez en place votre méthode de hashage, vous n'êtes probablement que de simples développeurs. Pas des experts en sécurité, ni en cryptographie. Il y a fort probablement un détail qui va vous échapper, une faille qui va rester...<br/>
<br/>
<h3>Solution 1&nbsp;: <span style="font-style: italic;">"pourvu qu'aucun hackeur ne s'en prenne à mon site"</span></h3>
Voilà ce qu'on faisait auparavant, et qu'il vaut mieux éviter aujourd'hui.<br/>
<br/>
<pre class="brush: php;">
$password = 'jmlaBiere69';<br/>
$hash = md5($password);<br/>
//DO: save hash to database<br/>
</pre>
<br/>
MD5 - au même titre que SHA1, mais aussi SHA2 (SHA256/SHA512) - est prévu pour être rapide à exécuter. Il est donc "aisé" pour une personne mal intentionnée entrée en possession de votre base de données <a href="http://php.net/manual/en/faq.passwords.php">d'attaquer par force brute ces hash pour retrouver les mots de passe</a>. Même si vous salez correctement les chaînes avant de les hasher.<br/>
<br/>
<h3>Solution 2&nbsp;: laisser faire les experts</h3>
Pourquoi coder soi-même ce que des gens plus compétents ont pu faire avant nous ? Je vous présente <a href="http://www.openwall.com/phpass/">PHPass</a>, une petite librairie simple, légère, optimisée et réputée (utilisée entre autre par Wordpress et phpBB). Elle gère automatiquement le choix des algorithmes et fonctions les plus sécurisées disponibles selon votre installation de PHP et votre système.<br/>
<br/>
Intégrez à votre projet le fichier <span style="font-style: italic;">PasswordHash.php</span> téléchargé sur le site de PHPass, puis utilisez-le comme suit&nbsp;:<br/>
<pre class="brush: php; highlight: [5];">
require_once 'PasswordHash.php';<br/>
$hasher = new PasswordHash(8, FALSE);<br/>
<br/>
$password = 'jmlaBiere69';<br/>
$hash = $hasher->HashPassword($password);<br/>
//DO: save hash to database<br/>
</pre>
<br/>
<span style="font-weight: bold;">Note 1&nbsp;:</span> le sel et le hash sont concaténés. Ainsi, vous n'avez qu'un seul élément à enregistrer dans votre base de données.<br/>
<span style="font-weight: bold;">Note 2&nbsp;:</span> si vous ne voulez pas vous casser la tête, laissez les paramètres du constructeur (8, FALSE) ainsi. Sinon, allez jeter un oeil dans la doc.<br/>
<br/>
Pour revérifier le mot de passe (quand l'utilisateur tente de se connecter, par exemple)&nbsp;:<br/>
<pre class="brush: php;">
if( $hasher->CheckPassword($password, $hash) ) {<br/>
  //DO: authenticate user<br/>
}<br/>
</pre>
<br/>
<h3>Solution 3&nbsp;: retrousser ses manches</h3>
Si on ne veut pas dépendre d'une librairie tierce, il va falloir plonger nos mains dans le cambouis et répondre aux questions suivantes&nbsp;:<br/>
<br/>
<ol>
  <li>Avez-vous accès au hashage par Blowfish ? Vérifiez simplement la condition <span style="font-style: italic;">(defined(CRYPT_BLOWFISH) && CRYPT_BLOWFISH == 1)</span>. A partir de PHP 5.3, c'est forcément le cas.</li>
  <li>Avez-vous accès à la fonction <span style="font-style: italic;">openssl_random_pseudo_bytes</span> pour générer des chaines aléatoire de manière plus sécurisée que <span style="font-style: italic;">rand</span> ou <span style="font-style: italic;">mt_rand</span> ? Elle fait partie du module <span style="font-style: italic;">openssl</span> de PHP. Vous pouvez vérifier la condition <span style="font-style: italic;">function_exists('openssl_random_pseudo_bytes')</span>.</li>
</ol>
<br/>
Partons du principe que vous avez bien PHP 5.3, mais que vous n'avez pas accès au module <span style="font-style: italic;">openssl</span> de PHP (configuration assez fréquente).<br/>
<br/>
Pour hasher le mot-de-passe, on va utiliser <span style="font-style: italic;">crypt()</span> (il est aussi possible de passer par <span style="font-style: italic;">hash()</span>, mais pas pour Blowfish). Cette fonction attend la chaine à hasher, ainsi que le sel que nous devons générer nous-même. Le plus tordu&nbsp;: la forme et la composition du sel fourni définira quel algorithme de hashage crypt va utiliser (ex&nbsp;: MD5, SHA1, Blowfish, ...).<br/>
<br/>
Inspiré de <a href="http://php.net/manual/en/function.crypt.php#110514">ce post</a> dans la doc de crypt()&nbsp;:<br/>
<pre class="brush: php;">
//$password: the clear password to hash<br/>
//$cost: number of iteration, from 4 to 31. The higher, the safer, the heavier<br/>
//return: the concatenated random salt + hashed password<br/>
function blowfishCrypt($password,$cost)<br/>
{<br/>
    //the 63 different authorized characters for the salt<br/>
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';<br/>
    <br/>
    //the salt prefix that will define which hashing algorithme crypt() will call (Blowfish)<br/>
    if (version_compare(PHP_VERSION, '5.3.7') < 0) {<br/>
        $salt=sprintf('$2a$%02d$',$cost);<br/>
    } else {<br/>
        $salt=sprintf('$2y$%02d$',$cost);<br/>
    }<br/>
    <br/>
    //the salt must be 22 characters long<br/>
    for($i=0;$i<22;$i++) $salt.=$chars[mt_rand(0,63)];<br/>
    <br/>
    return crypt($password,$salt);<br/>
}<br/>
</pre>
<br/>
<span style="font-weight: bold;">EDIT (2013-03-16)&nbsp;:</span> une petite librairie intéressante génère des chaines aléatoires pour vous de manière beaucoup plus sécurisée que le <a href="http://">mt_rand()</a> de l'exemple ci-dessus. Jetez un oeil à <a href="https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/">la librairie</a> ou directement à <a href="https://github.com/yosko/yoslogin">l'utilisation que j'en fait dans YosLogin</a>.<br/>
<br/>
<h3>Conclusion</h3>
Le choix est votre. La première solution est toujours valable lorsque vous agissez sur des données peu sensibles. La seconde conserve le meilleur des deux mondes&nbsp;: simplicité et sécurité. La troisième reste la plus modulable, mais n'oubliez pas que vous n'êtes pas forcément un expert, et que vous risquez de commettre des erreurs.<br/>
<br/>
Bibliographie (en vrac, beaucoup de redites, mais tous ces liens sont intéressants)&nbsp;:<br/>
<ul>
  <li><a href="http://stackoverflow.com/questions/4795385/how-do-you-use-bcrypt-for-hashing-passwords-in-php">Exemple plus complet avec bcrypt (stackoverflow.com)</a></li>
  <li><a href="http://stackoverflow.com/questions/11745082/php-bcrypt-hash-store-the-salt">Exemple d'utilisation avec openssl_random_pseudo_bytes (stackoverflow.com)</a></li>
  <li><a href="http://codahale.com/how-to-safely-store-a-password/">Pourquoi Blowfish plutôt que MD5, SHA1 ou SHA2 ?</a></li>
  <li><a href="http://stackoverflow.com/questions/5343611/portable-phpass-password-hashes-should-i-use-them/5343655#5343655">Petite explication sur PHPass et sur la forme des sels</a></li>
</ul>
<br/>
Keep hashing & salt on (ça ne veut absolument rien dire, mais je trouve que c’est assez dans le ton).


feed_id: http://www.yosko.net/?d=2012/12/27/17/24/21-snippet-01---php-color-conversion
title: [Snippet #01 - PHP] color conversion
uri: Snippet-01-PHP-color-conversion
url: http://www.yosko.net/?d=2012/12/27/17/24/21-snippet-01---php-color-conversion
content:
<b>La conversion RGB/HSL en PHP ? Rien de plus simple</b><br/>
<p>En partant des algorithmes fournis <a href="http://www.easyrgb.com/index.php?X=MATH">sur ce site</a>, j'ai écris en PHP les fonctions de conversion RGB/HSL.</p>

<p>On en trouve des variantes un peu partout sur le web, mais certaines sont fausse, ou parlent à tort de HSL quand elles traitent du HSV... Bref, autant repartir sur de bonnes bases.</p>

<p>En plus des fonctions de conversion RGB/HSL, je vous propose quelques petites fonctions utilitaires, le tout à télécharger au bas de l'article.</p>
<h3>Nettoyer un code couleur HTML</h3>
S'assurer qu'il est au bon format et se débarasser du "#" pour pouvoir le traiter sans souci.<br/>
<pre class="brush:php;">
//returns a clean 6 digit hex number as a string
function cleanHexColor($hex) {
    $hex = strtolower($hex);
    //remove the leading "#"
    if(strlen($hex) == 7 || strlen($hex) == 4)
        $hex = substr($hex, -(strlen($hex)-1));

    // $hex like "1a7"
    if(preg_match('/^[a-f0-9]{6}$/i', $hex))
        return $hex;
    // $hex like "162a7b"
    elseif(preg_match('/^[a-f0-9]{3}$/i', $hex))
        return $hex[0].$hex[0].$hex[1].$hex[1].$hex[2].$hex[2];
    //any other format
    else
        return "000000";
}
</pre>
<h3>Conversion HTML > RGB</h3>
Convertir un code de couleur hexadécimal (sans #) en RGB (3 valeurs de [0-255]).<br/>
<pre class="brush:php;">
//returns an array of 3 [0-255] int
function hex2rgb($hex) {
    return array(
        hexdec(substr($hex, 0, 2)),
        hexdec(substr($hex, 2, 2)),
        hexdec(substr($hex, 4, 2)),
    );
}
</pre>
<h3>Conversion RGB > HTML</h3>
Convertir les valeurs RGB (3 valeurs de [0-255]) d'une couleur en un code HTML (sans #)<br/>
<pre class="brush:php;">
//returns a 6 digit hex number as a string
function rgb2hex($rgb) {
    return ""
        .substr("00".dechex(ceil($rgb[0])), -2)
        .substr("00".dechex(ceil($rgb[1])), -2)
        .substr("00".dechex(ceil($rgb[2])), -2)
    ;
}
</pre>
<h3>Faire varier une couleur</h3>
Un petit snippet pour fournir  des variations d'un couleur fournie en paramètre. Il suffit de définir quelle dimension (HSL&nbsp;: teinte, saturation ou luminosité) on souhaite garder constante, et la fonction retournera un tableau avec les variantes. A combiner avec les fonctions ci-dessus + <span style="font-style: italic;">rgb2hsl</span> et <span style="font-style: italic;">hsl2rgb</span> à télécharger plus bas.<br/>
<pre class="brush:php;">
//returns a 2 dimensional array of HSL values (as subarray of 3 values [0-1] floats)
function variationAround($hsl, $stepSize = 0.2,  $constant = "hue") {
    if($constant == "hue") {
        $i = 1; $j = 2;
    } elseif($constant == "sat") {
        $i = 0; $j = 2;
    }if($constant == "lum") {
        $i = 0; $j = 1;
    }
    
    $variations = array();  //resulting variations
    $hslTemp = array();     //stores current variation
    $mod = array();         //stores modulos for h, s & l
    
    $mod[0] = (float)(($hsl[0]*100) % ($stepSize * 100)) / 100;
    $mod[1] = (float)(($hsl[1]*100) % ($stepSize * 100)) / 100;
    $mod[2] = (float)(($hsl[2]*100) % ($stepSize * 100)) / 100;
    
    $hslTemp = $hsl;
    $hslTemp[$i] = $mod[$i];
    while($hslTemp[$i] <= 1) {
        $row = array();
        $hslTemp[$j] = $mod[$j];
        while($hslTemp[$j] <= 1) {
            $row[(string)$hslTemp[$j]] = array($hslTemp[0], $hslTemp[1], $hslTemp[2]);
            $hslTemp[$j] = (float)($hslTemp[$j] + $stepSize);
        }
        $variations[(string)$hslTemp[$i]] = $row;
        $hslTemp[$i] = (float)($hslTemp[$i] + $stepSize);
    }
    
    return $variations;
}
</pre>
Permet par exemple d'obtenir, pour la couleur <span style="background-color:#22bb77">#22bb77</span> et à teinte constante&nbsp;:<br/>
<center><table><tr><th>sat \ lum</th><th>0.03</th><th>0.23</th><th>0.43</th><th>0.63</th><th>0.83</th></tr>
	<tr><th>0.09</th><td style="background:#070908;">#070908</td><td style="background:#36403c;">#36403c</td><td style="background:#64786f;">#64786f</td><td style="background:#99aaa2;">#99aaa2</td><td style="background:#d0d8d5;">#d0d8d5</td></tr>
	<tr><th>0.29</th><td style="background:#060a08;">#060a08</td><td style="background:#2a4c3d;">#2a4c3d</td><td style="background:#4e8e72;">#4e8e72</td><td style="background:#86bda4;">#86bda4</td><td style="background:#c8e1d6;">#c8e1d6</td></tr>
	<tr><th>0.49</th><td style="background:#040c09;">#040c09</td><td style="background:#1e583e;">#1e583e</td><td style="background:#38a474;">#38a474</td><td style="background:#73cfa6;">#73cfa6</td><td style="background:#bfe9d7;">#bfe9d7</td></tr>
	<tr><th>0.69</th><td style="background:#030d09;">#030d09</td><td style="background:#136440;">#136440</td><td style="background:#22bb77;color:#fff"><b>#22bb77</b></td><td style="background:#60e2a8;">#60e2a8</td><td style="background:#b6f2d7;">#b6f2d7</td></tr>
	<tr><th>0.89</th><td style="background:#010f09;">#010f09</td><td style="background:#076f41;">#076f41</td><td style="background:#0dd079;">#0dd079</td><td style="background:#4df5aa;">#4df5aa</td><td style="background:#aefbd8;">#aefbd8</td></tr>    <br/>
</table></center>
<h3><center><a href="http://www.yosko.net/files/color-lib.zip">Télécharger les fonctions et un exemple d'utilisation</a></center></h3>
Contient&nbsp;:<br/>
<ul>
  <li>color.lib.php&nbsp;: toutes les fonctions évoquées, dont <span style="font-style: italic;">rgb2hsl</span> et <span style="font-style: italic;">hsl2rgb</span>.</li>
  <li>exemple.php&nbsp;: une implémentation utilisant ces fonctions pour afficher les variations autour d'une couleur.</li>
</ul>

<p>Keep calm & taste the rainbow.</p>


feed_id: http://www.yosko.net/?d=2012/12/21/18/14/42-noter-ses-mp3-de-facon-perenne
title: Noter ses MP3 de façon pérenne
uri: Noter-ses-MP3-de-façon-pérenne
url: http://www.yosko.net/?d=2012/12/21/18/14/42-noter-ses-mp3-de-facon-perenne
content:
<b>La plupart des logiciels de musique permettent de noter ses morceaux sur 5 étoiles, mais leurs méthodes sont souvent incompatibles...</b><br/><img src="media/e7557d83.stars.png" data-original-source="http://www.yosko.net/img/stars.png" class="right" width="87" height="97" style="max-width: 100%; height: auto;" />

<p>En effet, deux principales méthodes existent&nbsp;:</p>
<ol>
  <li>Conserver la note <span style="font-weight: bold;"><span style="text-decoration: underline;">dans la base de données de votre bibliothèque</span></span>, qui est spécifique au logiciel, et même spécifique à cette installation. Si parmi les logiciels utilisant cette méthode certains permettent d'exporter ces informations pour en effectuer des sauvegardes (export XML de Winamp, par exemple), c'est plutôt rare et ça nécessite une intervention de votre part.</li>
  <li>Conserver la note <span style="font-weight: bold;"><span style="text-decoration: underline;">directement dans le fichier</span></span>&nbsp;: certes, si vous refilez vos MP3 à vos amis, ils se retrouveront avec vos notes. Mais si votre ordi décède de sa belle mort bleue (comprenne qui pourra), ça vous évite d'avoir à renoter vos 5000 morceaux. Certains logiciels de la première catégorie seront même capables d'afficher les notes stockées dans les fichiers, mais pas de les modifier.</li>
</ol>

<p>Un petit comparatif des principales solutions sous Windows&nbsp;:</p>

<p><div style="text-align:center;"><img src="media/b69f2496.music-managers-rating-comparison.png" data-original-source="http://www.yosko.net/img/music-managers-rating-comparison.png" alt="music-managers-rating-comparison.png" width="470" height="499" style="max-width: 100%; height: auto;" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">Un logiciel ou explorateur manque à la liste ? Donnez-moi vos retours dessus ou demandez-moi de l'essayer</span></div></p>

<p>Même s'il n'y a pas de solution universelle, écrire la note dans le fichier semble vraiment être la méthode la plus pérenne et la plus polyvalente. Certains logiciels ne la proposent pas de base, mais peuvent l'implémenter après une petite manip&nbsp;: c'est le cas de <a href="http://wiki.songbirdnest.com/User:Lukas_Skywalker/F.A.Q./How_to_enable_that_ratings_are_written_to_the_file">songbird</a> et de <a href="http://www.hydrogenaudio.org/forums/index.php?showtopic=71224">foobar2000</a> via leurs tutoriels respectifs.</p>
Enfin quelques remarques sur certains de ces logiciels&nbsp;:<br/>
<ul>
  <li>foobar2000 est complètement customisable, et permet donc de faire tout ce que l'on souhaite. Je le recommande malgré tout aux plus geeks, car il n'est pas franchement des plus accessibles.</li>
  <li>iTunes... Vous vous rendez compte que je viens de VIOLER mon PC rien que pour vous ? En plus, l'installeur fait 85 Mo (soit presque 6 fois plus que le plus gros installeur de tous les autres&nbsp;!!!).</li>
  <li>Je ne connais pas bien MediaMonkey, mais il m'a très agréablement surpris. Pour les demi-notes, comme par exemple 4,5, il stocke 4,5 dans sa base privée, et arrondi au supérieur en stockant 5 dans le fichier. Bonne initiative de compatibilité.</li>
  <li>Songbird et Windows Media Player veulent bien écrire la note dans le fichier, mais refusent d'afficher une note présente dans un fichier si elle n'a pas été faite via ce logiciel. Un peu dommage.</li>
  <li>A noter que l'explorateur affichera la note à tous les coups si elle est absente de la bibliothèque de WMP. Mais si elle est dans WMP, il faudra nécessairement que la note ait été attribuée via celui-ci pour que l'explorateur l'affiche. Un peu dommage aussi...</li>
  <li>Je constate que seuls Songbird et  VLC sont open-source (même si je me demande bien pourquoi j'ai ajouté ce dernier à la liste puisque j'ai été incapable de l'utiliser pour noter un morceaux...).</li>
</ul>

<p>En résumé, je conseillerais plutôt l'un des deux lecteurs suivants&nbsp;: foobar2000 ou MediaMonkey. Associé à l'explorateur de Windows 7, par exemple, ça devient très sympa.</p>

<p>Listen to Music & Rock On.</p>

<p><span style="font-weight: bold;">Edit&nbsp;:</span> j'allais oublier de préciser&nbsp;! Lorsque la note est stockée dans le fichier, c'est en fait directement dans un tag MP3 "non officiel", et tous les logiciels sus-cités utilisent le même nommé "RATING".</p>


feed_id: http://www.yosko.net/?d=2012/12/17/15/00/00-ghostery-saved-my-internet
title: Ghostery saved my internet
uri: Ghostery-saved-my-internet
url: http://www.yosko.net/?d=2012/12/17/15/00/00-ghostery-saved-my-internet
content:
<b>Dans notre navigation, le bloquage de popup, de pubs et de cookies traqueurs est devenu monnaie courante.</b><br/>Ghostery est juste l'un des outils de cette nouvelle ère. Comme je l'avais découvert grâce à <a href="http://www.sebsauvage.net/links/?qztXSA">sebsauvage</a>, et comme ils en reparlaient récemment sur <a href="http://blog.m0le.net/2012/11/28/firefox-mon-navigateur/">m0le'o'blog</a>, il est très sympa, et se montre assez efficace non seulement contre les pubs, mais aussi contre tous les autres trucs qui traquent votre navigation (boutons sociaux, outils de statistique/analytique, etc...).<br/>
<br/>
Mais surtout, ce que je trouve sympa, c'est la visibilité qu'il donne sur ce qu'il bloque ou laisse passer. Par défaut, il affiche pendant quelques secondes tous les traqueurs détectés à chaque chargement de page. Et on a parfois des surprises désagréables (comme <a href="http://www.sebsauvage.net/links/?n_d8CA">ici</a> ou <a href="http://www.sebsauvage.net/links/?-0Ap6A">là</a> chez sebsauvages).<br/>
<br/>
Eh bien j'ai aujourd'hui trouvé un record. Sur le site <a href="http://www.lesnumeriques.com/">lesnumeriques.com</a>, que je respecte et que j'apprécie, on trouve de base une dizaine de traqueurs (même s'il y en a plusieurs des mêmes sociétés, comme Adsense, Analytics et +1 pour Google...). Mais en voulant absolument regarder <a href="http://www.lesnumeriques.com/jeux-video/tomb-raider-malmene-lara-croft-a-occasion-vga-2012-n27334.html">cette vidéo du prochain Tomb Raider</a>, je met Ghostery en pause le temps de la charger. Et là, quelle n'est pas ma surprise&nbsp;:<br/>
<br/>
<div style="text-align:center;"><a class="fancybox" href="media/cdd832a3.ghostery-lesnumeriques-spike.png" data-original-source="http://www.yosko.net/img/ghostery-lesnumeriques-spike.png"><img src="media/3394d6bc.ghostery-lesnumeriques-spike-thb.jpg" data-original-source="http://www.yosko.net/img/ghostery-lesnumeriques-spike-thb.jpg" alt="ghostery-lesnumeriques-spike.png" /></a></div><br/>
<br/>
Oui, vous avez bien lu&nbsp;: 60 traqueurs&nbsp;!!!<br/>
Non mais WTF&nbsp;! Cela signifie que le lecteur flash intégré et issu de spike.com a ajouté 50 traqueurs à la page à lui seul&nbsp;!<br/>
<br/>
Note&nbsp;: après rechargement de la page, je n'ai pas toujours le même nombre de traqueurs (et souvent moins que 60). J'imagine que la régie publicitaire associée à spike.com doit faire appel aléatoirement à différents traqueurs de différentes sociétés.<br/>
<br/>
Et si on lançais une quête pour trouver la page qui intégrait le plus de traqueurs détectés par Ghostery ? Il doit bien y avoir pire que ça, non ? Pas que je le souhaite, plutôt que je le redoute...


feed_id: http://www.yosko.net/?d=2012/11/25/16/00/00-site-php-se-souvenir-de-moi
title: Site PHP : "se souvenir de moi" ?
uri: Site-PHP-se-souvenir-de-moi
url: http://www.yosko.net/?d=2012/11/25/16/00/00-site-php-se-souvenir-de-moi
content:
<b>Pour identifier un utilisateur en PHP, deux principales techniques s'affrontent : les sessions et les cookies</b><br/><img class="right" src="media/c7803af5.icon-lock.png" data-original-source="http://www.yosko.net/img/icon-lock.png">
Et si on pouvait profiter des deux réunies, avec des fonctionnalités supplémentaires ? C'est ce que j'ai voulu mettre en place suite à mes discussion avec idleman et sebsauvage.<br/>
<br/>
<span style="font-weight: bold;"><span style="color:red;">EDIT 2013-03-16&nbsp;:</span></span> une version plus aboutie de ce projet est présentée <a href="http://www.yosko.net/?d=2013/01/04/12/00/00-update-site-php-se-souvenir-de-moi">dans cet article</a>, et est disponible <a href="https://github.com/yosko/yoslogin">sur GitHub</a>.<br/>
<br/>
<h3>Objectifs&nbsp;: que veut-on ?</h3>
<br/>
<ol>
  <li>Distinguer les utilisateurs connectés/déconnectés</li>
  <li>Connexions multi-utilisateurs</li>
  <li>Connexions depuis différentes machines / différents navigateurs pour un même utilisateur.</li>
  <li>Que l'utilisateur puisse conserver une connexion "longue" s'il le souhaite (case à cocher "se souvenir de moi"). Par exemple 1 mois.</li>
  <li>Conserver une relative sécurité en évitant de stocker des données sensibles côté client, et en vérifiant l'IP de l'utilisateur</li>
</ol>
<br/>
Presque tout cela est déjà possible avec les sessions PHP (<span style="font-weight: bold;">$_SESSION</span>). La seule limitation&nbsp;: la session est conservée en mémoire, et sa durée dépend de la configuration PHP et de l'état du serveur.<br/>
<br/>
<h3>Solution 1&nbsp;: augmenter la durée de session</h3>
<br/>
Votre serveur comporte un paramètre PHP nommé <span style="font-weight: bold;"><a href="http://www.php.net/manual/en/session.configuration.php#ini.session.gc-maxlifetime">gc_maxlifetime</a></span>, (valeur par défaut&nbsp;: 1440). Cela indique le nombre de secondes (1440s = 24 minutes) au bout desquelles la session peut potentiellement expirer. On peut le modifier directement depuis un script&nbsp;:<br/>
<br/>
<pre class="brush:php;">ini_set('session.gc_maxlifetime', 3600);    //durée augmentée à 1 heure</pre>
<br/>
Problèmes&nbsp;:<br/>
<ol>
  <li>La méthode ini_set n'est pas disponible chez tous les hébergeurs</li>
  <li>Il n'est pas forcément judicieux d'étendre trop cette valeur. De ce que j'en ai lu sur le web (<a href="http://stackoverflow.com/questions/9708137/setting-gc-maxlifetime-to-24-hours#comment12339826_9708137">ici</a>), cela augmente d'autant la quantité de données stockées dans memcached, et peut présenter une charge non négligeable</li>
  <li>Un serveur qui redémarre, un gros site qui nécessite plusieurs serveurs, et il devient difficile de gérer correctement la session.</li>
</ol>
<br/>
<h3>Solution 2&nbsp;: cookies & stockage serveur</h3>
<br/>
<span style="font-style: italic;">Note&nbsp;: vous pouvez télécharger une implémentation de cette solution en fin d'article.</span><br/>
<br/>
On va continuer à utiliser la session PHP "classique", tout en ajoutant par dessus une couche permettant de la relancer lorsqu'elle se termine trop tôt. Ce système de session "maison", basé sur les cookie pour le côté client et sur une sauvegarde sous forme de fichiers texte ou de base de données pour le serveur. Ça donne un peu l'impression de réinventer la roue, mais on est obligé d'y passer.<br/>
<br/>
Résumé du principe&nbsp;:<br/>
<ul>
  <li>Connexion sans cocher "se souvenir de moi"&nbsp;: dès expiration de la session PHP, utilisateur déconnecté</li>
  <li>Connexion en cochant la case&nbsp;: dès expiration de la session PHP&nbsp;:<br/>
    <ul>
      <li>Si session long terme toujours valide, recréation d'une session PHP</li>
      <li>Si pas de session long terme ou session expirée, utilisateur déconnecté</li>
    </ul>
  </li>
</ul>
<br/>
<h3>Mise en oeuvre</h3>
<br/>
Paramètres de notre système&nbsp;:<br/>
<pre class="brush:php;">
$config = array();<br/>
$config['LTDir'] = 'cache/';      //dossier où seront stockés les fichiers de session<br/>
$config['nbLTSession'] = 200;     //nombre max de sessions long-terme simultanées<br/>
$config['LTDuration'] = 2592000;  //durée d'une session long-terme (2592000 = 1 mois)<br/>
</pre>
<br/>
A partir de là, on va, au début de chacune de nos pages, vérifier l'authentification de l'utilisateur en appelant la fonction suivante (retourne true/false)&nbsp;:<br/>
<br/>
<pre class="brush:php;">$isLoggedIn = logUser();</pre>
<br/>
Si l'utilisateur n'est pas connecté, on affichera alors l'écran de connexion, sinon on lui affichera un contenu privé et personnalisé.<br/>
<br/>
La fonction en elle-même&nbsp;:<br/>
<br/>
<pre class="brush:php;">
function logUser() {<br/>
    global $config;<br/>
    <br/>
    //démarrer la session PHP<br/>
    session_start();<br/>
    <br/>
    //déconnexion en cours ou IP incorrecte<br/>
    if(isset($_GET['logout'])<br/>
            || isset($_SESSION['ip']) && $_SESSION['ip']!=getIpAddress()) {<br/>
        //suppression de la session "long-terme"<br/>
        unsetLTSession($_SESSION['uid']);<br/>
        setcookie('yosloginlt', null, time()-31536000,<br/>
            dirname($_SERVER['SCRIPT_NAME']).'/',<br/>
            '', false, true);<br/>
        <br/>
        //suppression de la session PHP<br/>
        unset($_SESSION['uid']);<br/>
        unset($_SESSION['ip']);<br/>
        unset($_SESSION['login']);<br/>
        unset($_SESSION['userRelatedInformation']);<br/>
        <br/>
        session_set_cookie_params(time()-31536000, dirname($_SERVER['SCRIPT_NAME']).'/');<br/>
        session_destroy();<br/>
        <br/>
        header("Location: index.php");<br/>
        <br/>
    //si la session PHP est expirée mais que le cookie de session long-terme existe<br/>
    } elseif(!isset($_SESSION['uid']) && isset($_COOKIE['yosloginlt'])) {<br/>
        //récupération des données de session long-terme<br/>
        $LTSession = getLTSession($_COOKIE['yosloginlt']);<br/>
        if($LTSession&nbsp;!== false) {<br/>
            //rétablissement de la session PHP<br/>
            $_SESSION['uid']=$_COOKIE['yosloginlt'];<br/>
            $_SESSION['ip']=$LTSession['ip'];<br/>
            $_SESSION['login']=$LTSession['login'];<br/>
        } else {<br/>
            //si le cookie ne correspond à aucune session, on le supprime<br/>
            setcookie('yosloginlt', null, time()-31536000,<br/>
                dirname($_SERVER['SCRIPT_NAME']).'/',<br/>
                '', false, true);<br/>
        }<br/>
        <br/>
    //si l'utilisateur est en train de se connecter<br/>
    } elseif (isset($_POST['submitLogin'])<br/>
            && isset($_POST['login']) && trim($_POST['login'])&nbsp;!= ""<br/>
            && isset($_POST['password']) && trim($_POST['password'])&nbsp;!= "") {<br/>
        //récupération des données utilisateur<br/>
        $user = getUser($_POST['login']);<br/>
        //vérification du mot de passe<br/>
        if(!empty($user) && sha1($_POST['password']) == $user['password']) {<br/>
            //établissement de la session PHP<br/>
            $_SESSION['uid']=sha1(uniqid('',true).'_'.mt_rand());<br/>
            $_SESSION['ip']=getIpAddress();<br/>
            $_SESSION['login']=$user['login'];<br/>
            <br/>
            //si l'utilisateur a coché la case "se souvenir de moi"<br/>
            if(isset($_POST['remember']) && $_POST['remember'] == "remember") {<br/>
                //enregistrer la session long-terme<br/>
                $LTSession = array();<br/>
                $LTSession['login'] = $_SESSION['login'];<br/>
                $LTSession['ip'] = $_SESSION['ip'];<br/>
                setLTSession($_SESSION['uid'], $LTSession);<br/>
                <br/>
                //nettoyage des vieilles sessions<br/>
                flushOldLTSessions();<br/>
            }<br/>
            <br/>
            header("Location: $_SERVER[REQUEST_URI]");<br/>
        }<br/>
    }<br/>
    <br/>
    //si l'utilisateur est connecté<br/>
    if (!empty($_SESSION['uid'])) {<br/>
        //mise à jour de la date d'expiration du cookie long-terme<br/>
        if(isset($_COOKIE['yosloginlt'])<br/>
                || isset($_POST['remember']) && $_POST['remember'] == "remember") {<br/>
            setcookie('yosloginlt', $_SESSION['uid'], time()+$config['LTDuration'],<br/>
                dirname($_SERVER['SCRIPT_NAME']).'/',<br/>
                '', false, true);<br/>
        }<br/>
        return true;<br/>
    } else {<br/>
        return false;<br/>
    }<br/>
}<br/>
</pre>
<br/>
Récupération de l'utilisateur (à adapter selon votre architecture technique).<br/>
<pre class="brush:php;">
function getUser($login) {<br/>
    return(array("login" => "yosko", "password" => sha1("yosko"));<br/>
}<br/>
</pre>
<br/>
Récupérer l'adresse IP de l'utilisateur, même pour un serveur utilisant un proxy (basé sur <a href="http://stackoverflow.com/questions/1634782/what-is-the-most-accurate-way-to-retrieve-a-users-correct-ip-address-in-php">cette discussion</a>)&nbsp;:<br/>
<pre class="brush:php;">
function getIpAddress(){<br/>
    foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key){<br/>
        if (array_key_exists($key, $_SERVER) === true){<br/>
            foreach (explode(',', $_SERVER[$key]) as $ip){<br/>
                $ip = trim($ip); // just to be safe<br/>
                if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)&nbsp;!== false){<br/>
                    return $ip;<br/>
                }<br/>
            }<br/>
        }<br/>
    }<br/>
}<br/>
</pre>
<br/>
Et les fonctions de stockage de session long-terme (à adapter selon si vous utilisez des fichiers ou une base de données)&nbsp;:<br/>
<br/>
<pre class="brush:php;">
function setLTSession($sid, $value) {<br/>
    global $config;<br/>
    <br/>
    $fp = fopen($config['LTDir'].$sid, 'w');<br/>
    fwrite($fp, gzdeflate(json_encode($value)));<br/>
    fclose($fp);<br/>
}<br/>
</pre>
<br/>
<pre class="brush:php;">
function getLTSession($sid) {<br/>
    global $config;<br/>
    <br/>
    $dir = $config['LTDir'];<br/>
    <br/>
    $value = false;<br/>
    if (file_exists($dir.$sid)) {<br/>
        <br/>
        //expiration de la session long-terme<br/>
        if(filemtime($dir.$sid)+$config['LTDuration'] <= time()) {<br/>
            unsetLTSession($sid);<br/>
            $value = false;<br/>
        } else {<br/>
            $value = json_decode(gzinflate(file_get_contents($dir.$sid)), true);<br/>
            //mise-à-jour de la date de modification<br/>
            touch($dir.$sid);<br/>
        }<br/>
    }<br/>
    return($value);<br/>
}<br/>
</pre>
<br/>
<pre class="brush:php;">
function unsetLTSession($sid) {<br/>
    global $config;<br/>
    <br/>
    if (file_exists($config['LTDir'].$sid)) {<br/>
        unlink($config['LTDir'].$sid);<br/>
    }<br/>
}<br/>
</pre>
<br/>
<pre class="brush:php;">
function flushOldLTSessions() {<br/>
    global $config;<br/>
    <br/>
    $dir = $config['LTDir'];<br/>
    <br/>
    //liste des fichiers de session<br/>
    $files = array();<br/>
    if ($dh = opendir($dir)) {<br/>
        while ($file = readdir($dh)) {<br/>
            if(!is_dir($dir.$file)) {<br/>
                if ($file&nbsp;!= "." && $file&nbsp;!= "..") {<br/>
                    $files[$file] = filemtime($dir.$file);<br/>
                }<br/>
            }<br/>
        }<br/>
        closedir($dh);<br/>
    }<br/>
    <br/>
    //tri par date (plus récents en premier)<br/>
    arsort($files);<br/>
    <br/>
    //vérification de chaque fichier<br/>
    $i = 1;<br/>
    foreach($files as $file => $date) {<br/>
        if ($i > $config['nbLTSession'] || $date+$config['LTDuration'] <= time()) {<br/>
            unsetLTSession($file);<br/>
        }<br/>
        ++$i;<br/>
    } <br/>
}<br/>
</pre>
<br/>
<h3>Conclusion</h3>
<br/>
Et voilà \o/ (qu'est-ce que vous espériez, comme conclusion ?&nbsp;:-P)<br/>
<br/>
Plus sérieusement, je suis sûr que ce script est encore imparfait. Mais il ne demande qu'à être amélioré, alors n'hésitez surtout pas à me faire part de vos idées. Entre autres, je ne suis pas expert en sécurité, et je ne serais donc pas surpris qu'il y a quelques failles dans ce système...<br/>
<br/>
Télécharger une implémentation fonctionnelle&nbsp;: <a href="http://www.yosko.net/misc/yoslogin/yoslogin-v1.zip">yoslogin-v1.zip</a> (5ko)<br/>
<br/>
Sources&nbsp;:<br/>
<ul>
  <li>Certaines lignes de code sont inspirées de l'authentification de <a href="http://sebsauvage.net/wiki/doku.php?id=php:shaarli">Shaarli</a> (qui ne gère que la session PHP)</li>
  <li><a href="http://stackoverflow.com/questions/10626061/extending-the-session-in-php-with-gc-maxlifetime-not-working">Discussion sur le gc_maxlifetime</a></li>
  <li>La doc PHP (bourrée d'infos et d'exmples simples)</li>
</ul>
<br/>
Améliorations possible&nbsp;:<br/>
<ul>
  <li><a href="http://www.siteduzero.com/tutoriel-3-34510-eviter-les-attaques-par-force-brute.html">Protection contre les brute-force</a></li>
</ul>


feed_id: http://www.yosko.net/?d=2012/11/13/21/00/00-ddb-travaillez-votre-memoire-onirique
title: DDb : travaillez votre mémoire onirique
uri: DDb-travaillez-votre-mémoire-onirique
url: http://www.yosko.net/?d=2012/11/13/21/00/00-ddb-travaillez-votre-memoire-onirique
content:
<b>Dans le but de noter mes rêves et ceux de mes amis, j'ai réalisé un petit CMS pour les noter. Je vous présente aujourd'hui le résultat de mon travail : DDb.</b><br/>
<p>DDb (la Dream Database, aka base de données de rêves) est un petit outil en ligne permettant de noter ses rêves et ceux de ses amis, et de les consulter.</p>

<p>En effet, le meilleur moyen de se souvenir de ses rêves c'est, paraît-il, de prendre l'habitude de les noter. Mais cela peut avoir d'autres utilités, comme aider à faire des rêves lucides (où l'on est conscient d'être dans un rêve, ce qui offre l'opportunité de modifier le cours du rêve).</p>

<p><div style="text-align:center;"><img src="media/eecf3e8d.ddb-dream.jpg" data-original-source="http://www.yosko.net/img/ddb-dream.jpg" alt="ddb-dream.jpg" width="494" height="314" style="max-width: 100%; height: auto;" /></div></p>

<p>Grâce à la DDb, travaillez sur vos rêves et partagez-les entre amis. Vous pouvez ainsi les ajouter, les modifier, les supprimer, les rechercher sur divers critères. Je serais presque tenté d'ajouter que "la seule limite est votre imagination", mais ça serait sans doute un peu prétentieux.</p>

<p><div style="text-align:center;"><img src="media/7c4f7a38.ddb-add-or-edit.jpg" data-original-source="http://www.yosko.net/img/ddb-add-or-edit.jpg" alt="ddb-add-or-edit.jpg" width="494" height="791" style="max-width: 100%; height: auto;" /></div></p>

<p><span style="font-weight: bold;">EDIT&nbsp;:</span> DDb est réalisé en HTML5 / PHP / PDO-SQLite (merci H3 pour la remarque&nbsp;:-P)<br/>
<span style="font-weight: bold;">EDIT bis&nbsp;:</span> DDb comporte désormais un flux RSS et une option de connexion "se souvenir de moi"</p>

<p>J'ai essayé de faire en sorte que l'installation soit la plus simple possible (vu qu'il s'agit d'un petit script, ça aurait été dommage de compliquer les choses). Pour plus d'info, rendez-vous sur la page dédiée au projet&nbsp;:</p>

<p><div style="text-align:center;"><a href="http://www.yosko.net/ddb.php"><img src="media/34e0ae93.ddb-logo.png" data-original-source="http://www.yosko.net/img/ddb-logo.png" alt="ddb-logo.png" width="196" height="64" style="max-width: 100%; height: auto;" /></a></div></p>

<p><div style="text-align:center;"><span style="font-weight: bold;"><a href="http://www.yosko.net/ddb.php">>> Aller sur la page de DDb <<</a></span></div></p>

<p>Et surtout, si vous rencontrez le moindre problème ou que vous avez des idées d'améliorations, n'hésitez pas à m'en faire part&nbsp;!</p>


feed_id: http://www.yosko.net/?d=2012/09/09/12/00/00-avator-nouvel-outil-de-creation-davatar-beta
title: Avator, nouvel outil de création d'avatar (beta)
uri: Avator-nouvel-outil-de-création-d-avatar-(beta)
url: http://www.yosko.net/?d=2012/09/09/12/00/00-avator-nouvel-outil-de-creation-davatar-beta
content:
<b>Je reviens (enfin !) de vacance, et après en avoir terminé avec une semaine de boulot assez chargée, je trouve enfin le temps de vous écrire à nouveau. Et ça faisait un sacré petit bout de temps que je voulais présenter ce projet.</b><br/>
<p>Rappellez-vous, après l'adaptation d'<a href="http://www.yosko.net/anime-girl-generator.php">Anime Girl Generator en C#.Net</a>, je vous avais évoqué mon envie d'en faire une variante en C++/Qt multiplateforme et open-source. Si je n'ai pas immédiatement envisagé de la réaliser, j'ai commencé à travailler sur un autre outil qui a finalement dérivé pour fourni des fonctionnalités apparentées. J'ai l'honneur de vous présenter&nbsp;: <span style="font-weight: bold;"><span style="text-decoration: underline;">Avator&nbsp;!</span></span></p>

<p>En réalité, j'étais parti sur un outil de gestion des couleurs en Pixel-Art suite à <a href="http://www.yosko.net/index.php?d=2012/08/28/12/00/00-pixel-art-en-ombres-et-lumieres">mes recherches sur le sujet</a>. Mais une fois le premier jet en place, j'ai re-codé la plupart des fonctionnalités qui m'intéressaient de ma version d'AGG.</p>
<h3>Avator, un "avatar generator"</h3>

<p><div style="text-align:center;"><img src="media/fa87b9e1.avator-v05-example.png" data-original-source="http://www.yosko.net/img/avator-v05-example.png" alt="Exemple davatar realise avec Avator 07" width="40" height="80" style="max-width: 100%; height: auto;" /></div></p>

<p><div style="text-align:center;"><span style="font-weight: bold;"><a href="http://www.yosko.net/avator.php">>> Aller sur la page d'Avator <<</a></span></div></p>
Du coup, je suis prêt à vous présenter aujourd'hui une première version d'Avator, dont voici les principales fonctionnalités&nbsp;:<br/>
<ul>
<li><span style="text-decoration: underline;">Affichage d'un avatar éditable</span></li>
<li><span style="text-decoration: underline;">Gestion multi-couche&nbsp;:</span> une couche pour chaque élément (corps, vêtements, cheveux, visage, ...)</li>
<li><span style="text-decoration: underline;">Adaptable&nbsp;:</span> vous pouvez définir dans un fichier .ini la façon de construire un avator en fonction des différentes ressources que vous utilisez (définissez vos propres couches, avec celles qui vous sont utiles, celles qui sont facultatives).</li>
<li><span style="text-decoration: underline;">Couleurs&nbsp;:</span> modifier les couleurs d'une couche (pas encore en temps réel, mais ça viendra)</li>
<li><span style="text-decoration: underline;">Sauvegarder vos modèles&nbsp;:</span> exporter des modèles d'avatar dans des fichiers texte</li>
<li><span style="text-decoration: underline;">Exportez votre avatar au format PNG</span></li>
<li><span style="text-decoration: underline;">D'autres fonctionnalités à venir</span>&nbsp;: création et utilisation de préréglages de couleurs, sélections multiples d'une couche, mémorisations en tout genres, affichage en français ou autre, etc...</li>
</ul>

<p>Et un petit aperçu, un&nbsp;:<br/>
<div style="text-align:center;"><img src="media/19e20cf2.avator-v05-001.png" data-original-source="http://www.yosko.net/img/avator-v05-001.png" alt="Apercu de lecran principal dAvator" width="485" height="733" style="max-width: 100%; height: auto;" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">L'interface sera probablement revue dans la version finale,<br/>
pour plus de simplicité et d'accessibilité</span></div></p>
La gestion des couleurs dans Avator étant un peu particulière, elle ne peut s'utiliser que sur des images parfaitement compatible.<br/>
Mais si on omet cette fonctionnalité, Avator reste compatible avec les ressources existantes suivantes&nbsp;:<br/>
<ul>
<li><span style="text-decoration: underline;">RPG Maker VX Ace&nbsp;:</span> Avator est entièrement compatible avec les ressources de l'éditeur de sprites d'RPGMVXA. N'ayant pas les droits sur les images, je ne peux vous les fournir, mais elles se trouvent facilement sur le web.</li>
<li>AGG&nbsp;: Avator n'est que partiellement compatible avec les ressources d'Anime Girl Generator pour la simple raison que leur logique de gestion des couches est très différente. Je n'ai pas souhaité reprendre ce principe, trop complexe à développer (surtout dans un système dynamique)</li>
<li>Si vous connaissez d'autres ressources de ce type qui pourraient être utilisées dans Avator, faites-moi signe&nbsp;!</li>
</ul>

<p>Si vous avez des questions, des remarques, des préréglages de couleurs à proposer ou des ressources particulières que vous souhaiteriez utiliser dans Avator, n'hésitez pas à me contacter.</p>
<h3>Apparté sur les couleurs dans Avator</h3>

<p>Pour ceux que ça intéresse (je ne voudrais pas saouler les autres avec mes propos techniques)&nbsp;:</p>

<p>La gestion des couleurs est un peu particulière, puisqu'elle a été pensée pour mes travaux en pixel art. Elle peut même sembler complexe de prime abord, mais les variations d'ombre et de lumière d'une même couleur sont calculée selon un procédé artistique et mathématique censé faciliter vos choix.</p>

<p><div style="text-align:center;"><img src="media/1df4eaa8.avator-v05-002.png" data-original-source="http://www.yosko.net/img/avator-v05-002.png" alt="Apercu de lecran de conversion des images dans Avator" width="286" height="372" style="max-width: 100%; height: auto;" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">Pour jouer avec les couleurs dans Avator, les images doivent être en "couleurs indexées".<br/>
Un outil de conversion est donc intégré pour réaliser cela</span></div></p>

<p>Si j'ai abordé les couleurs sous cet angle, c'était en réalité pour répondre à un besoin personnel&nbsp;: je galère toujours à sélectionner mes couleurs pour mes dessins en pixel art, et j'ai justement l'intention d'en faire beaucoup dans un avenir proche (j'ai hâte de vous montrer ça, mais y'a encore un sacré boulot à abattre avant que ça ne devienne possible).</p>

<p>En gros, chaque zone de couleur comporte un maximum de 6 variantes (en dégradé, exprimant les zones d'ombres et de lumières). Plutôt que de refaire une rotation basique de couleur comme le fait AGG, j'ai préféré me baser sur mes travaux récents pour essayer d'obtenir toujours des couleurs "réalistes". En effet, la rotation donnait parfois des résultats très artificiels et assez dégueulasse...</p>

<p>PS&nbsp;: un grand merci à tous ceux qui ont accepté de tester ça en avant-première, et ont gentiment essuyé les premiers plâtres. On ne m'a remonté que peu de bugs, mais je suis sûr qu'il en reste&nbsp;!</p>


feed_id: http://www.yosko.net/?d=2012/10/05/22/51/37-rss-est-mort-longue-vie-a-rss
title: RSS est mort, longue vie à RSS !
uri: RSS-est-mort-longue-vie-à-RSS
url: http://www.yosko.net/?d=2012/10/05/22/51/37-rss-est-mort-longue-vie-a-rss
content:
<b></b><br/>
<p>Cela fait plusieurs mois que j'entend parler de l'obsolescence soit-disant avérée (ou tout du moins programmée) de RSS et de son compagnon, Atom. Tout récemment encore, Google a prit des décisions sur feedburner <a href="http://maniacgeek.wordpress.com/2012/09/14/bientot-la-fin-de-feedburner/">qui laissent entendre qu'ils souhaitent s'en éloigner</a> (ce n'est pas forcément plus mal, puisqu'il s'agissait d'une "petite" partie, très fermée, des flux).</p>

<p>De mon côté, j'ai tendance à ressentir chaque jour un peu plus l'utilité de ces flux d'information. Je n'ai commencé à les utilisé qu'il y a peu (début 2010), et je peux de moins en moins m'en passer. J'agrémente jour après jour mon aggrégateur d'agrébles sources d'informations (ouais je sais, mes allitérations sont nazes...).</p>

<p>Il y a bien quelques outils qu'on tend à considérer comme des concurrents&nbsp;: les microblogs et autres outils de repost permettent à autrui d'aggréger l'information à votre place, ne vous en laissant que la crème de la crème comme susbtrat à votre vision du monde (ou de la toile). Mais n'est-ce pas là leur plus grand défaut ? Tout l'intérêt que je vois à mes flux, c'est que je les choisis, les prend de divers horizons, que je cherche à conserver un esprit critique sur mes sources d'informations (pas que j'y arrive particulièrement, hein... surtout si on considère que 80% de mes flux concernent les lolcats et les mèmes...). Et puis même pour suivre des microblogs, je passe par des flux.</p>

<p>J'en viens à me demander si ceux qui déclarent RSS comme mort n'y cherchaient pas, à tort, un outil supplémentaire pour faire des sous, et n'y auraient vu qu'un moyen de perdre des visiteurs sur leur site... Bref des gens qui ne veulent pas qu'on leur impose de fournir leurs données sous forme de flux parce que ça n'est pas assez rentable (et qu'on ne me parle pas de feedburner)...</p>

<p><div style="text-align:center;"><img src="media/25135410.feed.png" data-original-source="http://www.yosko.net/img/feed.png" alt="feed.png" width="24" height="24" style="max-width: 100%; height: auto;" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">Je déteste les article sans illustration, donc voici une icône RSS</span></div></p>
Mais bon, trève de discours sombres, et sommes toutes assez peu vérifiable, et passons à la partie la plus intéressante&nbsp;: mes flux&nbsp;!<br/>
Je me suis dit qu'il pourrait être intéressant de vous en partager quelques-uns (juste une 60aine parmi les 256 auxquels je suis actuellement abonné)&nbsp;:<br/>
<ul>
  <li>
    Arts graphique&nbsp;: essentiellement des dessinateurs sur DA, découverts au fil de mes périgrinations sur le web ou plus particulièrement via <a href="http://feeds2.feedburner.com/PinupCartoonGirls">PinupCartoonGirls</a> (oui, c'est assez orienté&nbsp;:-° )&nbsp;:<br/>
    <ul>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:aditya777">aditya777 sur DA</a></li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:artgerm">artgerm sur DA</a></li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:ex-kalibur">ex-kalibur sur DA</a>&nbsp;: pixel art</li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:lolita-art">lolita-art sur DA</a> (parfois un peu NSFW)</li>
      <li><a href="http://sergebirault.blogspot.fr/2012/09/suzanne.html">PapaNinja</a> (NSWF&nbsp;!)&nbsp;: un artiste français</li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:randis">randis sur DA</a></li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:robaato">robaato sur DA</a>&nbsp;: il est en train de bosser sur un projet de arpg <a href="http://cryamore.tumblr.com/rss">qui a l'air assez sympa</a></li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:sigurdhosenfeld">sigurdhosenfeld</a>&nbsp;: une fixation (un peu NSFW) sur les Nintendo girls</li>
      <li><a href="http://backend.deviantart.com/rss.xml?q=gallery:gb2k">gb2k sur DA</a></li>
      <li><a href="http://latolo.didrocks.fr/feed/atom">Julie Tolomelli</a></li>
      <li><a href="http://www.pierre-vincent.com/blog/?feed=rss2">Pierre Vincent</a>, photographe</li>
    </ul>
  </li>
  <li>Blogueurs, amis ou journalistes dont j'aime suivre l'activité (y'a un peu de tout, là)&nbsp;:<br/>
    <ul>
      <li><a href="http://artisan.karma-lab.net/rss.xml">Artisan Numérique</a> pour le bricolage virtuel</li>
      <li><a href="http://h3b.us/feed/">H3bus Bleuarg</a> (même s'il poste encore moins souvent que moi)</li>
      <li><a href="http://www.maitre-eolas.fr/feed/atom">Maître Eolas</a>&nbsp;: vulgarisation juridique</li>
      <li><a href="http://odieuxconnard.wordpress.com/feed/">L'odieux connard</a>&nbsp;: critique drôlement sérieux, vachement violent et digne successeur de <a href="http://feeds.feedburner.com/NiouTaik">Nioutaik</a></li>
      <li><a href="http://blog.m0le.net/feed/">m0le.net</a></li>
      <li><a href="http://feeds.feedburner.com/slydnet">SlyDnet</a></li>
      <li><a href="http://www.warriordudimanche.net/feed/rss">Warrior du Dimanche</a>&nbsp;: sur les conseils tous récents d'idleman</li>
      <li>idleman&nbsp;: il a droit à sa catégorie à lui dans mon aggrégateur, entre son <a href="http://blog.idleman.fr/?feed=rss2">blog</a>, et ses nombreux projets (13 flux rien que pour lui, avec Leed, Moss, DizPlay, Dropcenter, etc... et encore je n'ai pas compté CodeZ, puisque c'est rangé dans les projets sur lesquels je bosse)</li>
      <li>le grand, le seul, l'unique&nbsp;: <a href="http://sebsauvage.net/rss/updates.xml">sebsauvage</a>, sans oublier son <a href="http://sebsauvage.net/links/index.php?do=atom">shaarli</a></li>
      <li>Divers trucs sur des technos ou applis en particulier (<a href="http://blognode.fr/category/tuto/feed/">Node.js</a>, <a href="http://www.cdisplayex.com/feed/atom">CDisplayEx</a>, <a href="http://www.dailydoseofexcel.com/feed/">Excel</a>, <a href="http://page2rss.com/rss/9b28b542e72ae3c35e855d4c77ec6f2f">Keepnote</a>, <a href="http://twitter.com/statuses/user_timeline/sparkleshare.rss">SparkleShare</a>, <a href="http://www.sublimetext.com/blog/feed">Sublime</a>, <a href="http://mycinema-software.com/feed">MyCinema</a>, etc...)</li>
    </ul>
  </li>
  <li>Quelques sites de news en tous genres<br/>
    <ul>
      <li><a href="http://www.clubic.com/xml/news.xml">Clubic</a>, pour l'informatique</li>
      <li><a href="http://feeds.p-nintendo.com/pn-majs">Puissance-Nintendo</a>, <a href="http://www.puissance-zelda.com/rss.xml">Puissance-Zelda</a> et <a href="http://halo.fr/index.php?option=com_ninjarsssyndicator&feed_id=1&format=raw">Halo.fr</a> quand Clubic ne suffit pas pour les jeux-vidéo</li>
      <li><a href="http://www.geekculture.fr/feed">GeekCulture</a> pour le cinoche et les comics (parce que soyons franc, l'odieux connard ne m'aide pas à choisir ce que je vais voir, juste ce que je ne vais pas voir)</li>
      <li><a href="http://www.sciencesetavenir.fr/rss.xml">Sciences & Avenir</a> pour les sciences (mais pas pour l'avenir. Pour ça j'ai ma voyante)</li>
      <li>Sans oublier <a href="http://feeds.feedburner.com/refletsinfo">Reflets.info</a> et le blog de <a href="http://bluetouff.com/feed/">Bluetouff</a> (même s'il n'y poste que rarement)</li>
    </ul>
  </li>
  <li>For the lulz, les lolcats et autres mèmes (tout ce que j'aime vraiment dans cette catégorie, je le reposte sur <a href="http://7avoir.yosko.net/">7àvoir</a>):<br/>
    <ul>
      <li><a href="http://lachroniquefaciledumercredi.wordpress.com/feed/">La chronique facile du mercredi</a>&nbsp;: chroniques sur les divers mèmes à la con</li>
      <li>ICanHasCheezburger&nbsp;: j'ai dû suivre une trentaine de site de ce réseau, mais je n'en ai conservé que 2, aujourd'hui&nbsp;: <a href="http://feeds.feedburner.com/comixed/CMX">Comixed</a> et <a href="http://feeds.feedburner.com/SoMuchPun">So Much Pun</a></li>
      <li><a href="http://feeds.feedburner.com/bashfr">DTC/BashFr</a> (ils n'arriveront jamais à se débarrasser de l'ancien nom)</li>
      <li><a href="http://www.wikipourri.com/rss/">WikiPourri</a></li>
      <li><a href="http://feeds.feedburner.com/ImgurGallery?format=xml">Imgur</a>&nbsp;: un top des meilleures images (pas que des trucs drôle, y'a parfois un peu d'art ou d'histoires attendrissantes de bébés dauphins)</li>
      <li><a href="http://feeds2.feedburner.com/9GAG">9Gag</a>&nbsp;: rah mais pourquoi je me suis mis à suivre ce site aussi ? Quel est le x@:~# qui m'a filé ce lien ?</li>
      <li><a href="http://www.lelombrik.net/files.rss">LeLombrik</a>&nbsp;: pas mieux >.< (mais en français)</li>
      <li><a href="http://feeds.feedburner.com/alt-tab-org">Alt-Tab.org</a></li>
      <li><a href="http://blackboardsinporn.blogspot.com/feeds/posts/default">BlackboardsInPorn</a>&nbsp;: pas vraiment NSFW, juste marrant</li>
      <li><a href="http://feeds.feedburner.com/eatliver">EatLiver</a>&nbsp;: une bonne pré-sélection</li>
      <li><a href="http://www.foundshit.com/feed/">FoundShit</a>&nbsp;: à la jonction entre le fun, le WTF et l'art</li>
      <li><a href="http://feeds.feedburner.com/glennztees">GlennzTees</a>&nbsp;: des t-shirts geeks et marrants</li>
      <li><a href="http://www.halolz.com/feed/rss/">Halolz</a>&nbsp;: humour vidéoludique très orienté (surtout TF2, un peu de Halo et de Pokemon)</li>
      <li><a href="http://feeds2.feedburner.com/Ufunknet-GadgetsJaponaisEtArtsInsolites">UFunk</a>&nbsp;: bien que parfois un peu trop sponsorisé, contient aussi quelques perles artistique et/ou humoristiques</li>
      <li><a href="http://feeds.feedburner.com/DuelingAnalogs">Dueling Analogs</a>&nbsp;: dans le même genre qu'Halolz, mais en plus varié</li>
      <li><a href="http://feeds.feedburner.com/WtfJapanSeriously">WtfJapanSeriously</a>&nbsp;: faut-il vraiment une description ?</li>
      <li><a href="http://lesjoiesducode.tumblr.com/rss">Les joies du code</a>&nbsp;: Gif animés appliqués à l'informatique</li>
      <li><a href="http://cacadujour.fr/feed/">CacaDuJour</a>&nbsp;: détournement d'affiches de films</li>
    </ul>
  </li>
  <li>Webcomics&nbsp;: j'en avais <a href="http://blog.yosko.net/2010/10/webcomic-ouaibe-comique/">déjà fait une liste</a>, donc je ne vais pas me casser la tête à recommencer (qui a dit "espèce de grosse flemasse" ?)</li>
  <li>Pour finir, quelques petits outils générant des flux RSS pour des besoins précis&nbsp;:<br/>
    <ul>
      <li><a href="http://www.google.com/alerts">Google Alert</a>&nbsp;: ce n'est pas à proprement parler un flux, mais plus un outil pour se tenir informé de sujets précis. C'est tellement relou de devoir parcourir tous les jours 50 news d'un site de jeux-vidéo pour savoir si l'une d'entre-elle parle enfin de la sortie d'Half-Life 3.</li>
      <li><a href="http://www.page2rss.com/">Page2RSS</a>&nbsp;: un outil qui monitore une page web à votre place, et vous informe de toute modification via RSS. Pratique pour les sites préhistorique composé d'une seule page éditée tous les 36 du mois.</li>
    </ul>
  </li>
</ul>

<p>J'en ai probablement oublié des très importants (voir vitaux), mais on va s'arrêter là pour l'instant.</p>

<p>Keep Calm and Rock On.</p>

<p>EDIT&nbsp;: promis, idleman, la prochaine fois je fais plus court... Ah mince, non, je vais devoir présenter Avator, et ça va être dur de faire concis... -__-</p>


feed_id: http://www.yosko.net/?d=2012/09/19/19/10/00-xkcd-click-and-drag
title: xkcd: Click and Drag
uri: xkcd-Click-and-Drag
url: http://www.yosko.net/?d=2012/09/19/19/10/00-xkcd-click-and-drag
content:
<b>Le dernier strip d'xkcd est vraiment fun. Un strip dynamique gigantesque, bourré de détails marrant cachés.</b><br/>Bon, je sais que je me suis déjà fait <a href="https://azttm.wordpress.com/2012/09/19/map-of-xkcds-click-and-drag/">griller par certains</a>, mais j'ai quand même décidé d'aller au bout de mon petit jeu&nbsp;: je me suis amusé à vous en faire une miniature complète du strip d'xkcd.<br/>
<br/>
<div style="text-align:center;"><a class="fancybox" href="media/855a0178.xkcd-click-and-drag-thumb.png" data-original-source="http://www.yosko.net/sandbox/img/xkcd-click-and-drag-thumb.png"><img src="media/77198050.xkcd-click-and-drag-thumb-thb.jpg" data-original-source="http://www.yosko.net/sandbox/img/xkcd-click-and-drag-thumb-thb.jpg" alt="xkcd-click-and-drag-thumb.png"  /></a></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">Cliquez pour agrandir un peu (~5200x2400 pixels)</span></div><br/>
<br/>
Pour la réaliser, j'ai observé le code de la page, et j'ai constaté, comme azt.tm avant moi, que l'affichage se basait sur des images de carrées de 2048 pixels de côté, et dont les noms sont basés sur un système d'orientation nord-sud (haut-bas de l'image) et ouest-est (gauche-droite).<br/>
<br/>
Le javascript utilisé par Randall Munroe <a href="http://imgs.xkcd.com/clickdrag/1110.js">semblait indiquer</a> qu'il y avait des coordonnées allant de jusqu'à 14 nord, 48 est,  25 sud et 33 ouest.<br/>
<br/>
J'ai donc réalisé un petit script qui m'a sorti les liens vers toutes les images supposées exister, entre (33 ouest - 14 nord) et (48 est - 25 sud). Soit un total de 81 par 39 images, donc 3159 images de 2048 pixels, soit.... 165888 par 79872 pixels&nbsp;!<br/>
Bon, ok, jusqu'ici, on s'en fout un peu.<br/>
<br/>
Ensuite, j'ai utilisé l'addon de Firefox nommé DownThemAll, pour télécharger automatiquement toutes ces images. On constate finalement que certaines n'existent pas, dans les zones où l'auteur n'a rien dessiné. Il n'y a finalement "que" 225 images au total. Les autres redirigent vers une 404, et DownThemAll a été assez intelligent pour ne pas les télécharger. Bon la méthode est assez bourrine, mais je suis sûr que son serveur est dimenssionné pour suporter une telle charge. O:-P<br/>
<br/>
Un petit coup d'<a href="http://www.imagemagick.org/script/index.php">imagemagick</a> pour batchresize tout ça en carrés de 64px&nbsp;:<br/>
<pre class="brush:bash;">mogrify -resize 3.125% *.png</pre>
<br/>
Il ne reste plus qu'à pondre un petit code pour rassembler le tout en une image. J'ai d'abord pensé à la commande montage d'imagemagick, mais soit elle n'est pas assez puissante, soit je n'ai pas trouvé les fonctionnalités que je cherchais.<br/>
<br/>
Du coup, j'ai fait ça en C++/Qt&nbsp;:<br/>
<br/>
<pre class="brush:cpp;">#include &lt;QImage&#62;<br/>
#include &lt;QPainter&#62;<br/>
#include &lt;QFile&#62;<br/>
#include &lt;QDebug&#62;<br/>
<br/>
int main(int argc, char *argv[]) {<br/>
    const int TILE_SIZE = 64;<br/>
//    const int TILE_SIZE = 2048;<br/>
    const int NORTH_SIZE = 14;<br/>
    const int EAST_SIZE = 33;<br/>
    const int SOUTH_SIZE = 23;<br/>
    const int WEST_SIZE = 48;<br/>
<br/>
    QImage *resultImage = new QImage(TILE_SIZE * (EAST_SIZE + WEST_SIZE) ,TILE_SIZE * (NORTH_SIZE + SOUTH_SIZE), QImage::Format_ARGB32);<br/>
    resultImage-&#62;fill(Qt::gray);<br/>
    for(int i=0; i &lt; (EAST_SIZE+WEST_SIZE); i++) {<br/>
        for(int j=0; j &lt; (NORTH_SIZE+SOUTH_SIZE); j++) {<br/>
            QString filePath = "thumbs/";<br/>
//            QString filePath = "images/";<br/>
            if(j &lt; NORTH_SIZE) {<br/>
                filePath += QString::number(NORTH_SIZE-j) + "n";<br/>
            } else {<br/>
                filePath += QString::number(j-NORTH_SIZE+1) + "s";<br/>
            }<br/>
            if(i &lt; EAST_SIZE) {<br/>
                filePath += QString::number(EAST_SIZE-i) + "w";<br/>
            } else {<br/>
                filePath += QString::number(i-EAST_SIZE+1) + "e";<br/>
            }<br/>
            filePath += ".png";<br/>
            QFile imageFile(filePath);<br/>
            if(imageFile.exists()) {<br/>
                qDebug() &lt;&lt; filePath;<br/>
                QPainter painter;<br/>
                painter.begin(resultImage);<br/>
                painter.drawImage(i*TILE_SIZE, j*TILE_SIZE, QImage(filePath));<br/>
                painter.end();<br/>
            }<br/>
        }<br/>
    }<br/>
    resultImage-&#62;save("result.png");<br/>
} </pre>
<br/>
Je vous proposerais bien d'exécuter le même code directement sur les images en 2048 pixels, mais je pense que la mémoire pourrait avoir quelques soucis.<br/>
<br/>
PS&nbsp;: et j'allais presque oublier le lien vers le strip&nbsp;!!! <a href="http://xkcd.com/1110/">Allez jouer avec sur xkcd</a>&nbsp;!<br/>
<br/>
<div style="text-align:center;"><img src="media/c63d862d.xkcd-sample.png" data-original-source="http://www.yosko.net/sandbox/img/xkcd-sample.png" /></div>


feed_id: http://www.yosko.net/?d=2012/09/03/17/25/00-qt-creation-dynamique-de-widgets
title: Qt : création dynamique de widgets
uri: Qt-création-dynamique-de-widgets
url: http://www.yosko.net/?d=2012/09/03/17/25/00-qt-creation-dynamique-de-widgets
content:
<b>Vous êtes-vous déjà retrouvé devant une situation où vous réalisez une interface avec un nombre de boutons ou de listes indéterminées ?<br />
<br />
Créer une interface lorsqu'on sait déjà à l'avance comment elle sera constituée est une chose, devoir créer les éléments qui la composent de manière dynamique en est une autre.</b><br/>
<p>Je me suis retrouvé face à ce problème sur Qt récemment et me suis dit qu'il pourrait être intéressant de vous faire profiter de ce que j'ai appris.</p>

<p>Imaginons que nous devons créer une interface avec des boutons (QPushButton). Un nombre indéterminé de boutons. Du moins, un nombre que notre programme ne connait pas à l'avance, qu'il découvrira à l'exécution (récupérant cette information dans un fichier .INI, par exemple).</p>

<p>Ainsi, lorsqu'on lit le fichier .ini, une simple petite boucle nous permet de créer tout ça&nbsp;:<br/>
<pre class="brush:cpp;">QVBoxLayout *mainLayout = new QVBoxLayout;
this->setLayout(mainLayout);

//Lecture du fichier .ini
QSettings iniFile("DynamicWidgets.ini", QSettings::IniFormat);
iniFile.beginGroup("Buttons");

//Lecture d'un tableau de clés
foreach(QString key, iniFile.childKeys()) {
	//Récupération du nom du bouton
	QString buttonName(iniFile.value(key,"").toString());
	QPushButton *button = new QPushButton;
	button->setText(buttonName);
	mainLayout->addWidget(button);
}
iniFile.endGroup();</pre>
Et un petit exemple de fichier .ini pourrait être&nbsp;:<br/>
<pre class="brush:bash;">[Buttons]
1=Presse-moi
2=Youpi
3=Je hais les pointeurs
4=Yosko wuz here
5=Bouton n°5</pre>

<p>On se retrouve donc avec 5 boutons (alors que rien dans notre programme n'indiquait qu'il y en aurait 5).</p>

<p>Mais maintenant, comment fait-on pour interagir avec ces boutons ? Si on les connectes tous à un slot, comment le slot saura-t-il quel bouton l'a appelé ?<br/>
Pour cela, on peut procéder de plusieurs manières. Il nous faut tout d'abord déclarer un slot dans le .h&nbsp;:</p>
<pre class="brush:cpp;">public slots:
    void displayButton(QString buttonName);</pre>

<p>Ce slot fera tout simplement un affichage du nom du bouton dans une boite de message</p>
<pre class="brush:cpp;">void MainWindow::displayButton(QString buttonName) {
    QMessageBox::information(this, "Bouton", "Bouton \"" + buttonName + "\" cliqué !");
}</pre>
<h3>L'erreur</h3>

<p>Celle qui nous viendrait naturellement à l'esprit serait de connecter le signal à notre slot en ajoutant la valeur qui nous intéresse (comme le nom du bouton, par exemple) dans les paramètre du slot. Il suffirait théoriquement de faire cette connexion lorsqu'on est dans notre boucle, et ainsi transmettre notre "buttonName"&nbsp;:</p>
<pre class="brush:cpp;">QObject::connect(button, SIGNAL(clicked()), this, SLOT(displayButton(buttonName)));</pre>

<p>Sauf que... NON&nbsp;! Il s'agit d'une erreur.</p>

<p>Les signaux et les slots fonctionnent de telle manière qu'il est interdit de transmettre à un slot plus que ce que le signal transmet (note qu'à l'inverse, le slot n'est pas obligé de prendre en compte tous les paramètres envoyé par le signal). Le code ci-dessus ferait donc planter votre programme.</p>
<h3>Solution 1&nbsp;: récupération du pointeur</h3>

<p>A l'intérieur même du slot, nous avons un moyen de récupérer le pointeur vers l'objet qui a émit le signal déclencheur. Ici, ce pointeur est un pointeur vers un QPushButton.</p>

<p>Ainsi, si notre slot ne prenait pas de paramètre, il pourrait ressembler à ceci&nbsp;:</p>

<p><pre class="brush:cpp;">void MainWindow::displayButton() {
    //QObject::sender() retourne le pointeur vers l'objet qui a émit le signal
    QPushButton *button = qobject_cast<QPushButton*>(QObject::sender());

    //On récupère simplement le texte du bouton pour avoir le nom
    QString buttonName = button->text();

    QMessageBox::information(this, "Bouton", QString("Bouton \"" + buttonName + "\" cliqué !"));
}</pre>

<p>Solution simple, mais probablement pas la plus judicieuse. Imaginons par exemple que vous ayez envie d'appeler ce slot via des signaux émits par des objets qui n'ont rien à voir avec des QPushButton ? Vous auriez alors un beau plantage.</p>
<h3>Solution 2&nbsp;: mapper les signaux et slot</h3>

<p>Cette solution est en réalité le seul véritable moyen de transmettre un paramètre supplémentaire au slot, en plus de ceux issus du signal. Les objets de la classe <code>QSignalMapper</code> s'occupent de décider quel paramètre supplémentaire envoyer au slot en fonction de l'objet émettant le signal.</p>

<p>Avant notre boucle, on déclare le mapper&nbsp;:</p>
<pre class="brush:cpp;">QSignalMapper *signalMapper = new QSignalMapper(this);
QObject::connect(signalMapper, SIGNAL(mapped(QString)), this, SLOT(displayButton(QString)));</pre>

<p>Le mapper est le seul qui sera directement connecté au slot de destination. En gros, on lui dit qu'il devra transmettre au slot une QString issue de son mappage.</p>

<p>Le mappage en lui même se fait en utilisant la méthode setMapping de QSignalMapper, qui consiste à lui dire "tel bouton correspond à telle QString". Et quand on fait le mappage, on décide complètement de ce que sera cette QString. On pourrait très bien en faire une constante, ou la récupérer d'ailleurs. Ici, on réutilise simplement la même chaîne que celle affichée sur le bouton&nbsp;:</p>
<pre class="brush:cpp;">signalMapper->setMapping(button, buttonName);
QObject::connect(button, SIGNAL(clicked()), signalMapper, SLOT(map()));</pre>

<p>Et voilà, ça fonctionne. C'est magique&nbsp;!</p>

<p>Vous pouvez récupérer les sources de cet exemple ici&nbsp;: <a href="http://www.yosko.net/files/dynamic-widgets-and-qsignalmapper.zip">Widgets dynamiques et QSignalMapper</a>.</p>
<h3>Solution 3&nbsp;: widget "wrapper" customisé</h3>

<p>Une autre solution pourrait être d'étendre la classe QWidget, et de se faire un widget "customisé" qui contiendrait un bouton et un slot, puis de créer plusieurs instance de ce widget.</p>

<p>Pour l'exemple proposé dans cet article, il s'agit d'une solution un peu lourde, mais dans certains cas, cela peut vite s'avérer utile (si votre interface répète toujours le même "module" avec ses 3 boutons, sa liste déroulante, son label et sa case à cochée + tous les signaux et slots associés).</p>

<p>Je ne vais pas enter dans les détails de cette idée, et vous laisserai chercher par vous-même. Cependant, si vous souhaitez la mettre en place, voici un petit détail qui pourrait vous être utile&nbsp;: si jamais vous souhaitez remonter quelque-chose à la fenêtre ou au widget parent depuis votre widget personnalisé, rien ne vous empêche d'émettre manuellement les signaux du parent&nbsp;:</p>
<pre class="brush:cpp;">QWidget *papa = dynamic_cast<QWidget *> (this->parentWidget());
emit papa->signalDePapa();</pre>
<h3>Conclusion</h3>

<p>Nous avons vu ici quelques idées sur la manière de gérer la création de widgets de manière dynamique. Il ne s'agit très probablement pas des seules méthodes possible, et je ne vous ai pas non plus complètement prémâché le code. Mais j'espère que cet article saura vous être utile&nbsp;:-)</p>

<p>Si vous avez des questions ou remarques, surtout n'hésitez pas à me contacter ou à répondre dans les commentaires.</p>


feed_id: http://www.yosko.net/?d=2012/08/28/12/00/00-pixel-art-en-ombres-et-lumieres
title: Pixel Art en ombres et lumières
uri: Pixel-Art-en-ombres-et-lumières
url: http://www.yosko.net/?d=2012/08/28/12/00/00-pixel-art-en-ombres-et-lumieres
content:
<b>Vous êtes vous déjà demandé ce qui faisait la richesse des couleurs d'une image, ou au contraire ce qui vous donnait l'impression qu'elle était plate ou vide ?<br />
<br />
En réfléchissant au problème, je suis arrivé à des conclusions quelque peu inattendues et intéressantes sur l'expression des ombres et lumières dans le Pixel Art.</b><br/><h3>Ombres et lumières</h3>

<p>Pour qu'une image donne une impression de profondeur et de réalisme, elle se doit de rendre des effets d'ombre et de lumière.</p>

<p>Ces ombres et lumières apparaissent sous forme de dégradés plus ou moins marqués en fonction des zones. Sur un véritable dessin ou une photo, il y aurait une "infinité" de variantes de couleurs sur ces dégradés. Dans mon exemple ci-dessous, en pixel art, il m'a paru préférable de ne conserver au maximum que 6 variantes d'une même couleur pour garder un effet "jeu vidéo à l'ancienne", et surtout pour fonctionner dans un espace discret, plutôt que continu.</p>

<p><div style="text-align:center;"><img src="media/eb5b03d5.Flat-to-LumSat.png" data-original-source="http://www.yosko.net/sandbox/img/Flat-to-LumSat.png" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">L'image de droite comporte une variation de saturation et de luminosité, contrairement à celle de gauche</span></div></p>

<p>Comme on peut le voir, l'image de gauche parait franchement "plate", aucun volume n'est exprimé, et elle semble un peu vide.<br/>
Dans la seconde, on constate bien les variantes (sur le violet des cheveux par exemple), et on a un peu plus l'impression d'avoir une expression de la troisième dimension et de la lumière.</p>

<p>Les variantes ont été constituées de manière très basique (en reprenant le modèle HSL déjà évoqué plus tôt)&nbsp;: on conserve ici la même teinte, et on fait varier la luminosité (ce qui semble logique, puisqu'on cherche à exprimer une variation de lumière sur une même surface) et la saturation.</p>

<p>Mais il semble encore manquer quelque chose...<br/>
En effet, cette méthode est trop simpliste, et ça se voit. Les effets de lumière paraissent étranges, voire "plats", encore une fois...</p>
<h3>Couleurs des ombres et lumières</h3>
Il y a deux éléments primordiaux qui n'ont pas été pris en compte jusqu'ici&nbsp;:<br/>
<ol>
<li>La lumière a une couleur. Toute source de lumière tend vers une couleur, le Soleil y compris (en réalité, le Soleil émet une lumière blanche, mais, après être passée dans l’atmosphère, le bleu s'est dispersé dans le ciel, et on voit surtout du jaune). De ce fait, une surface bleue éclairée par une lampe jaune devrait paraitre un peu plus verte dans les zones éclairées, et plus bleue dans les zones d'ombre</li>
<li>Les ombres aussi comportent une couleur. En effet, si vous vous mettez à l'ombre d'un bâtiment orange, votre peau paraitra plus orange. Ce ne sont pas les ombres elles-mêmes qui génèrent la couleur. En réalité, l'élément qui entraine l'ombre reflète aussi une infime partie de la lumière dans sa couleur.</li>
</ol>

<p>Un bon exemple de ces deux points&nbsp;: la peau parait "rose" du fait de la couleur rouge du sang. Et souvent, les sources de lumière artificielles ont tendance à tendre vers le jaune.<br/>
On constatera que les photos et dessins de peau (je ne vous pousse pas à aller regarde du nu, mais c'est le meilleur moyen de le constater) ont tendance à accentuer les rouges dans les ombres, et à accentuer le jaune/orange dans les zones de lumière.<br/>
C'est particulièrement visible sur <a href="http://wallbase.cc/wallpaper/2177747">ce dessin</a> et un peu moins sur <a href="http://wallbase.cc/wallpaper/129116">cette photo</a></p>
<h3>Méthode empirique</h3>

<p>Pour simplifier, on pourrait partir du principe que toutes les sources de lumière sont jaunes, et que plus un objet est éclairé, plus il tend vers cette couleur, et plus il est dans l'ombre, plus il tend à refléter sa propre couleur.</p>

<p>C'est un peu comme ça que je le voyais, jusqu'à ce que j'analyse les couleurs utilisées dans divers sprites issus de jeux-vidéo différents (Ragnarok online, Jump Superstars, et divers autres). En prenant divers exemples, je me suis rendu compte que les 6 variantes de couleur avaient tendance à se construire sur le même principe, c'est à dire que les variations de teinte, de saturation et de luminosité entre les variantes d'une même couleur pouvait très souvent être exprimées avec la même formule mathématique.</p>

<p><div style="text-align:center;"><span style="font-style: italic;">Note&nbsp;: il n'y a aucune correspondance de couleur entre les graphiques, ni avec la couleur affichée<br/>
Les variantes de couleurs vont de 0 (la plus sombre) à 5 (la plus claire)</span></div></p>

<p><div style="text-align:center;"><img src="media/b12c739c.Color-saturation-graph.png" data-original-source="http://www.yosko.net/sandbox/img/Color-saturation-graph.png" /></div></p>

<p>Si on met de côté le drôle d'écart de la couleur 4, on a semble-t-il quelque chose d'assez linéaire. Chaque droite semble avoir une pente équivalente, mais un offset légèrement différent.</p>

<p><div style="text-align:center;"><img src="media/90b6ee80.Color-luminance-graph.png" data-original-source="http://www.yosko.net/sandbox/img/Color-luminance-graph.png" /></div></p>

<p>Ici, on constate que chacune des courbes se découpe en trois parties&nbsp;: une pente normale sur les premiers points, une pente forte au milieu et une pente faible sur la fin. Cela laisse imaginer une équation d'ordre 3 ou plus. Ici encore, les variations entre chaque courbe peuvent probablement s'exprimer par un offset, ou au pire par un facteur (ça reste franchement empirique et approximatif, mais avons-nous réellement besoin de plus de précision ?).</p>

<p>Bref, jusqu'ici, on constate que plus on est dans les ombres, plus la saturation est forte et la luminosité faible. Ca n'a rien de surprenant. Le plus intéressant arrive&nbsp;:</p>

<p><div style="text-align:center;"><img src="media/f32089b6.Color-hue-graph.png" data-original-source="http://www.yosko.net/sandbox/img/Color-hue-graph.png" /></div></p>

<p>Clairement, la variation de de teinte se présente sous forme linéaire. Mais les pentes varient beaucoup. Il y a donc une logique, mais laquelle ?</p>

<p>Elles semblent plus ou moins converger vers un point. Et ce point de convergence est {X=15, Y=75}. En bref, elles tendent toutes vers la couleur "75", qui correspond à un vert un peu jaune&nbsp;!</p>

<p>J'en entend dans le fond me dire que ce n'est pas le cas de la couleur 3, qui diverge (verge&nbsp;!). Sauf que... si&nbsp;!<br/>
En réalité, les valeurs que nous manipulons ici sont des angles exprimés en degrés. Si on calcule où semble tendre la couleur 3, on tombe sur 435°, qui équivaut en fait à 75° (rappel&nbsp;: 360° = 0°).</p>

<p>Si on applique cette logique à notre image du début, cela donne&nbsp;:</p>

<p><div style="text-align:center;"><img src="media/aa077192.LumSat-to-Hue.png" data-original-source="http://www.yosko.net/sandbox/img/LumSat-to-Hue.png" /></div><br/>
<div style="text-align:center;"><span style="font-style: italic;">L'image de droite comporte une variation de teinte, contrairement à celle de gauche</span></div></p>

<p>Je vous l'accorde, la différence ne saute pas aux yeux (elle est surtout visible dans les cheveux et le vêtement. La peau est un peu ratée, ici...). Et pourtant, c'est un élément crucial pour obtenir un effet plus réaliste.</p>
<h3>Traduction mathématique</h3>

<p>Après quelques recherches et interpolations, j'en viens à cette description du comportement des variations de couleurs sur les sprites de jeu vidéo entre ombre et lumière, que les graphistes les aient calculées ou choisies "au feeling", il semblerait qu'ils ont toujours tendance à suivre la même logique mathématique, qui peut s'exprimer comme suit&nbsp;:</p>
<h4>Teinte</h4>
<math xmlns="http://www.w3.org/1998/Math/MathML">
	<mstyle mathsize="14pt" tempstyle="1">
		<mrow>
			<mrow>
				<mtable>
					<mtr>
						<mtd>
							<mi>y</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>=</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>x</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>+</mo>
				<mfrac>
					<mrow>
						<mi>(</mi>
						<mi>75</mi>
						<mi>-</mi>
						<mi>x</mi>
						<mi>)</mi>
					</mrow>
					<mrow>
						<mi>15</mi>
					</mrow>
				</mfrac>
			</mrow>
		</mrow>
	</mstyle>
</math>

<p>(x = 0) étant la teinte appliquée à la première variante, la plus sombre.<br/>
On pourrait très bien faire varier le diviseur pour tendre plus ou moins rapidement vers le vert à 75°.</p>
<h4>Saturation</h4>
<math xmlns="http://www.w3.org/1998/Math/MathML">
	<mstyle mathsize="14pt" tempstyle="1">
		<mrow>
			<mrow>
				<mtable>
					<mtr>
						<mtd>
							<mi>y</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>=</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>55</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>-</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>7,2</mi>
							<mi>x</mi>
						</mtd>
					</mtr>
				</mtable>
			</mstyle>
		</mrow>
	</mrow>
</math>

<p>Une droite relativement simple à calculer (comme la plupart de mes calculs, il s'agit d'une conclusion empirique, bien sûr).</p>
<h4>Luminosité</h4>
<math xmlns="http://www.w3.org/1998/Math/MathML">
	<mstyle mathsize="14pt" tempstyle="1">
		<mrow>
			<mrow>
				<mtable>
					<mtr>
						<mtd>
							<mi>y</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>=</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>95</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>+</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>51,58</mi>
							<mi>x</mi>
						</mtd>
					</mtr>
				</mtable>
				<mo>-</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>18,55</mi>
							<msup>
								<mi>x</mi>
								<mi>2</mi>
							</msup>
						</mtd>
					</mtr>
				</mtable>
				<mo>+</mo>
				<mtable>
					<mtr>
						<mtd>
							<mi>2,92</mi>
							<msup>
								<mi>x</mi>
								<mi>3</mi>
							</msup>
						</mtd>
					</mtr>
				</mtable>
			</mstyle>
		</mrow>
	</mrow>
</math>

<p>Merci à l'outil d'interpolation de <a href="http://www.wolframalpha.com">WolframAlpha</a>, qui m'a permis d'arriver à un résultat exploitable, que j'ai ensuite simplifié un peu.</p>
<h3>Pistes de réflexion</h3>

<p>Maintenant que nous avons un moyen mathématique de calculer des variantes de couleur qui donneront un certain réalisme à notre image, que reste-t-il en suspens ?</p>
Tout d'abord, pourquoi le vert 75° ? Ce vert-jaune vers lequel semblent tendre toutes les couleurs dans leurs variantes claires pourrait-il avoir un lien avec l'un des éléments suivants ?<br/>
<ul>
<li>La couleur pour laquelle l'oeil humain est optimisé ? Cette couleur se trouve effectivement dans les verts un peu jaune.</li>
<li>La couleur de la lumière reçue du Soleil au travers de la couche d'Ozone ?<br/>
J'évoquais plus haut le fait que c'était surtout la lumière jaune qui nous parvenait du Soleil (même si elle est blanche à l'origine). Cependant, il me parait possible que le bleu du ciel ait son influence en renvoyant un peu de lumière bleue sur nous...</li>
</ul>
Autre question&nbsp;: à partir de quel point la droite d'une teinte devrait diverger ? J'ai deux hypothèses là-dessus, mais aucune preuve concrète pour l'étayer&nbsp;:<br/>
<ul>
<li>à 255°&nbsp;: c'est l'opposé au 75° vers lequel on tend. Ainsi, un angle supérieur à 255° se rapprochera plus rapidement du 75° en tournant dans le sens horaire plutôt qu'antihoraire.</li>
<li>à 270°&nbsp;: en réalité, les angles utilisés pour définir une teinte vont de 0° à 360°, allant du rouge au jaune, puis au vert, au bleu, au violet, avant de revenir au rouge. Pourtant, le spectre visible (et les-arcs-en-ciel) ne vont pas aussi loin, et s'arrêtent dans les bleus.<br/>
Alors comment peut-on expliquer le violet ? Il s'agit tout simplement d'un mélange de bleu et de rouge. Un mélange de 2 longueurs d'onde. Dans ce cas, la logique sur la variation de teinte n'est peut-être pas vraiment valable entre 270° et 259°...</li>
</ul>

<p>EDIT&nbsp;: et je compte réutiliser toute cette logique dans une variante de l'Anime Girl Generator, réalisée en C++/Qt, cette fois-ci.<br/>
Des nouvelles sur le sujet d'ici quelques jours.</p>


feed_id: http://www.yosko.net/?d=2013/04/25/14/00/00-ddb-derniere-version-majeure-v14
title: DDb dernière version majeure (v1.4)
uri: DDb-dernière-version-majeure-(v1-4)
url: http://www.yosko.net/?d=2013/04/25/14/00/00-ddb-derniere-version-majeure-v14
content:
<b></b><br/><img class="right" src="media/34e0ae93.ddb-logo.png" data-original-source="http://www.yosko.net/img/ddb-logo.png" />
Votre gestionnaire de rêves préféré est de retour pour vous jouer un mauvais tour.<br/>
<br/>
C'est probablement la dernière fois que je vous en parle avant longtemps, car j'estime avoir développé l'essentiel des fonctionnalités que je voulais. Je continuerai bien évidemment à poster des correctifs sur GitHub, et reste ouvert aux suggestions, mais l'essentiel est là.<br/>
<br/>
<h3>Téléchargement</h3>
Vous pouvez récupérer DDb depuis l'une de ces deux sources&nbsp;:<br/>
<ul>
  <li><a href="https://github.com/yosko/ddb">DDb sur Github</a>&nbsp;: les dernières versions à jour</li>
  <li><a href="http://dev.yosko.net/wiki/doku.php?id=web:php:ddb">DDb sur mon wiki</a>&nbsp;: changelog et téléchargement de versions plus anciennes</li>
</ul>
<h3>Nouveautés de la 1.4</h3>
<ul>
    <li>Possibilité de commenter les rêves (+ flux RSS des commentaires)</li>
    <li>Ajout d'un statut publié/brouillon aux rêves</li>
    <li>Utilisation de la date de dernière publication pour le flux RSS (si vous voulez notifier vos camarade d'un gros changement sur un rêve, il suffit de le dépublier et de le republier)</li>
    <li>Possibilité d'insérer des liens/images/smiley/mise en forme (syntaxe inspirée de Blogotext)</li>
    <li>Mise à jour de YosLogin</li>
    <li>Amélioration des filtres de la liste des rêves</li>
    <li>Affichage de l'auteur (utilisateur) pour chaque rêve</li>
    <li>Possibilité d'ajouter des tags avec icônes supplémentaires</li>
    <li>Sécuriastion de l'accès à la base et aux fichiers de session via des .htaccess</li>
    <li>Liste des tags&nbsp;: affichage pondéré en fonction de la fréquence d'utilisation</li>
    <li>Fusion de tags (via l'administration)</li>
    <li>Affichages de statistiques sur la liste des rêveurs</li>
    <li>Et de nombreuses corrections mineures (dont le lien KO du RSS)</li>
</ul>
<h3>Qu'est-ce que je deviens ?</h3>
Je ne poste plus beaucoup en ce moment. Comme je vous l'ai dit, je bosse sur pas mal de trucs que je vous montrerai plus tard, dont&nbsp;:<br/>
<ul>
  <li>Watamelo, un Framework PHP MVC&nbsp;: les sources du premier jet sont dès à présent disponibles sur <a href="https://github.com/yosko/watamelo">la page GitHub de Watamelo</a>. Je ne le mettrai probablement pas à jour avant 2 ou 3 mois, le temps de finir le reste.</li>
  <li>Un site web perso (à sources fermées) qui m'occupe à plein temps depuis un moment. Il se base sur Watamelo, et c'est la raison principale de mon silence actuel</li>
  <li>Un projet de CMS open-source qui utilisera lui aussi Watamelo (et aidera à le faire évoluer) que j'aborderai par la suite. Je vous en dirai un peu plus à la fin de l'été</li>
</ul>
Comme toujours, si vous avez des questions, n'hésitez pas à commenter ou me mailer&nbsp;:)<br/>
<br/>
Keep being awesome and code on!


feed_id: http://www.yosko.net/
title: Migration des flux
uri: Migration-des-flux
url: http://www.yosko.net/
content:
<p>Ce flux RSS n'est plus actif depuis la dernière mise à jour du site. Merci de vous rendre sur <a href="http://www.yosko.net/">Yosko.net</a> pour accéder à la nouvelle version.</p>


