Jekyll2023-12-28T10:17:37+01:00https://www.j0k3r.net/feed.xmlj0k3r.netJérémy BenoistFaire revivre les ThermoPeanut de Sense grâce à Bref et React Native2023-09-07T20:41:34+02:002023-09-07T20:41:34+02:00https://www.j0k3r.net/faire-revivre-thermometre-sense-bref-react-native-expo<p><em>Note : j’ai créé ce projet y’a 3 ans (en aout 2019), j’ai commencé à écrire cet article en février 2020 et depuis j’ai tout le temps repousser le moment de le finir, jusqu’à aujourd’hui ! :)</em></p>
<p>Fin 2015, la startup française Sense lance plusieurs <a href="https://www.iphon.fr/post/capteurs-peanut-autonomes-sense-841214">appareils connectés</a>. Dans le lot, il y en a un qui permet de recueillir la température : le <strong>ThermoPeanut</strong>. Il collecte la température, une application mobile récupère ses informations via le Bluetooth, les envoie vers leurs serveurs et affiche ensuite de belles courbes.</p>
<p>Mais deux ans plus tard, fin 2017, <a href="https://www.mac4ever.com/actu/129742_sen-se-la-fin-des-peanuts">la startup est en liquidation</a>. Les serveurs ont commencé à fonctionner par intermittence. Et début 2018, ils ont été coupés. L’application permet toujours d’afficher la température actuelle mais les graphiques ne sont plus mis à jour.</p>
<p>Ne voulant pas rester avec mes 4 Thermo Peanuts inutiles, j’ai commencé à chercher une solution.</p>
<p>Les objectifs étaient :</p>
<ol>
<li>de garder mes thermomètres</li>
<li>d’avoir une application iOS avec des graphiques</li>
<li>que ça ne coute rien (ou presque rien) au fonctionnement</li>
</ol>
<h2 id="lire-les-données">Lire les données</h2>
<h3 id="récupérer-directement-les-données-depuis-les-objets-connectés-">Récupérer directement les données depuis les objets connectés ❌</h3>
<p>L’avantage serait évidemment de lire directement les infos en Bluetooth. Problème, je n’ai pas réussi à établir une connection avec les devices.
J’ai trouvé des vieux projets en JS qui permettait de le faire depuis une page web. Mais évidemment, ils ne fonctionnent plus.
En ligne de commande directement, impossible de les trouver non plus. Même en ayant leur adresse MAC (trouvée en sniffant les appels réseaux de l’app), impossible de m’y connecter.</p>
<p>Échec.</p>
<h3 id="changer-lurl-de-lapi-utilisée-dans-lapplication-">Changer l’URL de l’API utilisée dans l’application ❌</h3>
<p>L’iPhone faisant tourner l’application est un 5c jailbreaké (l’app est en 32 bits). J’ai récupéré le code de l’application et j’ai commencé à regarder pour changer cette url. Mais impossible de trouver une variable qui contiendrait cette information.</p>
<p>Nouvel échec.</p>
<h3 id="proxifier-les-appels-vers-lapi-">Proxifier les appels vers l’API ✅</h3>
<p>Dernière solution, intercepter les appels à l’API via un proxy.</p>
<p>En jouant avec Charles Proxy, j’ai pu récupérer les données envoyées au serveur par l’application et les interpréter.</p>
<p>C’était gros fichier JSON avec plein d’infos différentes non-encore synchronisées avec le serveur :</p>
<ul>
<li>l’état de la batterie (<code class="language-plaintext highlighter-rouge">battery</code>)</li>
<li>la température (<code class="language-plaintext highlighter-rouge">temperature</code>)</li>
<li>l’état du thermomètre (connecté ou non) (<code class="language-plaintext highlighter-rouge">presence</code>)</li>
</ul>
<p>J’ai résumé les 3 types mais le fichier en contenait des centaines.</p>
<div class="language-json highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">[</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"gatewayNodeUid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5qQBoq9AJwhxdLbBdIqtVg8vlz7cGWsq"</span><span class="p">,</span><span class="w">
</span><span class="nl">"signal"</span><span class="p">:</span><span class="w"> </span><span class="mi">-96</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"code"</span><span class="p">:</span><span class="w"> </span><span class="mi">200</span><span class="p">,</span><span class="w">
</span><span class="nl">"body"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Present"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"presence"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dateEvent"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2018-06-03T19:38:04.761Z"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"gatewayNodeUid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5qQBoq9AJwhxdLbBdIqtVg8vlz7cGWsq"</span><span class="p">,</span><span class="w">
</span><span class="nl">"signal"</span><span class="p">:</span><span class="w"> </span><span class="mi">-83</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"centidegreeCelsius"</span><span class="p">:</span><span class="w"> </span><span class="mi">2416</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"temperature"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dateEvent"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2018-06-03T19:41:30.131Z"</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="p">{</span><span class="w">
</span><span class="nl">"gatewayNodeUid"</span><span class="p">:</span><span class="w"> </span><span class="s2">"5qQBoq9AJwhxdLbBdIqtVg8vlz7cGWsq"</span><span class="p">,</span><span class="w">
</span><span class="nl">"signal"</span><span class="p">:</span><span class="w"> </span><span class="mi">-83</span><span class="p">,</span><span class="w">
</span><span class="nl">"data"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span><span class="w">
</span><span class="nl">"levelMillivolt"</span><span class="p">:</span><span class="w"> </span><span class="mi">3000</span><span class="w">
</span><span class="p">},</span><span class="w">
</span><span class="nl">"type"</span><span class="p">:</span><span class="w"> </span><span class="s2">"battery"</span><span class="p">,</span><span class="w">
</span><span class="nl">"dateEvent"</span><span class="p">:</span><span class="w"> </span><span class="s2">"2018-06-03T19:41:30.131Z"</span><span class="w">
</span><span class="p">}</span><span class="w">
</span><span class="p">]</span><span class="w">
</span></code></pre></div></div>
<h2 id="mise-en-place-du-proxy">Mise en place du proxy</h2>
<p>Inconvénient à cette solution : il faut une tierce personne pour faire le proxy. Il faut rediriger le trafic allant vers <code class="language-plaintext highlighter-rouge">https://app-00.sen.se</code> vers une autre url.</p>
<p>Pour faire ça, j’ai acheté un Raspberry Zéro. J’ai installé Charles Proxy et avec un simple « Map Remote » le boulot était fait.</p>
<p><img src="https://user-images.githubusercontent.com/62333/64076153-79755d80-ccc1-11e9-9772-bfd61f2e0e45.png" alt="image" /></p>
<p>J’avais essayé plusieurs autres solutions dont <a href="https://github.com/suyashkumar/ssl-proxy">ssl-proxy</a>, <a href="https://www.apsis.ch/pound.html">pound</a>, <a href="https://mitmproxy.org/">mitmproxy</a> et <a href="http://hiproxy.org/">hiproxy</a> sans succès. Il faudrait que je me re-penche dessus car Charles Proxy est trop gourmand en RAM et fait planter le Raspberry de temps en temps.</p>
<h2 id="mise-en-place-des-données-via-une-api">Mise en place des données via une API</h2>
<p>Le proxy en place, je me suis dit que la façon la plus simple et rapide d’avoir une API c’était de passer par AWS avec une Lambda appelée via une API Gateway. Je connais déjà très bien cet environnement avec des lambdas en Node.js mais je me suis dit que <strong>c’était l’occasion de tester <a href="https://bref.sh/">BrefPHP</a></strong>.</p>
<p>L’application est plutôt basique :</p>
<ul>
<li>un endpoint pour enregistrer les données venant de l’application.
Par exemple, l’enregistrement d’un relevé de température :
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nv">$this</span><span class="o">-></span><span class="n">influx</span><span class="o">-></span><span class="nf">writePoints</span><span class="p">([</span>
<span class="k">new</span> <span class="nc">\InfluxDB\Point</span><span class="p">(</span>
<span class="s1">'temperature'</span><span class="p">,</span>
<span class="mi">2416</span><span class="p">,</span>
<span class="p">[</span><span class="s1">'mac'</span> <span class="o">=></span> <span class="s1">'00:0A:95:9D:68:16'</span><span class="p">],</span>
<span class="p">[],</span>
<span class="mi">1528055638198</span>
<span class="p">),</span>
<span class="p">],</span> <span class="nc">\InfluxDB\Database</span><span class="o">::</span><span class="no">PRECISION_MILLISECONDS</span><span class="p">);</span>
</code></pre></div> </div>
</li>
<li>un endpoint pour lister les thermomètres connectés</li>
<li>un endpoint pour afficher les détails d’un thermomètre
Par exemple, la requête vers InfluxDB pour récupérer la moyenne sur les 30 dernières minutes :
<div class="language-sql highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">SELECT</span> <span class="n">MEAN</span><span class="p">(</span><span class="nv">"value"</span><span class="p">)</span><span class="o">/</span><span class="mi">100</span> <span class="k">FROM</span> <span class="nv">"temperature"</span> <span class="k">WHERE</span> <span class="nb">time</span> <span class="o">></span> <span class="n">now</span><span class="p">()</span> <span class="o">-</span> <span class="mi">24</span><span class="n">h</span> <span class="k">AND</span> <span class="nv">"mac"</span><span class="o">=</span><span class="s1">'00:0A:95:9D:68:16'</span> <span class="k">GROUP</span> <span class="k">BY</span> <span class="nb">time</span><span class="p">(</span><span class="mi">30</span><span class="n">m</span><span class="p">)</span>
</code></pre></div> </div>
</li>
</ul>
<p>L’application devant fournir des graphiques sur plusieurs échelles (dernières 24h, 30 derniers jours et 12 derniers mois) <strong>je me suis tourné vers InfluxDB</strong> pour le stockage des métriques. AWS ne proposant pas encore de base de données de type « time serie », j’ai une petite instance EC2 pour InfluxDB uniquement (ndlr: ils proposent maintenant AWS Timestream, mais c’est beaucoup trop cher).</p>
<p>Enfin, pour le stockage des différents thermomètres avec les dernières valeurs et leur nom, <strong>j’ai opté pour une table dans DynamoDB</strong> pour la simplicité d’utilisation et le coût minime.</p>
<p>Le <a href="https://github.com/j0k3r/thermo-proxy-aws">code est disponible sur GitHub</a>.</p>
<h2 id="affichage-des-données-depuis-une-application-en-react-native">Affichage des données depuis une application en React Native</h2>
<p>La finalité de tout ça, c’est quand même de pouvoir consulter la température depuis n’importe où avec mon téléphone.</p>
<p>J’aurais pu partir sur une simple application en ligne de type PWA mais je me suis dit que c’était l’occasion de tester quelque chose de nouveau. <strong>J’ai opté pour une application en React Native en utilisant la plateforme <a href="https://expo.dev/">Expo</a></strong>.</p>
<p>Je suis rapidement arrivé à une interface concluante pour le listing. J’ai surtout refait l’interface de l’ancienne application de Sense pour ne pas trop être perdu (et puis le design, c’est pas mon truc).
Pour les graphiques, j’ai utilisé la librairie Victory qui permet d’avoir le choix dans le rendu.</p>
<p><img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/thermo-app/home_light.jpg" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/thermo-app/home_light.jpg 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_468/https://www.j0k3r.net/assets/images/thermo-app/home_light.jpg 468w" sizes="100vw" alt="La home en mode jour" width="468" height="1013" crossorigin="anonymous" />
<img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/thermo-app/view_dark.jpg" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/thermo-app/view_dark.jpg 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_468/https://www.j0k3r.net/assets/images/thermo-app/view_dark.jpg 468w" sizes="100vw" alt="Les graphiques d'un thermomètre en mode sombre" width="468" height="1013" crossorigin="anonymous" />
<img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/thermo-app/view_light.jpg" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/thermo-app/view_light.jpg 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_468/https://www.j0k3r.net/assets/images/thermo-app/view_light.jpg 468w" sizes="100vw" alt="Les graphiques d'un thermomètre en mode jouer" width="468" height="1013" crossorigin="anonymous" /></p>
<p>Le <a href="https://github.com/j0k3r/thermo-app">code est disponible sur GitHub</a>.</p>
<h2 id="le-bilan">Le bilan</h2>
<p>Une fois tout ça en place je peux maintenant connaître (à nouveau) la température qu’il fait dans les différentes pièces de mon appartement depuis n’importe où et avoir les graphiques pour connaître l’historique.</p>
<p>Les seuls bémols :</p>
<ul>
<li>le téléphone faisant tourner l’application Sense doit toujours être allumé</li>
<li>l’application iOS n’est pas compatible 64 bits (donc incompatible avec iOS > 11)</li>
<li>le proxy depuis le Raspberry doit aussi être tout le temps allumé</li>
</ul>
<p>Mais ce petit projet m’a permis de :</p>
<ul>
<li>découvrir la simplicité avec laquelle il est possible de faire fonctionner une application PHP dans une Lambda grâce à Bref (bravo Matthieu !!)</li>
<li>découvrir la puissance et la simplicité d’InfluxDB pour stocker des données pour faire des graphiques</li>
<li>jouer avec Expo pour faire une application iOS en React Native</li>
<li>jouer avec une table DynamoDB très basique</li>
</ul>
<p>La seule dépense que j’ai faite dans tout ça (outre l’achat des thermomètres en 2015) c’est le Raspberry Pi Zéro puisque tout le reste tourne sur AWS et que grâce aux <a href="https://www.j0k3r.net/recevoir-1200-dollars-credit-aws">crédits AWS</a> annuel que j’ai, elle ne me coûte rien !</p>j0kComment j'ai bidouillé une API en PHP avec une appli iOS en React Native pour ne pas jeter mes thermomètres connectés suite à la fermeture des serveurs de la boite qui les fournissait. Je me suis bien amusé alors je vous partage tout ça !Comment recevoir 1200$ de crédit par an pour s’amuser sur AWS2020-11-03T09:38:34+01:002020-11-03T09:38:34+01:00https://www.j0k3r.net/recevoir-1200-dollars-credit-aws<p>Au boulot, nous sommes en pleine migration vers le cloud public d’AWS. En vrai, il ne reste que les vieux vieux projets sur le cloud privé.</p>
<p>En jouant beaucoup avec AWS au travail, je découvre plein de nouvelles possibilités, de nouvelles facilitées, de nouvelles manières de travailler, etc. Par exemple, le fait de pouvoir scripter son infra avec du code, c’est vraiment pratique.</p>
<p>A tel point que bien souvent maintenant, j’utilise aussi AWS pour mes projets perso (dans la mesure du possible).</p>
<p>Mais comme tout le monde le sait, (la console) AWS est facile à utiliser et on arrive rapidement à déployer plusieurs services sans vraiment savoir combien cela va coûter à la fin (et on a souvent des surprises).
Il existe évidemment des services pour estimer la facture finale mais je les ai toujours trouvés trop difficiles à paramétrer pour avoir une vraie estimation.</p>
<h2 id="lastuce">L’astuce</h2>
<p>C’est là qu’entre en jeu <strong>une petite astuce pour avoir des crédits AWS</strong> de façon à limiter les coûts. Je parle ici d’un petit plus, pas d’un moyen de faire tourner l’infra complète d’un gros site gratos.</p>
<p><strong>AWS vous offre 100$ par mois</strong>, sans limite de temps, pour couvrir les frais que pourrait engendrer une skill Alexa.
Et il existe une façon très simple d’y arriver : créer une <a href="https://developer.amazon.com/en-US/docs/alexa/flashbriefing/understand-the-flash-briefing-skill-api.html">Flash Briefing Skill</a>.</p>
<p>Trois choses sont nécessaires :</p>
<ul>
<li>un compte AWS</li>
<li>une facture supérieure à 0$ sur AWS</li>
<li>un flux RSS (dont la dernière publication date de moins de 7 jours). <em>Pour info, un flux ATOM ne fonctionnera pas.</em></li>
</ul>
<p>Prenez votre flux RSS et faites-en une skill.</p>
<h2 id="la-mise-en-place">La mise en place</h2>
<p>Connectez-vous sur <a href="https://developer.amazon.com/alexa/console/ask">la console Alexa</a> avec vos identifiants Amazon (mon compte perso Amazon est le même que mon compte AWS) :</p>
<ol>
<li>Cliquez sur le bouton “<em>Create Skill</em>”.</li>
<li>Choisissez de créer un flash briefing.</li>
<li>Votre skill est créée, il faut maintenant renseigner votre flux RSS (Cliquez sur “<em>Add new feed</em>”).</li>
<li>Puis renseignez tout le formulaire (n’hésitez pas à passer votre souris sur les ? pour savoir quoi mettre).
<img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/flash-briefing-form.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/flash-briefing-form.png 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_540/https://www.j0k3r.net/assets/images/flash-briefing-form.png 540w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_760/https://www.j0k3r.net/assets/images/flash-briefing-form.png 760w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_980/https://www.j0k3r.net/assets/images/flash-briefing-form.png 980w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/flash-briefing-form.png 1200w" sizes="100vw" alt="Flash Briefing formulaire" width="2688" height="1450" crossorigin="anonymous" /></li>
<li>Une fois que vous avez sauvegardé, passer sur l’onglet “<em>Distribution</em>” et remplissez le formulaire.</li>
<li>Enfin passez sur l’onglet “<em>Certification</em>” pour lancer une validation. Corrigez éventuellement les erreurs.</li>
<li>Si aucune erreur n’est trouvée, vous pouvez passer sur le sous menu “<em>Submission</em>” à gauche.
<img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_540/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png 540w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_760/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png 760w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_980/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png 980w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/skill-ready-for-submission.png 1200w" sizes="100vw" alt="Skill ready to be submitted" width="1964" height="1034" crossorigin="anonymous" /></li>
</ol>
<p>La validation par les équipes d’Alexa peut prendre un peu de temps et vous serez sûrement sollicité pour faire des changements dans votre code.</p>
<p>Quand votre skill sera validée, vous pouvez soumettre la demande de crédits <a href="https://developer.amazon.com/en-US/alexa/alexa-skills-kit/new/aws-promotional-credits#application">depuis cette page</a>.</p>
<p>Une fois confirmé, vous recevrez le mois suivant (aux alentours du 15) un mail vous disant que votre compte AWS a été crédité de 100$ parce que votre skill a généré des frais AWS (même si votre skill n’est pas hébergée sur AWS et que les frais générés n’ont rien à voir avec votre skill).</p>
<p><img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/mail-aws-credit.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/mail-aws-credit.png 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_540/https://www.j0k3r.net/assets/images/mail-aws-credit.png 540w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_760/https://www.j0k3r.net/assets/images/mail-aws-credit.png 760w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_980/https://www.j0k3r.net/assets/images/mail-aws-credit.png 980w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/mail-aws-credit.png 1200w" sizes="100vw" alt="Email d'AWS indiquant le gain de crédit" width="1354" height="580" crossorigin="anonymous" /></p>
<h2 id="profit">Profit</h2>
<p>À noter que vous recevrez aussi 100$ de plus la première fois.</p>
<p><strong>Ce qui fait 1200$ de crédit par an dont 1300$ la première année. Ça vous laisse de quoi jouer tranquille !</strong></p>
<p>Par exemple, ce que j’utilise actuellement pour mes projets persos ne me coûte rien :</p>
<ul>
<li>Plusieurs domaines dans Route 53 (parce que c’est quand même chiant de gérer bind à la main)</li>
<li>Quelques EC2 nano / small</li>
<li>Beaucoup de lambdas</li>
<li>Quelques tables DynamoDB</li>
<li>Un RDS MySQL</li>
<li>Quelques buckets S3 pour des backups</li>
</ul>j0kSi comme moi vous aimez bien mettre vos projets persos sur AWS (parce que c'est quand même plus pratique que d'avoir un serveur dédié à gérer), j'ai une petite astuce pour avoir des 1200$ de crédits AWS par an. Il suffit de créer une Flash Briefing Skill Alexa et le tour est joué !10+ calendriers de l’avent à suivre2010-12-06T13:33:40+01:002010-12-06T13:33:40+01:00https://www.j0k3r.net/10-calendriers-de-l-avent-a-suivre<p>Je l’avais déjà fait y’a deux ans (déjà) mais je trouve la chose intéressante !</p>
<p>Voilà une liste de calendriers de l’avent que je suis cette année (enfin, en partie). J’en ai découvert des nouveaux aussi. Ils sont principalement orientés développement web mais aussi quelques uns sur l’iPhone (des apps gratos ça fait toujours plaisir !).</p>
<p>Alors oui, je sais, on est déjà le 6 décembre, mais disons que comme ça, en découvrant ces calendriers vous avez déjà un avant gout de ce qu’il vous réserve puisque vous avez 6 jours à dévorer !!</p>
<ol>
<li>
<p>Le plus populaire (à mon avis): <a href="http://24ways.org/2010">24ways</a> (<a href="http://twitter.com/24ways">@24ways</a>)</p>
<p>C’est la sixième année que 24 ways propose un calendrier de l’avent. Il est le plus connu je pense parce qu’il rassemble beaucoup de conseils, d’astuces et de petit script qui collent beaucoup avec l’actualité. Bien souvent ceux sont des posts majoritairement orientés CSS & Javascript. Je me rappelle très bien en 2005 m’être beaucoup aidé des tutoriels sur Ajax avec prototype (notamment sur le edit-in-place).</p>
<p>Cette année vous avez la possibilité d’acheter un livre avec tous les articles à l’intérieur, tous les profits iront à l’unicef. Le bouquin coute 8£.</p>
</li>
<li>
<p><a href="http://phpadvent.org/2010">phpAdvent</a> (@<a href="http://twitter.com/phpadvent">phpadvent</a>)</p>
<p>Lancé par Chris Shiflett en 2008, il regroupe des posts sur le côté dynamique: php & les bases de données.</p>
<p>A ma connaissance, c’est le seul calendrier qui traite de PHP.</p>
</li>
<li>
<p><a href="http://calendar.perfplanet.com/2010/">webperf</a> ! (@<a href="http://twitter.com/perfplanet">perfplanet</a> noyé dans d’autres liens (c’est un fil planet))</p>
<p>On parle beaucoup de performance en ce moment, entre les ateliers de parisweb et les séances un peu partout en france, c’est un sujet qui trend !</p>
<p>C’est la deuxième année que ce calendrier existe, mais je ne le découvre que cette année. A suivre !</p>
</li>
<li>
<p><a href="http://html5advent.com/">HTML5</a></p>
<p>HTML5 est toujours en développement mais déjà beaucoup l’utiliser et nous montre sa puissance (ça me fait un penser à Symfony2 ce que je viens de dire tiens). Premiere année pour ce calendrier qui présentera des applis, des tutoriels, des démos uniquement en HTML5</p>
</li>
<li>
<p><a href="http://www.jamieleung.co.uk/experiments/css3advent/">CSS3</a></p>
<p>Après avoir parlé d’HTML5, la suite logique c’est d’avoir un calendrier CSS 3. Il est mené à bien par Jamie Leung et je pense qu’il présente des articles ou des sites déjà existants qui traitent de CSS 3. Des découvertes en perspective en tout cas.</p>
</li>
<li>
<p><a href="http://perladvent.pm.org/2010/">Perl !</a></p>
<p>Attention on rentre un peu plus dans le code là, on délaisse la présentation sympa et les bords arrondis, voici le calendrier dédié à Perl.</p>
<p>On chercher l’efficacité avant tout !</p>
</li>
<li>
<p><a href="http://www.designisphilosophy.com/24-days-of-wordpress/24-days-of-wordpress-the-wordpress-advent-calendar/">24 jour avec Wordpress</a> (<a href="http://twitter.com/mor10">@mor10</a>)</p>
<p>Ah ben oui, on parle maintenant un peu plus spécifique. Pour les utilisateurs de Wordpress vous pouvez suivre ce calendrier. Selon son auteur, ça parlera de sécurité, de plugins, de menus, etc …</p>
<p>Première année pour lui aussi !</p>
</li>
<li>
<p><a href="http://24waystostart.com/2010/">Le calendrier des entrepreneurs</a> (<a href="http://twitter.com/24WaysToStart">@24WaysToStart</a>)</p>
<p>Celui ci change de sujet par rapport aux précédents. Ici, on lache un peu Eclipse pour s’orienter sur le côté business. Ce calendrier présente des astuces et des retours d’expériences sur l’entreprenariat.</p>
</li>
<li>
<p><a href="http://appventcalendar.com/">Apps & Jeux iPhone</a> (<a href="http://twitter.com/blacksmithgames">@blacksmithgames</a>)</p>
<p>On passe aux choses gratos que tout le monde apprécie ! Blacksmith games propose une application et un jeu iPhone gratuit chaque jour. En plus de ça, ils ont mis en place un concours qui consiste à faire le guignol devant une caméra, la poster sur Youtube, recevoir un max de vues et le tout pour gagner des lots plus flex comme un MBA 13”, un iPad, un ARDrone, etc ..</p>
</li>
<li>
<p><a href="http://blog.gameloft.com/index.php/2010/11/29/twitter-advent-calendar/">Des jeux gratuit chez Gameloft</a> (<a href="http://twitter.com/gameloft">@gameloft</a>)</p>
<p>Ils se prennent aussi au jeu et proposerons chaque jour des jeux gratuits sur iPhone, iPad, Android, Facebook.
Pour voir les différents jours, il faut les suivre sur twitter, il n’y a pas de site dédié.</p>
</li>
<li>
<p>(bonus) <a href="http://www.boston.com/bigpicture/2010/12/2010_hubble_space_telescope_ad.html">Et pour finir notre cher Hubble</a> (<a href="http://twitter.com/big_picture">@big_picture</a>)</p>
<p>Chaque année aussi Hubble est à l’honneur via BigPicture puisque une photo (vieille ou récente) est mise en avant sur leur site.
Même si des fois on ne comprends pas très bien ce qu’elles représente, elles sont jolies :-)</p>
</li>
</ol>
<p>Si vous connaissez d’autre calendrier sympa, partagez les !</p>j0kFin d'année oblige, on voit fleurir des calendriers de l'avent un peu partout. Je vous en propose une dizaine (webdesign, php, html5, css3, iPhone, etc ..), en espérant vous en faire découvrir quelques uns !Chez @pixmania, les produits avec des prix massacrés ne sont jamais expédiés …2010-06-13T13:03:14+02:002010-06-13T13:03:14+02:00https://www.j0k3r.net/chez-pixmania-les-produits-avec-des-prix-massacres-ne-sont-jamais-expedies<p>Red Dead Redemption, tout le monde a certainement entendu parler de ce titre en décembre dernier quand <a href="http://www.jeuxvideo.com/reportages-videos-jeux/0000/00000436/red-dead-redemption-serie-de-gameplay-introduction.htm#containerlienVoirDirectementlaVideo">la première vidéo est sortie</a>. Perso, je suis resté bouche bée devant le gameplay … Il faudra attendre le 21 mai pour l’acheter, argh!</p>
<h2 id="mi-mai">Mi-mai</h2>
<p>Avant de partir en vacances, j’entendais déjà parler <a href="http://www.fnac.com">au boulot</a> de personnes qui avaient précommandé le jeu, avec des prix variant entre 60 et 75€. Ça fait cher quand même pour un jeu .. Fnac.com a notamment fait une promotion juste au lancement avec un prix à 65, il me semble.</p>
<h2 id="mardi-1er-juin">Mardi 1er juin</h2>
<p>8h. J’ai reçu une newsletter de PIXmania dans la nuit avec plein de promos dedans (comme d’hab’) mais j’en remarque une qui me semble bien intéressante: Red Dead Redemption à -23%, soit 50€ ! 15€ de moins que sur le marché. Le jeu est un poil plus cher qu’en U, où il était à vendre à 47€.</p>
<p><img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/box_view_pix_1.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_311/https://www.j0k3r.net/assets/images/box_view_pix_1.png 311w" sizes="100vw" alt="Red Dead Redemption à -23%, soit 50€" width="311" height="186" crossorigin="anonymous" /></p>
<p>Ni une, ni deux, je vais sur le site, le produit est marqué en stock livraison en 24/48h. Parfait. Ma CB étant bloquée suite à mon retour de vacances un peu couteux, je choisis la livraison en point retrait PIXmania (à 2 minutes du boulot, top !) et de payer à la réception.</p>
<p>Je confirme ma commande par mail puisque c’est ce qu’il faut faire pour le retrait en point retrait. On me dit que je recevrais un SMS et un mail quand le colis sera arrivé, cool !
A moi les joies de galoper au Mexique en poursuivant des diligences dans 2 jours au plus !</p>
<h2 id="vendredi-4-juin">Vendredi 4 juin</h2>
<p>Ça fait 3 jours que j’ai passé ma commande et toujours aucune nouvelle. Dans le détail de la commande, mon colis est passé en “préparation” et dans l’encart “préparation”, la colonne “dispo actuelle” indique le 7 juin … Ce que je comprends, c’est que mon RDR n’est pas au point retrait et qu’il y sera normalement lundi prochain. Bon, on va dire qu’ils ont croulé sous les ventes de RDR vu l’offre aux rabais qu’ils ont fait. Attendons un peu plus …</p>
<h2 id="lundi-7-juin">Lundi 7 juin</h2>
<p>Ma commande est toujours en “préparation” et la date de dispo actuelle est toujours indiqué à aujourd’hui, même si le stock indiqué au point retrait est lui toujours à zéro.</p>
<p>En fin de journée, je retourne voir mon fameuse commande et là, tiens tiens tiens, la date de dispo actuelle a encore changé, elle indique maintenant le 10 juin !</p>
<p>Je sens que je me fais pigeonner là … A noter que le produit est toujours en stock sur le site, livraison 24/48h et à 49.99 €.</p>
<h2 id="mardi-8-juin">Mardi 8 juin</h2>
<p>9h, la dispo est toujours indiquée pour le 10 juin. Ça me soule, je claque un mail au service client pour savoir où est mon RDR et pourquoi la date de dispo se décale tous les jours.</p>
<p>Réponse automatique :</p>
<blockquote>
<p><strong>Note</strong>
Votre message est bien enregistré !
Nous vous confirmons la transmission
de votre demande au service clients,
qui se chargera de vous répondre sous
24h.</p>
</blockquote>
<p>Bon, chouette, j’aurais enfin une réponse dans la journée, au pire, demain matin !</p>
<h2 id="jeudi-10-juin">Jeudi 10 juin</h2>
<p>Toujours aucune réponse du service client PIXmania, si je compte bien, ça fait 48h là déjà. Le produit est toujours en dispo actuelle le 10/06, mais aucun stock au point retrait.</p>
<p>Et puis là, le <a href="http://twitter.com/pixmania/status/15837080706">twitter de pixmania se réveille</a> ! Je réponds que j’attends ma commande depuis le 01/06 … Joie, on me réponds qu’une personne va entrer en contact avec moi.</p>
<p><img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/gallery_view_pix_2.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_320/https://www.j0k3r.net/assets/images/gallery_view_pix_2.png 320w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_540/https://www.j0k3r.net/assets/images/gallery_view_pix_2.png 540w, https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_600/https://www.j0k3r.net/assets/images/gallery_view_pix_2.png 600w" sizes="100vw" alt="Réponse de PIXmania sur twitter" width="600" height="215" crossorigin="anonymous" /></p>
<p>Noëlla me demande la “référence CCL” (le numéro de commande). En fin de journée, j’ai une réponse qui fait rire (jaune) :</p>
<blockquote>
<p><strong>Note</strong>
Rupture de stock nationale. Problème
de livraison avec le fournisseur. Nous
en avons reçu cette semaine. Les
envois repartent d’ici la fin de la
semaine</p>
</blockquote>
<p>Là on y est. <strong>Rupture de stock nationale</strong>. Je vais direct sur la fiche produit et non, le produit est toujours indiqué “en stock”, “livraison 24/48h”. Là ça commence à m’énerver …</p>
<p>Je réponds tout simplement :</p>
<blockquote>
<p><strong>Note</strong>
Merci pour ces informations! Comment
se fait-il que le produit n’est jamais
changé de statut et soit tout le tps
resté “en stock 24/48h” ?</p>
</blockquote>
<p>ahah, touché ?
Eh bah non, la réponse est complètement à côté et noie le poisson … :</p>
<blockquote>
<p><strong>Note</strong>
On vient de me confirmer que le
produit partira demain ;)</p>
</blockquote>
<p>Je reste courtois et je remercie cette brave demoiselle d’avoir pris sur son temps de rechercher des infos sur ma commande.</p>
<p>Entre temps, je suis allé voir sur le site de la Fnac voir si le produit était réellement en <em>rupture nationale</em>. Sans trop d’étonnement, je vois que le produit est dispo en 24/48h mais affiché à 59.99€. Là, j’ai confiance, je sais que le produit est dispo (même si je suis allé vérifier en interne). Comme j’ai choisi la livraison en point retrait avec paiement sur place, je n’ai rien payé chez PIX et si je ne vais pas chercher le produit dans les 48h il repart. C’est salop, mais j’ai rien à perdre!
Je passe donc ma commande à la Fnac et au final je m’en tire pour 46€ ! (oui, c’est un avantage). Expédition prévue le 11/06, parfait !</p>
<h2 id="vendredi-11-juin">Vendredi 11 juin</h2>
<p>Tâche quotidienne du matin: où en est ma commande PIXmania ? Le produit est maintenant “en stock” au niveau de la colonne “dispo actuelle” mais toujours pas de quantité dans mon point retrait.</p>
<p>Le produit doit normalement partir aujourd’hui de chez PIX, je vais attendre ce soir voir si le statut de ma commande change.</p>
<p>Pendant ce temps là, ma commande Fnac est expédiée, comme annoncée.</p>
<h2 id="samedi-12-juin">Samedi 12 juin</h2>
<p><em>Là, c’est la partie la plus poilante de l’histoire.</em></p>
<p>Je vais voir l’état de ma commande PIX, qui n’a pas changé en elle-même. Par contre, y’a un détail qui a changé: le nom du jeu. Avant, il était affiché simplement <em>Red Dead Redemption [PS3]</em> et maintenant je vois <strong>Red Dead Redemption [PS3] (IMPORT UK)</strong> …</p>
<p>Alors bon, si je voulais le commander en UK, je l’aurai fait moi même sans passer commande chez PIX puisque le prix était moins cher. Mais ayant une expérience dans les achats UK, je sais qu’ils sont longs à arriver. Je me suis dit qu’en le commandant en France, j’aurai pas ce problème !</p>
<p>Deuxième surprise, le jeu est maintenant à 37.90 !! En même temps, ça fait 10 jours que je l’ai commandé, même sur la Fnac le prix a baissé … mais pas dans ma commande (logique ceci dit).</p>
<p><img src="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_1200/https://www.j0k3r.net/assets/images/gallery_index_pix_3.png" srcset="https://res.cloudinary.com/j0k/image/fetch/c_limit,f_auto,q_auto,w_200/https://www.j0k3r.net/assets/images/gallery_index_pix_3.png 200w" sizes="100vw" alt="Red Dead Redemption à 37.90€" width="200" height="150" crossorigin="anonymous" /></p>
<p>En re-regardant le détail de ma commande, je survole le “en savoir plus” du retrait en magasin PIXmania :</p>
<blockquote>
<p><strong>Note</strong>
Date de livraison estimée le
07/06/2010 En application avec
l’article L121-20-3 du code de la
consommation, nous nous engageons à
vous livrer au lieu déterminé avant le
09/06/2010.</p>
</blockquote>
<p>MOUHAHAHAHAAHHAAAAA nah mais c’est quoi cette blague ?
Je ne suis pas un expert juridique, mais je vais quand même faire un tour du côté de l’<a href="http://www.legifrance.gouv.fr/affichCodeArticle.do?cidTexte=LEGITEXT000006069565&idArticle=LEGIARTI000018048016&dateTexte=2010061">article L121-20-3</a>.</p>
<p>De ce que j’en comprends:</p>
<ul>
<li>Le vendeur est obligé d’afficher une date de livraison</li>
<li>Si le vendeur ne livre pas, je suis remboursé (bon, moi j’ai encore rien payé)</li>
<li>Si le produit est indisponible, le vendeur DOIT informer le client et le rembourser ou lui fournir un produit équivalent.</li>
<li>que la dernière ligne semble annuler toutes celles du dessus …</li>
</ul>
<p>Bref, je vais attendre voir ce qu’il se passe avec cette commande.</p>
<p>En attendant, j’ai reçu le jeu de la Fnac le matin !! (comme prévu).</p>
<h2 id="dimanche-13-juin">Dimanche 13 juin</h2>
<p>Suite et fin de l’histoire. Je reçois ce matin un SMS + un mail pour m’indiquer que mon colis est enfin arrivé au point de retrait.</p>
<p>Mon bon, merci la Fnac, j’ai déjà commencé Red Dead Redemption :-)</p>
<h2 id="au-final-">Au final …</h2>
<p>Tout le monde le sait, un client pas content le criera sur tous les toits alors qu’un client content va rarement le faire remarquer parce que pour lui c’est normal.</p>
<p>La notion du “en stock” chez PIXmania n’est donc pas du tout fiable puisque le produit était en rupture nationale mais tranquillement toujours disponible. Le client qui a gentiment passé sa commande mais aucunement informer du délais de réassort, remarque il n’était pas prévenu que le produit était en rupture.</p>
<p>Je ne retournerais certainement jamais chez PIXmania pour faire une commande. On en revient au même point: il suffit souvent de payer un poil plus cher pour avoir un vrai service de qualité. Service que ne semble pas avoir PIXmania.</p>j0kC'est pas du tout mon genre de pousser des coups de gueules par ici. Mais là l'histoire m'a tellement énervé, énervé de voir que PIXmania diffuse des informations mensongères et ne communique en rien avec ces clients. Tout commença avec la réception d'une newsletter affichant un prix massacré sur Red Dead Redemption …Ma version de forum avec Doctrine est sur #github !2010-03-20T18:51:49+01:002010-03-20T18:51:49+01:00https://www.j0k3r.net/ma-version-de-sfdoctrinesimpleforumplugin-est-sur-github<p>J’ai commencé les devs de la v3 <a href="http://www.j0k3r.net/blog/teasing-v3-0">fin 2009</a>. A cette époque je n’avais pas trouvé de script de forum compatible Doctrine qui tenait la route. Alors, je me suis pris un weekend et j’ai converti l’actuel <a href="http://www.symfony-project.org/plugins/sfSimpleForumPlugin">sfSimpleForumPlugin</a> sous Doctrine.</p>
<p>Une fois la v3 sortie, j’ai été contacté par plusieurs développeurs qui me demandaient quelle version de sfDoctrineSimpleForumPlugin j’utilisais sur mon site car ils étaient intéressés pour l’avoir. Je leur répondais à chaque fois que c’était <em>ma</em> version et que je devais prendre le temps ed la publier.</p>
<p>Fin janvier, <a href="http://twitter.com/vjousse">Vincent</a> me contacte sur twitter pour à peu près la même chose. Et du coup, je me suis ensuite lancé dans un rapide ménage du plugin pour le publier sur github !</p>
<p>Et c’est maintenant chose fait, vous pouvez trouver <a href="http://github.com/j0k3r/sfDoctrineSimpleForumPlugin">sfDoctrineSimpleForumPlugin sur github</a> !</p>
<p>Mon site tourne (encore) sur Symfony 1.2 mais je me suis efforcé de rendre sfDoctrineSimpleForumPlugin compatible avec la 1.4. J’ai fait un rapide readme (grandement repris de la version propel).</p>
<p>J’ai aussi rajouté une tâche que j’ai appelé <code class="language-plaintext highlighter-rouge">fixForum</code>. Elle m’a beaucoup servi quand j’ai importé mes données depuis phpBB2 (et là j’en vois qui vont maintenant me demander mon script d’export phpBB2 -> sfDoctrineSimpleForumPlugin …). Cette tâche permet de mettre à jour les données qui sont normalement calculées à chaque nouveau post ou topic créé:</p>
<ul>
<li>nb_posts</li>
<li>latest_post_id</li>
<li>nb_topics</li>
</ul>
<p>Lors de l’import brut (je suis passé de phpBB2 -> fichier yaml -> sfDoctrineSimpleForumPlugin) ces données n’étaient pas calculés automatiquement.</p>
<p>Je n’ai plus rien à ajouter pour le moment. N’hésite pas à forker le plugin et à soumettre vos modifications :-)</p>j0kNombreux ont été les développeurs Symfony à me demander la version du plugin sfSimpleForumPlugin que j'avais converti sous Doctrine sur mon site car ils ne trouvaient pas leur bonheur dans ce qui existait. C'est pourquoi je me suis décidé à la publier, "brut de décoffrage" sur github.J’ai joué un peu avec Sikuli …2010-02-16T08:22:04+01:002010-02-16T08:22:04+01:00https://www.j0k3r.net/j-ai-joue-un-peu-avec-sikuli<blockquote>
<p>Comme toute geek développeur 2.0 qui
se respecte, je fais de la veille. Le
problème de cette veille c’est qu’on
voit passer plein de nouveau script,
nouvelle technique, nouveau langage,
etc … On regarde un peu en diagonale
et on se dit que ça semble hyper
intéressant, on bookmark, on twitte,
on share en se disant qu’on regardera
au moment venu. Ce moment arrive très
très rarement.</p>
</blockquote>
<p>Il y a quelques temps j’ai vu passé le projet <a href="http://sikuli.org/">Sikuli</a>. Sikuli permet de contrôler une interface graphique en se basant sur des images. Les exemples données sur le site officiel montrent comment contrôler le changement de statut sur Skype, changer une adresse IP, etc ..
Pour des raisons personnelles, j’ai dû me pencher sur Sikuli pour mettre en place quelques automatisations. Voilà un peu ce que je retiens (pour le moment) de mon utilisation de Sikuli.</p>
<h2 id="prise-en-main">Prise en main</h2>
<p>L’installation est très rapide (sous réserve que vous ayez déjà java d’installer). L’IDE fournit avec Sikuli est très basique (trop même des fois).L’affichage est divisé en 3 parties:</p>
<ul>
<li>L’éditeur à proprement parler (continuellement en mode pseudo wysiwyg)</li>
<li>Le trace du traitement</li>
<li>Un encart pour les tests unitaires</li>
</ul>
<h2 id="utilisation">Utilisation</h2>
<p>L’autocompletion n’est pas toujours active, mais les fonctions utilisées sont très basiques: <code class="language-plaintext highlighter-rouge">find()</code>, <code class="language-plaintext highlighter-rouge">click()</code>, etc … chaque fonction de ce genre prend en paramètre un screenshot. Il suffit de cliquer sur le bouton pour prendre un screenshot: l’IDE se ferme, un voile gris se mets sur votre écran et vous pouvez sélectionner une zone pour le screenshot. L’image est ensuite enregistrée dans le dossier de votre projet et apparait en paramètre de votre fonction.
Une fois le screenshot pris, en cliquant dessus on peut émuler la recherche de ce screenshot sur l’écran et ainsi ajuster la similitude. Ainsi, on peut reconnaitre une forme même si par exemple, elle se retrouve avec une autre forme par dessus (dans l’absolu).</p>
<p>Pour tester notre bout de code, on peut lancer un mode “pas à pas”, qui va vous montrer ce que trouve le script avant de faire les actions. Pratique pour voir comment se déroule la recherche.</p>
<p>Côté code, c’est du Jython. Un interpréteur Python écrit en Java.
Chaque projet est constitué de deux fichiers sources:</p>
<ul>
<li>un .py qui correspond au code brut</li>
<li>un .html qui est la représentation avec les images dans l’IDE</li>
</ul>
<p>En regardant le code du .py, on voit très bien que c’est du Python (non sans dec’ ?) et que toutes les références faites aux images, sont les noms de fichier présent dans le même dossier:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>c2 = find(Pattern("1265534956718.png").similar(0.65))
</code></pre></div></div>
<p>C’est pratique des fois pour affiner quelques éléments dans le code. Un peu comme quand on développait sous Dreamweaver en mode Wysiwyg complet et que de temps en temps il fallait corriger à la main les lignes de codes dégueulasse qu’il générait ! ..</p>
<h2 id="a-la-longue">A la longue</h2>
<p>Le p’tit script que j’ai développé tourne toutes les nuits pour faire, à ma place, ce que je fais dans la journée.
J’ai souvent été emmené à avoir des problèmes de mémoires, le script faisant souvent des reloads dans Chrome, je devais rapidement atteindre le maximum de mémoire vive et le matin, je me rendais compte que mon onglet utilisé quelques choses comme 600Mo …</p>
<p>Un autre problème concerne le stockage des screenshots qu’il fait pour faire ses recherches. A chaque fois que vous utilisez <code class="language-plaintext highlighter-rouge">find()</code>, <code class="language-plaintext highlighter-rouge">click()</code> et ces copains, Sikuli prends un screenshot de votre écran, le stocke dans le dossier temporaire de la machine, fait la recherche dans cette image à partir de votre screenshot et une fois fini, il ne supprime pas le fichier ! Je vous laisse imaginer un tel script tourner pendant plusieurs nuits d’affiler .. J’ai eu droit à “espace disque insuffisant sur C:”. Les screenshots prenaient … 25Go .. <a href="https://bugs.launchpad.net/sikuli/+bug/515003">C’est un bug connu</a>. Pour pallier au bug, j’ai fait un méthode qui ouvre un terminal, écrit une ligne de commande qui supprime tous les fichiers temporaires, mais bon …</p>
<p>L’IDE présente des p’tits soucis de copier/coller mal géré. Le Ctrl+Z ne fonctionne pas non plus … et ça, c’est sacrément handicapant.</p>
<p>Quand on lance un script, l’IDE de Sikuli se ferme pour tourner en tache de fond. Il existe normalement un raccourci clavier (Alt+ Maj + C) qui permet de forcer l’arrêt. Mais il n’a jamais fonctionné chez moi … obligé de killer à la main javaw.exe. Le problème dans ce cas, c’est qu’on perds les modifications faites et non enregistrées de notre script. Ahem</p>
<h2 id="mais-ça-sert-à-quoi-en-fait-">Mais ça sert à quoi en fait ?</h2>
<p>Le site officiel grouille d’utilisation plus ou moins intéressantes: <a href="http://sikuli.org/blog/?p=108">tweeter</a>, <a href="http://sikuli.org/blog/?p=76">faire le ménage sur son pc</a>, <a href="http://sikuli.org/blog/?p=61">changer son statut facebook</a>, etc …
On pourrait éventuellement s’en servir pour faire des tests fonctionnels d’application. Un peu comme si on utilisait sfWebBrowserPlugin quoi :-)</p>
<p>L’idée qui me vient en tête c’est la création de bots par exemple, pour automatiser l’inscription, la pollution, etc ..
Il est assez simple de casser un captcha avec Sikuli, il faut simplement constituer la base de données des images potentielles pour le faire (je l’ai fait donc ..).</p>j0kIl y a quelques temps j'ai vu passé le projet Sikuli. Sikuli permet de contrôler une interface graphique en se basant sur des images. Les exemples données sur le site officiel montrent comment contrôler le changement de statut sur Skype, changer une adresse IP, etc .. Pour des raisons personnelles, j'ai dû me pencher sur Sikuli pour mettre en place quelques automatisations. Voilà un peu ce que je retiens (pour le moment) de mon utilisation de Sikuli.Hier c’était un #symfony day comme on en fait pas souvent !2009-12-02T14:13:13+01:002009-12-02T14:13:13+01:00https://www.j0k3r.net/hier-c-etait-un-symfony-day-comme-on-en-fait-pas-souvent<p>Ce 1er décembre 2009 fût riche en nouvelles pour la communauté des symfonians. On savait que les versions 1.3 et 1.4 allaient arriver en même temps fin novembre, mais on ne s’attendait pas forcément à voir 3 nouvelles version de Doctrine et la surprise d’un nouveau calendrier de l’avent !</p>
<h2 id="doctrine-120-stable">Doctrine 1.2.0 Stable</h2>
<p><a href="http://www.doctrine-project.org/blog/doctrine-1-2-0-stable-released">Doctrine 1.2.0</a> devait être packagé par défaut avec les nouvelles versions de Symfony, la 1.3 & 1.4. C’est pourquoi elles sont sorties le même jour !</p>
<p>Cette version est la dernière version dite LTS (Long Term Support). Elle sera supportée pendant 18 mois (jusqu’au 06/01/2011).
Dans cette release on trouve une customisation possible lors de l’hydratation des objets, une version améliorée des <em>getter</em> magiques de Doctrine. On pourra faire des appels dans ce genre :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$users</span> <span class="o">=</span> <span class="nv">$userTable</span><span class="o">-></span><span class="nf">findByIsAdminAndIsModeratorOrIsSuperAdmin</span><span class="p">(</span><span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span>
</code></pre></div></div>
<p>On a aussi une facilité d’utilisation pour le travail sur les <code class="language-plaintext highlighter-rouge">Nested</code>, grâce aux nouveaux systèmes d’hydratation justement. La gestion du cache a aussi été amélioré.</p>
<h2 id="doctrine-1014--116">Doctrine 1.0.14 & 1.1.6</h2>
<p><a href="http://www.doctrine-project.org/blog/more-doctrine-releases">Doctrine 1.0.14 & 1.1.6</a> sont des versions de maintenance des anciennes branches. Le support de ces deux branches s’arrêtera en mars prochain, il est temps de passer sur Doctrine 1.2 !</p>
<h2 id="symfony-1210">Symfony 1.2.10</h2>
<p>Le support de Symfony 1.2.x s’arrêtera en février prochain mais l’idée c’est de porter un paquet de changements qui ont été fait sur la version 1.3 notamment. On note l’ajout de la tâche project:validate qui permet de faciliter la migration vers la version 1.4 en détaillant les points qu’il faut changer pour la migration.</p>
<p>Il y a aussi une rafle de tickets corrigés. Vous pouvez checker les nouveautés de <a href="http://www.symfony-project.org/blog/2009/12/01/symfony-1-2-10-preparing-you-for-1-3-and-1-4">Symfony 1.2.10</a> sur le blog officiel.</p>
<h2 id="symfony-13--14">Symfony 1.3 & 1.4</h2>
<p>On les attendait pour la fin de l’année, pour fin novembre plus précisément. Elles sont <a href="http://www.symfony-project.org/blog/2009/12/01/symfony-1-3-and-1-4-stable-released">arrivées tout début décembre</a> !</p>
<p>La version 1.3 affiche des améliorations faites par rapport à la 1.2, notamment des briques qui n’avaient pas été intégré par faute de temps (comme swift mailer par exemple). On note aussi l’arriver d’un nouvel onglet dans la web debug toolbar qui permet de voir toutes les variables qui sont passées au template, pratique ! Un travail sur l’amélioration de la performance a aussi été fait.</p>
<p>La version 1.4 est la nouvelle LTS de Symfony: la nouvelle version qui sera supportée pendant les 3 prochaines années. Elle est identique à la version 1.3 mais sans toutes les fonctions, variables etc .. dépréciées. Comme cité dans la release de la version 1.2.10, une tâche project:validate permet d’identifier les changements à faire sur la version 1.3 pour migrer vers la version 1.4 sans trop de problème.</p>
<h2 id="un-nouveau-calendrier-de-lavent-symfony">Un nouveau calendrier de l’avent Symfony</h2>
<p>L’<a href="http://www.j0k3r.net/blog/les-calendriers-de-l-avent-2008">année dernière</a> on avait eu droit à Jobeet, cette année c’est un tout autre calendrier, tout aussi intéressant !</p>
<p>En effet, il présente <a href="http://www.symfony-project.org/blog/2009/12/01/one-more-thing?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+symfony/blog+(symfony+Project+Blog)&utm_content=Google+Reader">une utilisation plus avancée de Symfony</a> sur plusieurs points :</p>
<ul>
<li>le routing</li>
<li>les emails</li>
<li>les formulaires</li>
<li>Doctrine (version avancée et héritage)</li>
<li>l’intégration avec Facebook</li>
<li>la ligne de commande</li>
<li>et le cache</li>
</ul>
<p>Mise à part le fait qu’on va certainement découvrir de nouvelles choses, ce qui est encore plus énorme, c’est qu’il sera disponible dans <a href="http://www.symfony-project.org/advent_calendar/">5 langues chaque jour</a> !!</p>
<p>Et cerise sur le gâteau (comme si ce n’était déjà pas assez), il existe aussi un livre qu’on peut dors et déjà <a href="http://www.amazon.com/gp/product/2918390178?ie=UTF8&tag=symfonyprojec-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=2918390178">acheté sur amazon</a>.</p>
<p>Que du bon pour cette fin d’année !</p>j0kCe 1er décembre a été riche en nouvelle pour Symfony. Il y a eu 3 nouvelles version de Symfony & 3 nouvelles version de Doctrine le même jour. Sans compte le reste !!Génération de PDF avec le Zend Framework2009-11-14T23:09:10+01:002009-11-14T23:09:10+01:00https://www.j0k3r.net/generation-de-pdf-avec-le-zend-framework<blockquote>
<p><strong>Note</strong>
Cet article a été écrit par un
rédacteur invité.</p>
</blockquote>
<p>La génération de documents PDF bien formatés avec PHP est une tâche plutôt difficile. Il existe habituellement deux approches principales. Avec suffisamment de temps et de patience, ces deux approches font l’affaire, mais laissent tout de même à désirer :</p>
<p><strong>L’approche HTML vers PDF</strong>: Cette approche est très employée dans des applications traditionnelles. Dans ce cas, un document est programmé en HTML et converti en PDF en utilisant une des nombreuses librairies Open Source (<em>domPDF, HTML 2 (F)PDF, HTML_ToPDF, mPDF etc.</em>). Cependant, le HTML n’est pas un format orienté page (tel le PDF) et donc ce n’est pas possible d’exécuter une conversion fidèle de HTML en PDF. Les caractéristiques typiques de format d’un fichier de traitement de texte, tels que les en-têtes et pieds de page, les veuves et orphelins ou encore la numérotation de pages ne peuvent tout simplement pas être représentés en HTML.</p>
<p><strong>L’approche programmation</strong>: Cette approche offre un contrôle total sur le PDF résultant. Cependant, il faut que toutes les coordonnées x et y de chaque ligne de texte, les formes géométriques et les graphiques soient fixés par le code source 2. Non seulement, c’est une solution qui prend énormément de temps, mais elle est également très fragile : à chaque fois que la mise en page d’un document est changée, le programmeur doit retravailler son code source.</p>
<h2 id="une-approche-totalement-nouvelle">Une approche totalement nouvelle</h2>
<p>Cet article présente une troisième approche totalement nouvelle : elle repose sur des modèles qui ont été créés dans un environnement WYSIWYG, tels que Microsoft® Word ou Open Office, puis chargés avec des données dans PHP. Le document résultant peut être enregistré non seulement sous PDF, mais aussi en tant que DOCX, DOC et RTF. Cette approche multiplateforme a été mise en œuvre en utilisant le Zend Framework.</p>
<p>Avant d’arriver à une discussion technique sur les travaux internes de cette nouvelle approche, prenons d’abord un exemple pratique ! Le code PHP5 suivant montre la génération d’un PDF dans lequel les champs <em>software</em>, <em>licensee</em> et <em>company</em> sont remplis avec des données scalaires dans le modèle <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/license-agreement-template.docx">template.docx</a> [46.7 KB]. Le document résultant <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/license-agreement-document.pdf">document.pdf</a> [104.7 KB] est créé et enregistré sur le disque.</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$phpLiveDocx</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Zend_Service_LiveDocx_MailMerge</span><span class="p">(</span>
<span class="k">array</span> <span class="p">(</span>
<span class="s1">'username'</span> <span class="o">=></span> <span class="s1">'yourUsername'</span><span class="p">,</span>
<span class="s1">'password'</span> <span class="o">=></span> <span class="s1">'yourPassword'</span>
<span class="p">)</span>
<span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">setLocalTemplate</span><span class="p">(</span><span class="s1">'template.docx'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'software'</span><span class="p">,</span> <span class="s1">'Magic Graphical Compression Suite v1.9'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'licensee'</span><span class="p">,</span> <span class="s1">'Henry Smith'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'company'</span><span class="p">,</span> <span class="s1">'Megasoft Co-operation'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">createDocument</span><span class="p">();</span>
<span class="nv">$document</span> <span class="o">=</span> <span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">retrieveDocument</span><span class="p">(</span><span class="s1">'pdf'</span><span class="p">);</span>
<span class="nb">file_put_contents</span><span class="p">(</span><span class="s1">'document.pdf'</span><span class="p">,</span> <span class="nv">$document</span><span class="p">);</span>
<span class="k">unset</span><span class="p">(</span><span class="nv">$phpLiveDocx</span><span class="p">);</span>
</code></pre></div></div>
<p>Le code démontré dans cet article sera livré avec <a href="http://www.zendframework.com/download/latest">Zend Framework 1.10</a> lorsqu’il sera disponible. Bien qu’à l’heure de la rédaction de cet article il n’y a pas encore de date officiel de sortie, le lancement de 1.10 est attendu pour fin 2009. En attendant, vous pouvez faire un checkout des composants du répertoire Subversion <a href="http://framework.zend.com/svn/framework/standard/incubator/">Standard Incubator</a>.</p>
<h2 id="présentation-de-livedocx">Présentation de LiveDocx</h2>
<p><a href="http://www.livedocx.com/">LiveDocx</a> est un service <a href="http://en.wikipedia.org/wiki/SOAP">SOAP</a> pour la génération de document basé sur les composants de traitement de texte <a href="http://www.textcontrol.com/">TX Text Control .NET</a>. Il permet de remplir des modèles de traitement de texte avec des données dans tous les langages de programmation supportant SOAP. Le document résultant peut être enregistré sous tout format supporté. Toutefois, cet article se concentre sur l’utilisation de LiveDocx dans PHP5.</p>
<p>Les composants de l’implémentation du Zend Framework dans LiveDocx sont situés dans le fichier standard de distribution <code class="language-plaintext highlighter-rouge">/Zend/Service/LiveDocx/</code>. C’est possible d’utiliser LiveDocx directement avec le <a href="http://www.phplivedocx.org/articles/using-livedocx-without-the-zend-framework/">SoapClient</a> de PHP5, sans le Zend Framework, ainsi qu’avec la librairie tierce partie <a href="http://www.phplivedocx.org/articles/using-livedocx-with-nusoap/">NuSOAP</a>. L’approche NuSOAP permet d’utiliser LiveDocx aussi dans PHP4. Cependant, cet article est centré sur les composants officiels du Zend Framework dans PHP5.</p>
<h2 id="modèles-et-documents">Modèles et documents</h2>
<p>Tout au long de cet article, nous nous référons aux termes <em>modèle</em> et <em>document</em>. C’est important de comprendre la différence entre les deux.</p>
<p><strong>Modèles</strong>: le terme <em>modèle</em> renvoie au fichier d’entrée contenant les champs de données et de texte. Les modèles peuvent être un des formats de fichier suivants:</p>
<ul>
<li>DOCX - Format Office Open XML</li>
<li>DOC - Format Microsoft® Word DOC</li>
<li>RTF - Format Rich Text</li>
<li>TXD - Format TX Text Control®</li>
</ul>
<p>Les modèles peuvent être sauvegardés soit <em>localement</em> sur l’ordinateur du client (celui à partir duquel la requête SOAP est démarrée) ou à <em>distance</em> sur le serveur LiveDocx. Le choix entre ces deux solutions dépend de l’application que vous développez.</p>
<p>Si vous enregistez les modèles localement, vous devez les transférer sur le serveur avec les données à charger à chaque requête. Cette approche est inefficace, si le modèle reste le même à chaque fois. Il est préférable de télécharger le modèle une seule fois sur le serveur LiveDocx, puis de le référencer dans tous les requêtes subséquentes. De cette façon, seules les données qui doivent être chargées sont transférées du client au serveur terminal. La plupart des applications qui utilisent LiveDocx tombent dans cette catégorie.</p>
<p>D’un autre côté, si vous avez un modèle qui change constamment ou une application dans laquelle vous autorisez les utilisateurs à télécharger des modèles, vous devez stocker les modèles localement et les transférer à chaque requête. Cette approche est manifestement plus lente car le modèle et les données sont transférés à chaque requête.</p>
<p><strong>Documents</strong>: le terme <em>document</em> décrit dans le contexte de LiveDocx un fichier généré par le service Web, donc un modèle rempli avec des données. Les documents peuvent être enregistrés sous chacun des formats suivants :</p>
<ul>
<li>DOCX - Format Office Open XML</li>
<li>DOC - Format Microsoft® Word DOC</li>
<li>HTML - Format XHTML 1.0 Transitional</li>
<li>RTF - Format Rich Text</li>
<li>PDF - Format Acrobat® Portable Document</li>
<li>TXD - Format TX Text Control</li>
<li>TXT - Format ANSI Plain Text</li>
</ul>
<p>Outre les formats de fichier de traitement de texte nommés ci-dessus, les documents peuvent également enregistrés sous les formats de fichiers d’image suivants :</p>
<ul>
<li>BMP - Format Bitmap Image</li>
<li>GIF - Format Graphics Interchange</li>
<li>JPG - Format Joint Photographic Experts Group</li>
<li>PNG - Format Portable Network Graphics</li>
<li>TIFF - Format Tagged Image File</li>
<li>WMF - Format Windows Meta File</li>
</ul>
<h2 id="utilisation-de-livedocx">Utilisation de LiveDocx</h2>
<p>Dans ce paragraphe, nous allons étudier toute la procédure de création d’un document en utilisant LiveDocx.</p>
<h3 id="création-dun-modèle-dans-microsoft-word-2007">Création d’un modèle dans Microsoft® Word 2007</h3>
<p>Le premier pas dans n’importe quel projet LiveDocx est la création d’un modèle. Pour cela, vous pouvez utiliser soit Open Office ou Microsoft® Word. Dans cet article, nous allons utiliser Microsoft® Word 2007. Si vous préférez employer Open Office, veuillez consulter le <a href="http://blog.livedocx.com/post/Creating-templates-using-OpenOfficeorg.aspx">Blog LiveDocx</a>.</p>
<p>Démarrez en créant un nouveau fichier dans Microsoft® Word 2007 et enregistrez le fichier modèle sous <em>template.docx</em>.
Vous pouvez commencer à concevoir le modèle en insérant du texte, des graphiques et des champs de données avec la boîte de dialogue <em>Field</em> affichée ci-après.</p>
<p><img src="/media/articles/zend_pdf/msword-dialog.png" alt="Insert merge field in Microsoft® Word 2007" /></p>
<p>Au bout d’un moment, vous aurez un modèle qui contient des images, du texte et un certain nombre de champs de données. Ces derniers sont représentés par <em>{ MERGEFIELD nom }</em> et seront remplis par des données scalaires dans la prochaine étape. La capture d’écran suivante du modèle dans Microsoft® Word 2007 vous montre de quoi il peut avoir l’air :</p>
<p><img src="/media/articles/zend_pdf/msword-basic-template.png" alt="Template in Microsoft® Word 2007" /></p>
<p>Sauvegardez le modèle <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/license-agreement-template.docx">template.docx</a> [46 KB] quand vous avez terminé.</p>
<h3 id="charger-les-modèles-avec-des-données">Charger les modèles avec des données</h3>
<p>Maintenant que vous avez le fichier modèle, vous devez le charger avec des données. Dans l’exemple suivant, nous allons attribuer des types de données scalaires, dans ce cas des chaînes de caractères, au modèle.</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$phpLiveDocx</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Zend_Service_LiveDocx_MailMerge</span><span class="p">(</span>
<span class="k">array</span> <span class="p">(</span>
<span class="s1">'username'</span> <span class="o">=></span> <span class="s1">'yourUsername'</span><span class="p">,</span>
<span class="s1">'password'</span> <span class="o">=></span> <span class="s1">'yourPassword'</span>
<span class="p">)</span>
<span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">setLocalTemplate</span><span class="p">(</span><span class="s1">'template.docx'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'software'</span><span class="p">,</span> <span class="s1">'Magic Graphical Compression Suite v1.9'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'licensee'</span><span class="p">,</span> <span class="s1">'Henry Smith'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'company'</span><span class="p">,</span> <span class="s1">'Megasoft Co-operation'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'date'</span><span class="p">,</span> <span class="s1">'January 14, 2009'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'time'</span><span class="p">,</span> <span class="s1">'4:30:43 PM CET'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'city'</span><span class="p">,</span> <span class="s1">'Bremen'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'country'</span><span class="p">,</span> <span class="s1">'Germany'</span><span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">createDocument</span><span class="p">();</span>
<span class="nv">$document</span> <span class="o">=</span> <span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">retrieveDocument</span><span class="p">(</span><span class="s1">'pdf'</span><span class="p">);</span>
<span class="nb">file_put_contents</span><span class="p">(</span><span class="s1">'document.pdf'</span><span class="p">,</span> <span class="nv">$document</span><span class="p">);</span>
<span class="k">unset</span><span class="p">(</span><span class="nv">$phpLiveDocx</span><span class="p">);</span>
</code></pre></div></div>
<p>Dans plusieurs applications, en particulier celles dans lesquelles les fichiers PDF sont utilisés pour des raisons d’archivage, vous devriez fixer les métadonnées du fichier PDF. La méthode <code class="language-plaintext highlighter-rouge">setDocumentProperties()</code> doit être déterminée avant <code class="language-plaintext highlighter-rouge">createDocument()</code> :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nv">$documentProperties</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="s1">'title'</span> <span class="o">=></span> <span class="s1">'Magic Graphical Compression Suite v1.9'</span><span class="p">,</span>
<span class="s1">'author'</span> <span class="o">=></span> <span class="s1">'Megasoft Co-operation'</span><span class="p">,</span>
<span class="s1">'subject'</span> <span class="o">=></span> <span class="s1">'Magic Graphical Compression Suite v1.9'</span><span class="p">,</span>
<span class="s1">'keywords'</span> <span class="o">=></span> <span class="s1">'Graphics, Magical, Compress, Suite, License'</span>
<span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">setDocumentProperties</span><span class="p">(</span><span class="nv">$documentProperties</span><span class="p">);</span>
</code></pre></div></div>
<p>Le document résultant <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/license-agreement-document.pdf">document.pdf</a> [104 KB] est enregistré sur le disque et peut être ouvert dans votre lecteur PDF favori, tel que le Document Viewer livré avec Ubuntu :</p>
<p><img src="/media/articles/zend_pdf/msword-basic-document.png" alt="Template in Document Viewer" /></p>
<h3 id="attribution-de-données-composées-à-livedocx">Attribution de données composées à LiveDocx</h3>
<p>Outre les données scalaires qui ont été assignées au modèle dans l’exemple précédent, vous pouvez également attribuer des données composées, tel un tableau associatif. Prenez le modèle <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/telephone-bill-template.doc">template.doc</a> [20.5 KB] et le document résultant <a href="http://www.phplivedocx.org/wp-content/uploads/2009/01/telephone-bill-document.pdf">document.pdf</a> [77.6 KB]. Observez en particulier la partie suivante du modèle :</p>
<p><img src="/media/articles/zend_pdf/msword-complex-template.png" alt="Template in Microsoft® Word, cropped. Click to enlarge." /></p>
<p>La partie du modèle entre les crochets <strong>I</strong> et <strong>I</strong> est répétée tout au long du document pour produire les rangées du tableau. Le tableau associatif suivant utilise un sous-tableau pour chaque rangée.</p>
<p>En utilisant le code PHP5, vous allez charger le modèle avec un tableau associatif de données de numéros de téléphone. Pour plus de clarté, cet exemple montre seulement la partie dans laquelle est attibué le tableau associatif. L’objet de LiveDocx, la création de document et les procédures récupérables sont identiques aux exemples précédents et ont été omis :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// instanciez LiveDocx</span>
<span class="nv">$billConnections</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">'connection_number'</span> <span class="o">=></span> <span class="s1">'+11 (0)222 333 441'</span><span class="p">,</span>
<span class="s1">'connection_duration'</span> <span class="o">=></span> <span class="s1">'00:01:01'</span><span class="p">,</span>
<span class="s1">'fee'</span> <span class="o">=></span> <span class="s1">'1.15'</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">'connection_number'</span> <span class="o">=></span> <span class="s1">'+11 (0)222 333 442'</span><span class="p">,</span>
<span class="s1">'connection_duration'</span> <span class="o">=></span> <span class="s1">'00:01:02'</span><span class="p">,</span>
<span class="s1">'fee'</span> <span class="o">=></span> <span class="s1">'1.15'</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">'connection_number'</span> <span class="o">=></span> <span class="s1">'+11 (0)222 333 443'</span><span class="p">,</span>
<span class="s1">'connection_duration'</span> <span class="o">=></span> <span class="s1">'00:01:03'</span><span class="p">,</span>
<span class="s1">'fee'</span> <span class="o">=></span> <span class="s1">'1.15'</span>
<span class="p">),</span>
<span class="k">array</span><span class="p">(</span>
<span class="s1">'connection_number'</span> <span class="o">=></span> <span class="s1">'+11 (0)222 333 444'</span><span class="p">,</span>
<span class="s1">'connection_duration'</span> <span class="o">=></span> <span class="s1">'00:01:04'</span><span class="p">,</span>
<span class="s1">'fee'</span> <span class="o">=></span> <span class="s1">'1.15'</span>
<span class="p">)</span>
<span class="p">);</span>
<span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">assign</span><span class="p">(</span><span class="s1">'connection'</span><span class="p">,</span> <span class="nv">$billConnections</span><span class="p">);</span>
<span class="c1">// générez et récupérez le document</span>
</code></pre></div></div>
<p>Le document résultant contient un tableau avec les données qui lui ont été attribuées :</p>
<p><img src="/media/articles/zend_pdf/docviewer-complex-template.png" alt="Template in PDF document viewer, cropped. Click to enlarge." /></p>
<h3 id="génération-de-fichiers-dimage-avec-livedocx">Génération de fichiers d’image avec LiveDocx</h3>
<p>Outre les formats de fichier de traitement de texte listés auparavant qui sont supportés par LiveDocx, vous pouvez également enregistrer les documents résultants sous un ou plusieurs fichiers d’image. Pour cela, <code class="language-plaintext highlighter-rouge">Zend_Service_LiveDocx_MailMerge</code> offrent les méthodes <code class="language-plaintext highlighter-rouge">getAllBitmaps()</code> et <code class="language-plaintext highlighter-rouge">getBitmaps()</code> :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// instanciez LiveDocx</span>
<span class="c1">// enregistrez toutes les images matricielles</span>
<span class="c1">// (zoomFactor, format)</span>
<span class="nv">$bitmaps</span> <span class="o">=</span> <span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">getAllBitmaps</span><span class="p">(</span><span class="mi">100</span><span class="p">,</span> <span class="s1">'png'</span><span class="p">);</span>
</code></pre></div></div>
<p>De la même façon, il est possible de ne récupérer que les images de certaines pages :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// enregistrez seulement les images matricielles de certaines pages</span>
<span class="c1">// (fromPage, toPage, zoomFactor, format)</span>
<span class="nv">$bitmaps</span> <span class="o">=</span> <span class="nv">$phpLiveDocx</span><span class="o">-></span><span class="nf">getBitmaps</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">100</span><span class="p">,</span> <span class="s1">'png'</span><span class="p">);</span>
</code></pre></div></div>
<p>Notez le paramètre <code class="language-plaintext highlighter-rouge">zoomFactor</code>. C’est un pourcentage situé entre 10% et 400%. Cette commande est idéale pour générer des imagettes du document créé, par exemple, pour l’afficher dans le navigateur en prévisualisation.</p>
<p>Les fichiers d’image peuvent être enregistrés sur le disque en itérant sur le tableau <code class="language-plaintext highlighter-rouge">$bitmaps</code>. Chaque valeur contient une page de données binaires :</p>
<div class="language-php highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// enregistrez sur le disque</span>
<span class="c1">// (une page par valeur)</span>
<span class="k">foreach</span> <span class="p">(</span><span class="nv">$bitmaps</span> <span class="k">as</span> <span class="nv">$pageNumber</span> <span class="o">=></span> <span class="nv">$bitmapData</span><span class="p">)</span>
<span class="p">{</span>
<span class="nv">$filename</span> <span class="o">=</span> <span class="nb">sprintf</span><span class="p">(</span><span class="s1">'documentPage%d.png'</span><span class="p">,</span> <span class="nv">$pageNumber</span><span class="p">);</span>
<span class="nb">file_put_contents</span><span class="p">(</span><span class="nv">$filename</span><span class="p">,</span> <span class="nv">$bitmapData</span><span class="p">);</span>
<span class="nb">printf</span><span class="p">(</span><span class="s1">'Written %d bytes to disk as %s.%s'</span><span class="p">,</span> <span class="nb">filesize</span><span class="p">(</span><span class="nv">$filename</span><span class="p">),</span> <span class="nv">$filename</span><span class="p">,</span> <span class="kc">PHP_EOL</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="déployer-livedocx-dans-vos-propres-applications">Déployer LiveDocx dans vos propres applications</h2>
<p>Tout le code source PHP, incluant les composants LiveDocx, qui est livré avec le Zend Framework est sorti sous la licence <em>Free BSD</em>. Ainsi, vous pouvez déployer, modifier et redistribuer le code source PHP dans des projets selon les termes de cette licence. Le serveur SOAP de LiveDocx est toutefois un logiciel propriétaire. Vous avez trois possibilités pour déployer ce service SOAP dans vos propres applications :</p>
<ol>
<li>
<p><strong>Serveur public gratuit</strong></p>
<p>Les développeurs choisissent cette approche dans la majorité des applications. Le serveur LiveDocx par défaut qui est référencé dans les composants du Zend Framework est un serveur public gratuit. Il a toutes les opérations et fonctions des serveurs d’hébergement et peut être utilisé gratuitement dans vos propres applications. Tout ce dont vous avez besoin sont un nom d’utilisateur et un mot de passe - pour cela, <a href="https://www.livedocx.com/user/account_registration.aspx">cliquez ici</a>.</p>
</li>
<li>
<p><strong>Solution d’hébergement</strong></p>
<p>Si votre application doit créer des milliers de documents par jour, vous devrez payer une modeste cotisation mensuelle pour accéder à votre propre serveur LiveDocx. En association avec des fournisseurs d’hébergement dominants dispersés dans le monde entier, vous pouvez louer un tel serveur d’hébergement de première qualité dans votre région géographique. Cela réduit le temps de latence et, comme votre application est la seule qui fonctionne sur le serveur, cela augmente la performance.</p>
</li>
<li>
<p><strong>Licence complète</strong></p>
<p>Pour des applications à très haut débit, il est possible d’installer un ou plusieurs serveurs LiveDocx dans vote propre réseau local. Cette approche réduit le temps de latence à un minimum absolu car le serveur LiveDocx est connecté au serveur d’application web par un réseau local gigabit. Les applications typiques pour la licence complète sont les billets d’avion, la création de factures pour les opérateurs de télécommunications et les applications qui traitent des données très sensibles et privées, tels que des rapports médicaux ou financiers.</p>
</li>
</ol>
<h2 id="en-savoir-plus">En savoir plus</h2>
<p>Cette article est une première approche de ce que vous pouvez faire avec LiveDocx. Si vous désirez en savoir plus sur cette nouvelle plateforme performante de génération de document, veuillez consulter les ressources suivantes :</p>
<p>LiveDocx dans PHP5</p>
<ul>
<li><a href="http://www.phplivedocx.org/">Blog phpLiveDocx.</a></li>
<li><a href="http://www.phplivedocx.org/articles/">Articles techniques.</a></li>
</ul>
<p>Service SOAP LiveDocx</p>
<ul>
<li><a href="http://blog.livedocx.com/">Blog LiveDocx.</a></li>
<li><a href="http://www.livedocx.com/pub/documentation/api.aspx">Référence API.</a></li>
</ul>
<p>N’hésitez pas à <a href="http://www.phplivedocx.org/contact/">contacter l’auteur</a> ou vous adresser <a href="http://www.phplivedocx.org/support/">au support technique</a> dans le forum d’assistance (gratuit) à tout moment.</p>
<p><img src="/media/articles/zend_pdf/jonathan_maron.png" alt="Jonathan Maron" /></p>j0kLa génération de documents PDF bien formatés avec PHP est une tâche plutôt difficile. Il existe habituellement deux approches principales. Avec suffisamment de temps et de patience, ces deux approches font l'affaire, mais laissent tout de même à désirer. L'approche **HTML vers PDF** qui peut être efficace pour de simple document mais peut s'avérer complexe et avec un rendu moins fidèle sur de gros document. L'approche **programmation** où l'on définit les éléments en se basant sur des coordonnées. Et là par contre, c'est très complexe à mettre en oeuvre. Et il existe une 3è méthode en utilisant LiveDocx !Vous ne rêvez pas, vous êtes bien sur la v3 !!2009-04-30T00:11:49+02:002009-04-30T00:11:49+02:00https://www.j0k3r.net/vous-ne-revez-pas-vous-etes-bien-sur-la-v3<p>La refonte graphique a bien dû commencer il y a maintenant un an passé certainement.
La maquette graphique finale a été validée peu de temps après.
C’est <a href="http://www.kath.fr">kath</a> qui s’est occupé de cette refonte graphique !</p>
<p>Il a ensuite fallu faire l’intégration, qui n’a pas été si simple (problème avec les boxs et le centrage global du site, le footer qui a fait des siennes - <em>et qui en fait toujours d’ailleurs</em>).</p>
<p>Et puis le concret, la refonte complète sous <strong>Symfony 1.2 et Doctrine 1.0</strong>. C’est ce qui m’a pris le plus de temps.
En premier lieu, j’ai commencé par le backoffice. J’ai repris beaucoup de données de statistiques pour les afficher en dashboard :</p>
<ul>
<li>stats feedburner (dans le même genre du dashboard officiel)</li>
<li>stats adsense de la journée en cours et depuis le début</li>
<li>mini graphiques sur les derniers inscrits, commentaires, etc .. par semaine</li>
<li>graphiques de Google Analytics, les mêmes petits qu’on retrouve dans leur dashboard</li>
<li>et j’ai encore plein d’idées à intégrer.</li>
</ul>
<p>L’idée c’était d’avoir vraiment une vue de tout ce qui peut donnée statistiques que génèrent le site. Je pense y être pas trop mal arrivé :-)</p>
<p>Quand j’aurai le temps et si ça vous intéresse je ferai des mini tutoriels.</p>
<p>Ensuite, la partie front office et a été une partie de rigolade j’ai envie de dire. Comme l’intégration était faite, il fallait maintenant remplir le moule avec les données de la base.
Pour le côté “blog”, j’ai repris <a href="http://www.symfony-project.org/plugins/sfSimpleBlogPlugin">sfSimpleBlogPlugin</a> que j’ai convertis à Symfony 1.2 et Doctrine au passage. J’ai viré pas mal de chose pour arriver à ce que je voulais.</p>
<p>Et forcément, on garde le meilleur pour la fin: la reprise des données ! Et là, je ne pensais vraiment pas me heurter à un problème aussi long et complexe à régler. J’avais quand même de la chance d’utiliser symfony qui m’a grandement facilité la tâche via les fixtures !! Elles m’ont fait gagner du temps et fait chauffer mon processeur aussi.</p>
<p>L’idée c’était de faire un extract de la base existante (mix de phpBB - <em>pour les news et le forum</em> - et de tables persos) en yml pour l’insérer ensuite facilement.
C’est justement là le problème, réussir à générer des fixtures propres. Et là je me suis heurté au BBcode de phpBB. Il faut savoir que tous les contenus du site sont “convertis” en <a href="http://fr.wikipedia.org/wiki/Markdown">markdown</a> et ça a été une sacré paire de manche pour convertir correctement tout le BBcode … entre les persos foireux que j’avais et les anciens qui étaient difficilement convertible pour certain. Je me suis bien amusé !</p>
<p>Mais je suis arrivé à mes fins après quelques moi de bataille !</p>
<p>La conversion de phpBB 2.x à sfSimpleForumplugin a été très simple par contre, j’en ai été même étonné.
Ce qui a pris le plus de temps, ça été la réécriture partiel du plugin pour le rendre compatible avec Symfony 1.2 et Doctrine. Vous allez me dire qu’il suffisait d’utiliser DbFinderPlugin et d’activer le sfCompat10, mais non, je ne voulais justement pas passer par cette alternative et faire <strong>vraiment</strong> du Symfony 1.2 !</p>
<p>Je vous passe les quelques problèmes que j’ai eu avec lighttpd (serveur web principal de mon serveur) parce que je suis rapidement passé en proxy et la v3 tourne sur un bon vieux apache.</p>
<p><strong>Bref, cessons les déboires de développement, passons aux nouveautés !</strong></p>
<ul>
<li>refonte graphique et redéveloppement (mais vous l’aurez compris :p)</li>
<li>apparitions de tags et de catégories (plus clair que la version précédente)</li>
<li>menu beaucoup plus clair</li>
<li>possibilité de se connecter avec Facebook ou OpenID (ou votre ancien compte aussi) - <em>et bientôt avec twitter</em></li>
<li>ajout d’une catégorie <em>vrac</em> qui récupère des liens que je bookmark à droite à gauche</li>
<li>la <em>vidéo du moment</em> qui est en fait une vidéo que j’ai apprécié et que je souhaite partager</li>
<li>une page de <a href="http://www.j0k3r.net/contact">contact</a> (ouais c’est fou: il n’y en avait pas sur l’ancienne version…)</li>
<li>une page <a href="http://www.j0k3r.net/apropos">à propos</a> (pareil, elle est toute nouvelle)</li>
<li>remplacement du forum par du Symfony powered - <em>plus light moins de features qui ne servent à rien</em></li>
<li>les commentaires des news se font maintenant directement sur le site</li>
</ul>
<p>Voilà, je crois que le gros des changements est listé.</p>
<p>Bon, par contre, je vous vois venir les méticuleux du détail qui tue :-) <strong>Tout</strong> n’est pas finis, il me manque des images à changer, des p’tits trucs à faire par ci par là, mais il faut bien se lancer un jour !!!</p>
<p>Et pour finir, je souhaite remercier toutes celles et ceux qui m’ont aidé (de prêt ou de loin) à sortir cette nouvelle version :</p>
<ul>
<li><a href="http://www.kath.fr">kath</a>, pour la refonte graphique</li>
<li><a href="http://danrazor.net/">dan</a> pour ces éternels et méticuleux tests !</li>
<li><a href="http://www.skullpat.com/">skullpat</a>, <a href="http://www.digitalthink.fr/wordpress/">cooly08</a> et <a href="http://www.jiceb.net/">jiceb</a> pour leur retour sur la bêta <em>privée</em></li>
<li><a href="http://randomfeature.net/">engy</a> pour les conseils d’intégration pour ce %£^$à!§ de footer :-)</li>
<li>et tous les autres qui m’ont conseillé sur certain choix !</li>
</ul>
<p>Et pour finir, oui je vais commiter sfSimpleDoctrineForumPlugin et p’tet même sfSimpleDoctrineBlogPlugin. Mais pas tout de suite, tout de suite. Le temps que je fasse un peu le ménage dans le code !</p>j0kEt oui, après un loooong moment de développement, de design, d'intégration, de mous, de laisser-aller, etc ... cette v3 vois enfin le jour !!! Même s'il reste (et restera certainement) toujours des coquilles à corriger si et là, il faut bien se lancer un jour !!La 6e bougie est soufflée !2009-03-31T10:39:56+02:002009-03-31T10:39:56+02:00https://www.j0k3r.net/la-6e-bougie-est-soufflee<p>La première news est parue le 18 mars 2003 ! Yipiii
Cela fait donc maintenant 6 années que le site vie comme il peut, il a eu des hauts biens hauts au niveau de la fréquence des actualités pour maintenant revenir à quelques choses de plus rare .. faute de temps comme d’habitude.</p>
<p>J’avais pour ambition de publier la nouvelle version du site (qui traine, oui je sais ..) pour souffler grandement cette sixième bougie, mais ça ne sera pas possible :-( Toute la partie développement symfony est bel et bien finalisée, y’a du zend, de l’ajax, du facebook connect, de l’open id, etc .. mais le plus long (et je ne m’y attendais pas du tout), c’est la reprise de l’existant!</p>
<p>Étant donné que j’ai viré le bbcode pour utiliser <a href="http://michelf.com/projets/php-markdown/">markdown</a>, les changements sont assez importants et le plus compliqué c’est les sauts de ligne qui ne sont pas représenté pareil !</p>
<p>Je n’avais pas statué sur le sort du forum quand je vous ai présenté le <a href="http://www.j0k3r.net/news-teasing-v3-0-2008.html">teasing de la v3</a>, c’est maintenant chose faite : il sera bel et bien là ! Complètement refondu, exit phpBB, welcome <a href="http://www.symfony-project.org/plugins/sfSimpleForumPlugin">sfSimpleForum</a> remasterisé pour doctrine et symfony 1.2 et customisé pour la v3. Certes il ne reprendra pas toutes les fonctionnalités que proposait phpBB, mais comme je ne me servais pas de toutes (je n’allais jamais dans la partie admin par exemple) ce n’est pas un réel problème. Il sera d’ailleurs en version light pour la sortie mais je ferai certainement des rajouts dans le futur.</p>
<p>M’enfin, je ne vais pas vous divulguer tous les changements que j’ai fait dans la v3, je vais attendre qu’elle soit officiellement sortie (même si certains ont pu <a href="http://twitter.com/jiceb/status/1393565695">la voir à mon insu</a> :p).</p>
<p>Anyway, Happy birthday j0k3r.n3t ! :-)</p>j0kLa première news est parue le 18 mars 2003 ! Yipiii Cela fait donc maintenant 6 années que le site vie comme il peut, il a eu des hauts biens hauts au niveau de la fréquence des actualités pour maintenant revenir à quelques choses de plus rare .. faute de temps comme d'habitude. J'avais pour ambition de publier la nouvelle version du site …