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

<channel>
	<title>Vincent Mazenod, aka mazenovi, aka voisin de gennetines &#187; symfony</title>
	<atom:link href="http://blog.mazenod.fr/tag/symfony/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.mazenod.fr</link>
	<description>#symfony #ispcp #iphone #php #cornemuse</description>
	<lastBuildDate>Mon, 05 Apr 2010 17:35:26 +0000</lastBuildDate>
	
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>note sur les suffixes dans les routes symfony &gt;= 1.2</title>
		<link>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/</link>
		<comments>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/#comments</comments>
		<pubDate>Fri, 02 Apr 2010 15:41:18 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=375</guid>
		<description><![CDATA[le système de routage
Si comme moi vous avez tenté d&#8217;utiliser la directive .suffix dans le routing.yml

prod:
  routing:
    param:
      suffix: .html

et que vous vous attendiez à ce que vorte suffixe .html soit ajouté automatiquement à tous les liens générés via link_to et gen_url &#8230; c&#8217;est que vous [...]]]></description>
			<content:encoded><![CDATA[<h3>le système de routage</h3>
<p>Si comme moi vous avez tenté d&#8217;utiliser la directive .suffix dans le routing.yml</p>
<pre class="yml:nogutter" name="code">
prod:
  routing:
    param:
      suffix: .html
</pre>
<p>et que vous vous attendiez à ce que vorte suffixe .html soit ajouté automatiquement à tous les liens générés via link_to et gen_url &#8230; c&#8217;est que vous ne savez pas que <a href="http://groups.google.com/group/symfony-users/browse_thread/thread/b8991d26a8bfc82f">par défaut symfony génère des urls aussi courtes que possible</a>.</p>
<p>Il vous faudra donc changer ce comportement dans factories.yml</p>
<pre class="yml:nogutter" name="code">all:
  routing:
    class: sfPatternRouting
    param:
      generate_shortest_url: false
</pre>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/04/note-sur-les-suffixes-dans-les-routes-symfony-1-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>outils de modélisation pour symfony</title>
		<link>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/</link>
		<comments>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 10:14:18 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[doctrine]]></category>
		<category><![CDATA[gratuit]]></category>
		<category><![CDATA[modélisation]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=818</guid>
		<description><![CDATA[Symfony à radicalement changer mon approche du développement PHP.
En effet l&#8217;approche RAD proposée par ce framework rend d&#8217;autant plus crucial le schéma de base de données, sur lequel est basé l&#8217;application. Ce schéma matérialisé par le fichier schema.yml (ou .xml si vous utilisez propel) est en quelque sorte la feuille de route du développeur agile.
Il [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://www.symfony-project.org/">Symfony</a> à radicalement changer mon approche du développement PHP.</p>
<p>En effet l&#8217;approche <a href="http://fr.wikipedia.org/wiki/D%C3%A9veloppement_rapide_d%27applications">RAD</a> proposée par ce framework rend d&#8217;autant plus crucial le schéma de base de données, sur lequel est basé l&#8217;application. Ce schéma matérialisé par le fichier schema.yml (ou .xml si vous utilisez propel) est en quelque sorte la feuille de route du développeur agile.</p>
<p>Il est intéressant de remarquer qu&#8217;une modélisation graphique de ce schéma donne un point de vue (partiel certes) sur le système d&#8217;information que l&#8217;applicaiton sert &#8230;</p>
<p>Il ya déjà quelques temps déjà j&#8217;avais cherché à automatiser la modification du schéma géré par <a href="http://www.symfony-project.org/">symfony</a> via des outils graphiques (<a href="http://blog.mazenod.fr/2007/11/utiliser-dbdesigner-avec-l-i18n-de-symfony/">Utiliser DBDesigner avec l&#8217;i18n de symfony 1.0</a>)</p>
<ul></ul>
<p>Aujourd&#8217;hui je vous propose un petit tour d&#8217;horizon des solutions à votre disposition selon vos outils et votre orm préférés</p>
<h2>DbDesigner</h2>
<p>si vous utilisez propel</p>
<ul>
<li>Le plugin <a href="http://www.symfony-project.org/plugins/sfDB4toPropelPlugin/1_0_3">sfDB4toPropelPlugin</a> automatisera le processus de conversion du schéma sauvé par DbDesigner en un schema.yml (à la manière des scritps cités précédemment)</li>
</ul>
<p>si vous utilisez Doctrine</p>
<p>le plugin <a href="http://www.symfony-project.org/plugins/sfDbDesignerPlugin">sfDbDesignerPlugin</a> ne s&#8217;installe pas correctement via la commande symfony. En revanche en l&#8217;installant manuellement, i.e. en téléchargeant l&#8217;archive directement sur le site et en la décompressant dans /plugins/sfDbDesignerPlugin puis en activant le plugin dans /config/ProjectConfiguration.class.php</p>
<pre class="php:nogutter:nocontrols" name="code">
&lt;?php

require_once dirname(__FILE__).'/..\lib\vendor\symfony\lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();

class ProjectConfiguration extends sfProjectConfiguration
{
&nbsp;&nbsp;public function setup()
&nbsp;&nbsp;{
&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;enablePlugins('sfDoctrinePlugin');
&nbsp;&nbsp;&nbsp;&nbsp;$this-&gt;enablePlugins('sfDbDesignerPlugin');
&nbsp;&nbsp;}
}
</pre>
<p>vous aurez accès à la task</p>
<pre class="bash:nogutter:nocontrols" name="code">
php symfony dbdesigner:convert-doctrine doc/database.xml
</pre>
<p>Où  doc/database.xml est le path vers le fichier DBDesigner.<br />
Si vous n&#8217;avez pas une application nommée frontend il faudra la passer ne paramètre</p>
<pre class="bash:nogutter:nocontrols" name="code">
php symfony dbdesigner:convert-doctrine --application=myapp doc/database.xml
</pre>
<p>vous obtiendrez alors un fichier &laquo;&nbsp;ready to build&nbsp;&raquo; du schéma modélisé avec DBDesigner dans config/doctrine/schema.yml</p>
<h2>MySQLWorkBench</h2>
<p>Il est à noter que <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> est un fork de <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner4</a>, mais il ne peut pas remplacer ce dernier. En effet si <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> sait parfaitement importer un fichier xml produit par <a href="http://www.fabforce.net/dbdesigner4/">DbDesigner4</a>, il est en revanche incapable d&#8217;exporter ce schéma au format xml de <a href="http://www.fabforce.net/dbdesigner4/">DbDesigner4</a>.  <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> ne permet que d&#8217;enregistrer au format mwb.</p>
<p>Ce format est en fait un zip, qui une fois décompresser donne un fichier xml, mais qui n&#8217;a rien à voir avec le xml généré par <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner4</a>, le fichier xml issu du .wmb est donc inutilisatble avec les deux plugins ci dessus.</p>
<p><a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a> bénéficie en revanche d&#8217;<a href="http://forums.mysql.com/read.php?52,216856,216856">une bibliothèque de plugins</a> qui permettent de l&#8217;utiliser en remplaçant de <a href="http://www.fabforce.net/dbdesigner4/">DBDesigner</a>.</p>
<p>Pour installer un plugin dans<a href="http://www.mysql.fr/products/workbench/"> MySQLWorkbench</a> il suffit de copier le fichier .lua qui contient le code du plugin (du Python Like) dans le répertoire &laquo;&nbsp;modules&nbsp;&raquo; du répertoire d&#8217;installation de <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a></p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/03/plugin_mwb.png" rel="lightbox[818]"><img class="aligncenter size-full wp-image-855" title="plugin_mwb" src="http://blog.mazenod.fr/wp-content/uploads/2010/03/plugin_mwb.png" alt="" width="553" height="245" /></a></p>
<ul>
<li>Si vous utilisez Doctrine <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/">mysql-workbench-doctrine-plugin<sup>1</sup> </a>fonctionne plutôt bien</li>
<li>Si vous utilisez Propel <a href="http://www.diloc.de/blog/2009/10/14/mysql-workbench-propel-export-plugin-v05/">mysql-workbench-propel-export-plugin<sup>2</sup></a> de générer le contenu de schema.xml, alors que <a href="http://trac.symfony-project.org/wiki/SymfonyYamlMyqlWorkbenchPlugin">SymfonyYamlMysqlWorkbenchPlugin<sup>3</sup></a> vous permettra de générer le contenu de schema.yml</li>
</ul>
<h2>Dia</h2>
<p>Si vous utilisez propel, il est possible de modéliser également votre schéma avec <a href="http://projects.gnome.org/dia/">Dia</a> grâce au plugin <a href="http://www.symfony-project.org/plugins/diaToPropelPlugin">diaToPropelPlugin</a>, en utilisant la boite à outil &laquo;&nbsp;Database&nbsp;&raquo; ça va sans dire.</p>
<p>Si vous utilisez Doctrine le plugin <a href="http://trac.symfony-project.org/wiki/ConvertPropelSchemaToDoctrineSchema">ConvertPropelSchemaToDoctrineSchema</a> réalisera la conversion d&#8217;un schéma propel en schéma doctrine.</p>
<h2>ArgoUML</h2>
<p>En graltant un pei plus j&#8217;ai trouvé également le plugin<a href="http://sourceforge.net/projects/uml2symfony"> uml2symfony</a>, qui semble avoir l&#8217;ambition de traduire non pas un schéma de base de données, mais un schéma UML généré avec <a href="http://en.wikipedia.org/wiki/ArgoUML">ArgoUML</a>. Je vous laisse aller plus loin car j&#8217;ai personnellement trouvé l&#8217;<a href="http://www.symfonylab.com/uml2symfony/">idée séduisante</a> mais la documentation très pauvre, voir inexistante!</p>
<h2>Conclusion</h2>
<p>Mon choix se portera sur le doublette gagnante <a href="http://www.mysql.fr/products/workbench/">MySQLWorkbench</a>, <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/">mysql-workbench-doctrine-plugin</a> car:</p>
<ul>
<li><a href="http://www.fabforce.net/dbdesigner4/">DBDesigner </a>n&#8217;est plus maintenu depuis belle lurette (je me rends compte en écrivant ce post que <a href="http://sourceforge.net/projects/dbdesigner-fork/">DBDesigner fork</a> qui a dormi pendant prêt de 3 ans semble revenir à la vie depuis quelques jours)</li>
<li>j&#8217;utilise maintenant Doctrine</li>
<li>ce plugin permet une gestion fine des <a href="http://code.google.com/p/mysql-workbench-doctrine-plugin/wiki/HowToAddDoctrineBehavioursToTheWorkbenchModel">Behaviors Doctrine</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/03/outils-de-modelisation-pour-symfony/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>installation de symfony via PEAR avec UwAmp</title>
		<link>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/</link>
		<comments>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/#comments</comments>
		<pubDate>Sun, 31 Jan 2010 19:43:41 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[gratuit]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[windows]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=827</guid>
		<description><![CDATA[sorti en fin d&#8217;année 2009 UwAmp est un environnement de développement Apache MySQL PHP portable pour windows. Son installation est on ne peut plus simple puisqu&#8217;il suffit de le télécharger et de le dézipper (dans z:\Apps\UwAmp par exemple) pour l&#8217;utiliser.
C&#8217;est sans aucun doute le plus sexy de tous les environnements de développement PHP pour windows [...]]]></description>
			<content:encoded><![CDATA[<p>sorti en fin d&#8217;année 2009 UwAmp est un environnement de développement Apache MySQL PHP portable pour windows. Son installation est on ne peut plus simple puisqu&#8217;il suffit de le télécharger et de le dézipper (dans z:\Apps\UwAmp par exemple) pour l&#8217;utiliser.</p>
<p>C&#8217;est sans aucun doute le plus sexy de tous les environnements de développement PHP pour windows (<a href="http://www.wampserver.com/">wampserver2</a>, successeur du défunt Wamp5, <a href="http://www.apachefriends.org/en/xampp.html">xampp</a> , et <a href="http://www.easyphp.org/">easyPHP</a>), en effet son interface de maintenance est riche et ergonomique</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.2.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-829" title="UwAmp" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.2-268x300.jpg" alt="" width="268" height="300" /></a></p>
<p>Elle permet notamment de passer d&#8217;une version à une autre de PHP en 1 clic, ce qui n&#8217;est pas négligeable.</p>
<p>Elle propose des interfaces de gestion simplifiées pour la configuration de PHP (extensions et variables du fichier  php.ini)</p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-830" title="config extension php" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4-300x234.jpg" alt="" width="300" height="234" /></a></p>
<p style="text-align: center;"><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.4.jpg" rel="lightbox[827]"></a><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.5.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-831" title="config php.ini" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.5-300x234.jpg" alt="" width="300" height="234" /></a></p>
<p>ainsi que pour celle d&#8217;apache (configuration de virtual hosts &amp; modules apache)</p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.6.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-832" title="screenshot.6" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.6-300x265.jpg" alt="" width="300" height="265" /></a></p>
<p><a href="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.7.jpg" rel="lightbox[827]"><img class="aligncenter size-medium wp-image-833" title="screenshot.7" src="http://blog.mazenod.fr/wp-content/uploads/2010/01/screenshot.7-300x265.jpg" alt="" width="300" height="265" /></a></p>
<p>Pour les barbus, les fichiers php.ini et httpd.conf sont toujours accessibles via un bouton, et il est même possible de paramétrer son éditeur de texte  préféré (paths portables acceptés!) en cliquant sur le bouton &laquo;&nbsp;préférences&nbsp;&raquo;.</p>
<p>Dans la séries des raccourcis vous pourrez accéder en un clic</p>
<ul>
<li>à la page d&#8217;accueil de votre répertoire racine &#8211; http://localhost/</li>
<li>au répertoire contenant les virtual hosts &#8211; z:\Apps\UwAmp\www\</li>
<li>à phpMyAdmin &#8211; http://localhost/mysql/ (à noter que le mot de passe root de MySQL par défaut est root)</li>
<li>à un phpinfo() &#8211; http://localhost/uwamp/phpinfo.php</li>
<li>à SQLite Database Browser qui vous permettra de gérer vos bases de données SQLite</li>
</ul>
<p>et en cadeau bonus</p>
<ul>
<li>Les logs access du serveur apache</li>
<li>Les logs error du serveur apache</li>
<li>Les logs du serveur MySQL</li>
<li>à la suppression des fichiers de log sus cités</li>
<li>à la suppression des fichiers de sessions PHP</li>
</ul>
<p>&#8230;</p>
<p>Le petit bémol que je mettrais par rapport à l&#8217;installation de <a href="../2009/11/environnement-de-developpement-portable-pour-symfony-framakey/">xampp portable</a>, que j&#8217;ai déjà documentée, est que PEAR n&#8217;est pas préinstallé.</p>
<p>Pour utiliser les commandes PHP et mysql sans se préoccupper de leur path, suivez <a href="http://blog.mazenod.fr/2009/11/environnement-de-developpement-portable-pour-symfony-framakey/#path">les explications suivantes</a> en remplaçant ;\Apps\xampp\php\;\Apps\xampp\mysql\bin\ par  ;\Apps\UwAmp\apache\php_5.3.1;\Apps\UwAmpp\mysql\bin\. N&#8217;oubliez pas de fermer et de rouvrir votre session pour prendre en compte le nouveau Path (n&#8217;oubliez pas non plus de laisser un commentaire si vous avez une meilleure solution).</p>
<p>ouvrez ensuite un prompt de commande et tapez</p>
<pre class="php:nogutter" name="code">
cd z:\Apps\UwAmp\apache\php_5.31
go-pear.bat
</pre>
<p>si vous ne vous mettez pas dans le dossier  vous allez obtenir un message d&#8217;erreur du genre</p>
<p>Could not open input file: PEAR\go-pear.phar<br />
Appuyez sur une touche pour continuer&#8230;</p>
<p>Sinon PEAR vous demandera si vous souhaitez faire une installation système ou locale. La différence réside essentiellement dans la localisation du fichier pear.ini. Si vous voulez comme moi jouer avec plusieurs installations de PEAR choisissez local. En effet l&#8217;installation système installe le fichier pear.ini dans c:\windows, ce qui le rendra commun à toutes  vos installations de PEAR. L&#8217;installation local permet de ranger le pear.ini dans le répertoire de la commande php auquel CETTE commande PEAR est associée &#8230;</p>
<p>Une fois le choix fait vous pouvez choisir les options par défaut lors du process d&#8217;installation</p>
<p>ensuite pour installer symfony 1.0</p>
<pre class="php:nogutter" name="code">
pear upgrade PEAR
pear channel-discover pear.symfony-project.com
pear install symfony/symfony-1.0.21
symfony -V
</pre>
<p>Vous êtes alors prêt pour <a href="http://www.symfony-project.org/askeet/1_0/en/">askeet</a>.</p>
<p>Pour installer la dernière version courante de symfony1.4.1</p>
<pre class="php:nogutter" name="code">
pear install symfony/symfony
</pre>
<p>Vous êtes alors prêt pour <a href="http://www.symfony-project.org/jobeet/1_4/Doctrine/en/">jobeet</a>.</pre>
<p>L'avantage c'est que vous disposez d'une installation de PEAR par version de PHP, ce qui est idéale pour avoir plusieurs version de symfony sur votre plateforme de développement!</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2010/01/installation-de-symfony-via-pear-avec-uwamp/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>un captcha avec sfCryptographPlugin</title>
		<link>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/</link>
		<comments>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/#comments</comments>
		<pubDate>Tue, 28 Apr 2009 18:47:09 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=249</guid>
		<description><![CDATA[après avoir mis en place sfPropelActAsCommentableBehaviorPlugin, j&#8217;y ai ajouté quelques personnalisations
Intégration de FCK
j&#8217;ai réécrit le template _commentForm.php pour y mettre un éditeur &#171;&#160;riche&#160;&#187; : FCKeditor (il doit bien entendu être configuré au préalable dans le /apps/myapp/config/settings.yml) en modifiant
&#60;?php echo textarea_tag('sf_comment', '', array('cols' =&#62; 40, 'rows' =&#62; 8)) ?&#62;
en
&#60;?php echo textarea_tag('sf_comment', '', 'tool=Basic rich=fck size=40x8 disabled=false')?&#62;
J&#8217;ai [...]]]></description>
			<content:encoded><![CDATA[<p>après avoir mis en place <a href="http://www.symfony-project.org/plugins/sfPropelActAsCommentableBehaviorPlugin">sfPropelActAsCommentableBehaviorPlugin</a>, j&#8217;y ai ajouté quelques personnalisations</p>
<h2>Intégration de FCK</h2>
<p>j&#8217;ai réécrit le template _commentForm.php pour y mettre un éditeur &laquo;&nbsp;riche&nbsp;&raquo; : FCKeditor (il doit bien entendu être configuré au préalable dans le /apps/myapp/config/settings.yml) en modifiant</p>
<pre name="code" class="php:nogutter">&lt;?php echo textarea_tag('sf_comment', '', array('cols' =&gt; 40, 'rows' =&gt; 8)) ?&gt;</pre>
<p>en</p>
<pre name="code" class="php:nogutter">&lt;?php echo textarea_tag('sf_comment', '', 'tool=Basic rich=fck size=40x8 disabled=false')?&gt;</pre>
<p>J&#8217;ai ensuite stocké le fichier modifié au niveau de l&#8217;appli /apps/myapp/modules/sfComment/templates/_commentForm.php afin de ne pas toucher au code du plugin</p>
<p>FCK se sent obligé de mettre une ligne vide en valeur par défaut ce qui fait que la directive required ne suffit pas, puisque ligne vide n&#8217;est pas vide.<br />
J&#8217;ai donc réécrit la validation en tant qu&#8217;anonyme en y ajoutant</p>
<pre name="code" class="yml:nogutter">sf_comment:
  required:
    msg:           The message is required
  # blank from FCK
  sfCallbackValidator:
    callback:      strip_tags
    invalid_error: The message is required
</pre>
<p>J&#8217;ai stocké le fichier modifié au niveau de l&#8217;appli /apps/myapp/modules/sfComment/validate/anonymousComment.yml</p>
<h2>Liens en nofollow</h2>
<p>Mettre un lien en nofollow fait que ce lien n&#8217;est pas suivi par les moteurs de recherche, ce qui évite la transmission du PR &#8230; <a href="http://www.dofollow.fr/">y en a que ça énerve d&#8217;ailleurs le nofollow</a> <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
Pour commencer par supprimer l&#8217;intérêt de laisser un lien dans un commenatire, je me suis fait une fonction noFollow() dans /lib/myTools.php, qui prend en entrée du code html et réécrit en sortie tous les liens avec l&#8217;attribut rel=&nbsp;&raquo;nofollow&nbsp;&raquo;.</p>
<pre name="code" class="php:nogutter">
class myTools
{
  public static function noFollow ($str) {
    $str = stripslashes($str);
    $preg = "/&lt;a href=\"(.*)\"&gt;(.*?)&lt;\/a&gt;/U";
    preg_match_all($preg, $str, $match);
    if(count($match[1]))
    {
        foreach ($match[1] as $key=&gt;$val)
        {
            $pattern[] = '/'.preg_quote($match[0][$key],'/').'/';
            $replace[] = "&lt;a href=\"".$val."\" rel=\"nofollow\"&gt;".$match[2][$key]."&lt;/a&gt;";
        }
        return preg_replace($pattern, $replace, $str);
    }
    else
    {
       return $str;
    }
  }
}
</pre>
<p>J&#8217;ai ensuite réécrit l&#8217;affichage des commentaires en changeant ce passage à la fin de _commentView.php</p>
<pre name="code" class="php:nogutter">
&lt;?php
  echo __('&lt;span&gt;%1%&lt;/span&gt;, &lt;a href="#sf_comment_%2%"&gt;%3%&lt;/a&gt;',
    array(
      '%1%' =&gt; myTools::noFollow($author),
      '%2%' =&gt; $comment['Id'],
      '%3%' =&gt; $date
    )
  )
?&gt;
&lt;div&gt;
  &lt;?php echo myTools::noFollow($comment['Text']); ?&gt;
&lt;/div&gt;
</pre>
<p>En stockant ensuite ce fichier au niveau de mon application /apps/myapp/modules/sfComment/templates/_commentView.yml</p>
<p>Après ça je n&#8217;ai toujours pas réglé réellement le problème du spam &#8230; les bots des spamers ayant une approche pas très fine, ils ne prendront pas la peine de vérifier si leurs liens sont bien suivables par les moteurs de recherche dans les commentaires qu&#8217;ils ont laissés &#8230; en revanche ils vont repasser à intervalles réguliers ce qui est très désagréable!!</p>
<h2>formulaire javascript</h2>
<p>J&#8217;ai donc testé <a href="http://spamcleaner.org/fr/misc/blog-spam.html">une des solutions js proposées sur cette page</a> en remplacant les balise de formulaires dans /apps/myapp/modules/sfComment/templates/_commentForm.php par</p>
<pre name="code" class="js:nogutter">
...
&lt;script language="JavaScript" type="text/javascript"&gt;
    document.write('&lt;?echo form_tag('sfComment/'.$action, $options);?&gt;');
&lt;/script&gt;
...
&lt;script language="JavaScript" type="text/javascript"&gt;
    document.write('&lt;/form&gt;');
&lt;/script&gt;
...
</pre>
<p>Naïf et rapide mais pas suffisant :-/</p>
<h2>Captcha</h2>
<p>Pour régler définitivement le problème j&#8217;ai mis en place  <a href="http://raw.trac.symfony-project.org/wiki/sfCryptographpPlugin">sfCryptographpPlugin</a>.<br />
Comme la doc laisse un peu à désirer, voici une version traduite et adaptée de <a href="http://symfoniac.wordpress.com/2007/05/03/howto-do-a-captcha-in-symfony-with-plugins/">Howto do a Captcha in Symfony</a></p>
<h3>installation</h3>
<pre name="code" class="bash:nogutter">symfony plugin-install http://plugins.symfony-project.com/sfCaptchaPlugin</pre>
<p>activer le module dans le settings.yml de l&#8217;application</p>
<pre name="code" class="yml:nogutter">
all:
  .settings:
    enabled_modules: [default, sfCaptcha]
</pre>
<p>Personnellement je n&#8217;avais ni jpgraph_antispam-digits.class.php, ni jpgraph.class.php dans le répertoire lib/ du plugin je l&#8217;ai donc téléchargé ici <a href="https://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.phphttps://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.phphttps://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.php">https://trac.anl.gov/scavenger/browser/trunk/web/jpgraph/src/jpgraph_antispam-digits.php</a> &#8230; en le sauvant dans lib/jpgraph_antispam-digits.class.php du plugin et changé:</p>
<pre name="code" class="php:nogutter">
require_once 'jpgraph_antispam.class.php';
</pre>
<p>en</p>
<pre name="code" class="php:nogutter">require_once 'jpgraph_antispam-digits.class.php'</pre>
<p>L&#8217;idée c&#8217;est que dans l&#8217;action qui doit afficher le captcha on le sauve (le captcha) dans une session pour ensuite l&#8217;afficher dans le tempate. J&#8217;ai donc ajouté le code suivant dans les actions qui affichent des posts de blog ou des pages. Vu que le captacha est sauvé session il sera forcément le même pour tous les posts lors d&#8217;un affichage par liste :-/</p>
<pre name="code" class="php:nogutter">
$g = new Captcha();
$this-&gt;getUser()-&gt;setAttribute('captcha', $g-&gt;generate());
&lt;span style="font-family: verdana;"&gt;...&lt;/span&gt;
</pre>
<p>il suffit maintenant d&#8217;afficher le captcha dans le template d&#8217;affichage de formulaire de commentaires que nous avons déjà modifié tout à l&#8217;heure, en ajoutant</p>
<pre name="code" class="php:nogutter">
&lt;div class="required"&gt;
  &lt;img src="&lt;?php echo url_for('sfCaptcha/index'); ?&gt;" alt="captcha" /&gt;
  &amp;nbsp; &amp;nbsp; &amp;nbsp;
  &lt;?php echo form_error('captcha'); ?&gt;
  &lt;?php echo input_tag('captcha',''); ?&gt;
&lt;/div&gt;
</pre>
<p>Il reste à modifier le validateur anonymousComment.yml en ajoutant le contrôle du captcha</p>
<pre name="code" class="php:nogutter">
captcha:
  required:
    msg: Please enter the numbers in the captcha image
  captchaValidator:
    error: Incorrect code</pre>
</pre>
<p>reste à effacer le cache du projet </p>
<pre name="code" class="bash:nogutter">symfony cc</pre>
<p>et le captcha devrait fonctionner</p>
<p>Au passage un petit tips qui sert bien quand on joue avec les plugins dans symfony  : <a href="http://symfoniac.wordpress.com/2007/05/28/how-to-fix-a-manually-deleted-plugin-directory/">How to fix a manually deleted plugin directory</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/un-captcha-avec-sfcryptographplugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>internationalisation sans révolution avec symfony (pareil mais mieux!)</title>
		<link>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/</link>
		<comments>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/#comments</comments>
		<pubDate>Mon, 20 Apr 2009 12:47:10 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=234</guid>
		<description><![CDATA[imaginons qu&#8217;on ait un mld de ce genre

Le propos de ce tuto est le suivant
optimiser la navigation multilingue et le référencement avec symfony en ne touchant pas aux urls des link_to existants, sachant que le site n&#8217;est que partiellement traduit, que l&#8217;internaute doit être notifié dans sa langue en cas de contenu non traduit, et [...]]]></description>
			<content:encoded><![CDATA[<p>imaginons qu&#8217;on ait un mld de ce genre<br />
<a href="http://blog.mazenod.fr/wp-content/uploads/2009/09/mcd.gif" rel="lightbox[234]"><img class="size-full wp-image-235 aligncenter" title="mcd" src="http://blog.mazenod.fr/wp-content/uploads/2009/09/mcd.gif" alt="mcd" width="513" height="307" /></a><br />
Le propos de ce tuto est le suivant</p>
<h3>optimiser la navigation multilingue et le référencement avec symfony en ne touchant pas aux urls des link_to existants, sachant que le site n&#8217;est que partiellement traduit, que l&#8217;internaute doit être notifié dans sa langue en cas de contenu non traduit, et que tous les objets du site ne sont pas forcément multilingues</h3>
<h4>référencement et internationalisation</h4>
<p>Plusieurs paramètres permettent à Google de présager de la langue par défaut d&#8217;un site<br />
1 &#8211; L&#8217;extension pays (.fr -&gt; français, .it italien, etc &#8230;)<br />
2 &#8211; Et en cas d&#8217;extension générique (.com, .org, .net, etc &#8230;) la géolocalisation de l&#8217;IP<br />
Ce dernier point est à surveiller avec vigilance: un hébergeur comme <a href="http://1and1.fr/">http://1and1.fr</a> qui possède une IP allemande est susceptible de vous attribuer une IP allemande pour un site a contenu exclusivement français &#8230; ce qui n&#8217;est pas conseillé par <a href="http://seminaires.ranking-metrics.fr/">les tueurs du référencement qui m&#8217;ont fait la formation référencement naturelle</a> <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
Le meilleur moyen de veiller au grain est encore d&#8217;installer <a href="https://addons.mozilla.org/fr/firefox/addon/5791">FlagFox</a>, une <a href="http://blog.mazenod.fr/extensions-firefox-les-indispensables.html">extension firefox indispensable</a> qui  affiche le drapeau du pays où est hébergé une page dans la barre d&#8217;url de votre navigateur (ainsi que beaucoup plus de détails via un simple clic sur le sus dit drapeau).</p>
<p>Bref une fois que la langue par défaut, le nom de domaine et éventuellement la géolocalisation sont en accord, il s&#8217;agit de gérer les autres langues disponibles de manière claire.<br />
En gros j&#8217;ai retenu qu&#8217;il y avait deux écoles:<br />
1 &#8211; Un sous domaine par langue comme dans ce post &#8211; <a href="http://prendreuncafe.com/blog/post/2008/03/14/Symfonians-en-VF">symfonians en VF</a><br />
2 &#8211; Un répertoire dans l&#8217;url par langue comme je vais l&#8217;exposer dans ce billet<br />
D&#8217;après ce que j&#8217;ai compris on pourrait aussi faire figurer la langue dans le stripped_title des url réécrites par exemple</p>
<p>Pour toute la suite on travaille sur un site en .fr (donc google s&#8217;attend par défaut à du français), une application nommée back, et les trois langues gérées sont le français l&#8217;anglais et le chinois</p>
<p>N.B le stripped_title est généré via la fonction stripText suivante <strong>/lib/myTools.php</strong></p>
<pre name="code" class="php:nogutter">public static function stripText($text)
{
   $text = preg_replace('/\ +/', '-', $text);
   $text = preg_replace('/\-$/', '', $text);
   $text = preg_replace('/^\-/', '', $text);
   return $text;
}</pre>
<h4>faire apparaitre la culture dans l&#8217;url</h4>
<p>il s&#8217;agit ici de ne pas utiliser la variable d&#8217;url sf_culture qui courcircuiterait la gestion de la culture et compliquerait pas mal les chose &#8230;<br />
Je propose plutot une routing rule par culture <strong>/apps/back/config/routing.yml</strong></p>
<pre name="code" class="yml:nogutter">sf_cms_content_fr:
  url:   /:stripped_title.html
  param: { module: sfCmsContent, action: show }
sf_cms_content_en:
  url:   /en/:stripped_title.html
  param: { module: sfCmsContent, action: show }
sf_cms_content_zh:
  url:   /zh/:stripped_title.html
param: { module: sfCmsContent, action: show }</pre>
<p>les link_to s&#8217;écrivent alors comme suit</p>
<pre name="code" class="php:nogutter">&lt;?php link_to('link to content', '@sf_cms_content_'.$sfCmsContent-&gt;getCulture().'?stripped_title='.$sfCmsContent-&gt;getStrippedTitle());?&gt;</pre>
<p>L&#8217;idée est donc que l&#8217;on a ou non une langue par défaut qui ne fait pas l&#8217;objet d&#8217;un &laquo;&nbsp;préfixe culture&nbsp;&raquo; dans l&#8217;ur<br />
On peut stocker la cultutre éventuelle par défaut dans <strong>/config/app.yml</strong></p>
<pre name="code" class="php:nogutter">lang:
  default: fr</pre>
<h4>navigation multilingue</h4>
<p>le changement de culture se fait via une action associé à l&#8217;utilisateur <strong>/apps/back/modules/sfCmsUser/actions/actions.class.php</strong></p>
<pre name="code" class="php:nogutter">class sfCmsUserActions extends autosfCmsUserActions

{
   public function executeLanguage() {
      $this-&gt;getUser()-&gt;setCulture($this-&gt;getRequestParameter('culture'));
      $url = $this-&gt;getRequest()-&gt;getReferer() != '' ? $this-&gt;getRequest()-&gt;getReferer() : '@homepage';
      $this-&gt;redirect($url);
   }
}</pre>
<p>L&#8217;affichage des drapeaux se fait comme suit /apps/back/modules/sfCmsLang/tmeplates/_default.php</p>
<pre name="code" class="php:nogutter">&lt;?php if( count( sfConfig::get('app_lang_back') ) &gt; 1):?&gt;
   &lt;?php foreach(sfConfig::get('app_lang_back') AS $lg =&gt; $lang):?&gt;
      &lt;?php if($lg==$sf_user-&gt;getCulture()):?&gt;
         &lt;?php echo image_tag('lang/'.$lg.'_shadow',array('alt'=&gt; $lang, 'title'=&gt;$lang))?&gt;
      &lt;?php else:?&gt;
         &lt;?php echo link_to(image_tag('lang/'.$lg,array('alt'=&gt; $lang, 'title'=&gt;$lang)), 'sfCmsUser/language?culture='.$lg)?&gt;
      &lt;?php endif?&gt;
   &lt;?php endforeach?&gt;
&lt;?php endif?&gt;</pre>
<p>Notez la soumission par méthode get qui permet aux moteur de recherche d&#8217;indexer le contenu multilingue<br />
Enfin j&#8217;ai retouché la classe SfCmsContent comme suit dans /lib/model/SfCmsContent.php</p>
<pre name="code" class="php:nogutter">class SfCmsContent extends BaseSfCmsContent
{
  public function hydrate(ResultSet $rs, $startcol = 1)
  {
    parent::hydrate($rs, $startcol);
    $this-&gt;setCulture(sfContext::getInstance()-&gt;getUser()-&gt;getCulture());
    $lang = array_keys(sfConfig::get('app_lang_'.sfConfig::get('sf_app')));
    $lg = array_pop($lang);
    while($this-&gt;getTitle()=='' &amp;&amp; $lg )
    {
      $this-&gt;setCulture($lg);
      $lg = array_pop($lang);
    }
  }

  public function isTranslated($lg)
  {
    $c = new Criteria();
    $c-&gt;add(SfCmsContentI18nPeer::CULTURE, $lg);
    $c-&gt;addAnd(SfCmsContentI18nPeer::SF_CMS_CONTENT_ID, $this-&gt;getId());
    $sfCmsContentI18n = SfCmsContentI18nPeer::doSelectOne($c);
    if($sfCmsContentI18n &amp;&amp; $sfCmsContentI18n-&gt;getTitle() &amp;&amp; $sfCmsContentI18n-&gt;getStrippedTitle())
    {
      return $sfCmsContentI18n;
    }
    else
    {
      return false;
    }
  }
}</pre>
<p>La réécriture de la méthode <strong>hydrate</strong> permet d&#8217;avoir l&#8217;objet sfCmsContent dans la culture la plus adaptée à celle de l&#8217;internaute (notez que l&#8217;ordre des cultures à un sens)<br />
La méthode <strong>isTranslated</strong> permet de connaître la disponibilité d&#8217;un objet sfCmsContent pour une culture &#8230;</p>
<h4>gérer la cohérence de la langue dans l&#8217;url / éviter le duplicate content</h4>
<p>Maintenant le problème est de gérer la cohérence des urls, en utilisant correctement les routing rules énoncées plus haut, il y a une bonne partie du travail de fait &#8230; le seul problème qui se pose est celui de la culture par défaut qui n&#8217;a pas de préfixe. La conséquence est que les urls des cultures autres que par défaut répondent sur toute leur routing rule &#8230; et même sion a bien soigné la génération des liens en amont, mieux vaut se préserver du duplicate content et éviter les blagues. Voici donc un validateur pour l&#8217;action show qui va vérifier</p>
<ul>
<li>que l&#8217;url contient bien un stripped_title et non pas un simple identifiant</li>
<li>la disponibilité du contenu dans la culture de l&#8217;utilisateur (en le notifiant si le contenu n&#8217;est pas disponible)</li>
<li>la cohérence entre la culture du stripped_title du contenu et celle de l&#8217;utilisateur</li>
<li>la cohérence entre la culture du préfixe et celle du stripped_title</li>
</ul>
<pre name="code" class="php:nogutter">public function validateShow()
{

  // l'url contient bien un stripped_title et non pas un simple identifiant
  if($this-&gt;getRequestParameter('id'))
  {
    $sfCmsContent = SfCmsContentPeer::retrieveByPk($this-&gt;getRequestParameter('id'));
    $this-&gt;redirect('@sf_cms_content_'.$this-&gt;getUser()-&gt;getCulture().'?stripped_title='.$sfCmsContent-&gt;getStrippedTitle());
  }
  elseif($this-&gt;getRequestParameter('stripped_title'))
  {
    $sfCmsContent = SfCmsContentPeer::retrieveByStrippedTitle($this-&gt;getRequestParameter('stripped_title'));
    if($this-&gt;getUser()-&gt;can($sfCmsContent, 'show'))
    {
      $c = new Criteria();
      $c-&gt;add(SfCmsContentI18nPeer::STRIPPED_TITLE, $this-&gt;getRequestParameter('stripped_title'));
      $sfCmsContentI18nFromStrippedTitle = SfCmsContentI18nPeer::doSelectOne($c);

      // disponibilité du contenu dans la culture de l'utilisateur (en le notifiant si le contenu n'est pas disponible)
      if($sfCmsContent-&gt;isTranslated($this-&gt;getUser()-&gt;getCulture()))
      {
        $this-&gt;getUser()-&gt;setAttribute('translation_missing', false);
      }
      else
      {
        $this-&gt;getUser()-&gt;setAttribute('translation_missing', true);
      }

      // cohérence entre la culture du stripped_title du contenu et celle de l'utilisateur
      if($sfCmsContentI18nFromStrippedTitle-&gt;getCulture()!=$this-&gt;getUser()-&gt;getCulture())
      {
        if($sfCmsContent-&gt;isTranslated($this-&gt;getUser()-&gt;getCulture()))
      	 {
      	   $c = new Criteria();
      	   $c-&gt;add(SfCmsContentI18nPeer::SF_CMS_CONTENT_ID, $sfCmsContentI18nFromStrippedTitle-&gt;getSfCmsContentId());
      	   $c-&gt;addAnd(SfCmsContentI18nPeer::CULTURE, $this-&gt;getUser()-&gt;getCulture());
      	   $sfCmsContentUserCulture = SfCmsContentI18nPeer::doSelectOne($c);
      	   $this-&gt;redirect('@sf_cms_content_'.$this-&gt;getUser()-&gt;getCulture().'?stripped_title='.$sfCmsContentUserCulture-&gt;getStrippedTitle());
      	  }
       }
       // cohérence entre la culture du préfixe et celle du stripped_title
      	$uri = split('/',$this-&gt;getRequest()-&gt;getPathinfo());
    	if(strlen($uri[1])==2)
    	{
	  $uri_language = $uri[1];
    	}
    	elseif(sfConfig::get('app_lang_default'))
    	{
	  $uri_language = sfConfig::get('app_lang_default');
    	}
    	if(sfConfig::has('app_lang_default') &amp;&amp; $uri_language != $sfCmsContentI18nFromStrippedTitle-&gt;getCulture())
    	{
	  $this-&gt;redirect('@sf_cms_content_'.$sfCmsContentI18nFromStrippedTitle-&gt;getCulture().'?stripped_title='.$sfCmsContentI18nFromStrippedTitle-&gt;getStrippedTitle());
    	}
     }
     return true;
   }
   else
   {
     $this-&gt;forward404();
   }
 }
}</pre>
<p>Comme on a pris soin de mettre une notification si le contenu n&#8217;est pas disponible dans la culture souhaité, il ne reste plus qu&#8217;à l&#8217;afficher, avec un petit effet scriptaculous histoire d&#8217;attirer l&#8217;attention de l&#8217;internaute<br />
<strong>/apps/back/modules/sfCmsContent/templates/showSuccess.php</strong></p>
<pre name="code" class="php:nogutter">
&lt;?php if($sf_user-&gt;getAttribute('translation_missing')):?&gt;
   &lt;p id="missing_translation"&gt;
      &lt;?php echo __('The content you are looking for is missing for your language')?&gt;
   &lt;/p&gt;
   &lt;?php echo javascript_tag(
      visual_effect('highlight', 'missing_translation',
         array(
            'duration' =&gt; 2
         )
      )
   )?&gt;
&lt;?php endif?&gt;
&lt;?php echo $sfCmsContent->getHtmlBody();?&gt;
</pre>
<p>N.B pour chaque nouvel objet multilingue il faudra</p>
<ul>
<li>ajouter une routing rules par culture</li>
<li>réécrire les méthode hydrate et isTranslated</li>
<li>adapter le validateur validateShow</li>
<li>modififer le template pour faire apparaître la notification</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/internationalisation-sans-revolution-avec-symfony-pareil-mais-mieux/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>googlebot notifier</title>
		<link>http://blog.mazenod.fr/2009/04/googlebot-notifier/</link>
		<comments>http://blog.mazenod.fr/2009/04/googlebot-notifier/#comments</comments>
		<pubDate>Mon, 13 Apr 2009 22:21:48 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[bricolage]]></category>
		<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>
		<category><![CDATA[www]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=225</guid>
		<description><![CDATA[En lisant ce post, &#171;&#160;Être notifié du passage de Google sur son site&#160;&#187; de William DURAND, je me suis fendu d&#8217;une adaptation symfony à base d&#8217;un petit filtre &#8230;
Et je crois que Googlebot est justement en train de passer  

&#60;?php class sfGooglebotNotifierFilter extends sfFilter
{
   public function execute($filterChain)
   {
   [...]]]></description>
			<content:encoded><![CDATA[<p>En lisant ce post, <a href="http://www.willdurand.fr/posts/11/etre-notifie-du-passage-de-google-sur-son-site.html">&laquo;&nbsp;Être notifié du passage de Google sur son site&nbsp;&raquo; de William DURAND</a>, je me suis fendu d&#8217;une adaptation symfony à base d&#8217;un petit filtre &#8230;<br />
Et je crois que Googlebot est justement en train de passer <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_biggrin.gif' alt=':-D' class='wp-smiley' /> </p>
<pre name="code" class="php:nogutter:nocontrols">
&lt;?php class sfGooglebotNotifierFilter extends sfFilter
{
   public function execute($filterChain)
   {
      if (preg_match('#Googlebot#i', $this-&gt;getContext()-&gt;getRequest()-&gt;getHttpHeader('User-Agent')))
      {
         $mail = new sfMail();
         $mail-&gt;initialize();
         $mail-&gt;setMailer('sendmail');
         $mail-&gt;setCharset('utf-8');
         $mail-&gt;setSender('mailer@monsite.com', 'mailer de monsite.com');
         $mail-&gt;setFrom('mailer@monsite.com', 'mailer de monsite.com');
         $mail-&gt;addAddress('your@mail.com');
         $mail-&gt;setSubject('Google bot visit');
         $mail-&gt;setBody($this-&gt;getContext()-&gt;getRequest()-&gt;getHttpHeader('User-Agent').' has visited '.$this-&gt;getContext()-&gt;getRequest()-&gt;getUri());
         $mail-&gt;send();
      }
      $filterChain-&gt;execute();
   }
}
 ?&gt;
</pre>
<p>N.B. Les passages du googlebots sont aussi disponibles dans les rapports de Googel Analytics ( visiteurs, capacités du navigateur, navigateurs).<br />
à intégrer comme suit dans /apps/myapp/config/filters.yml</p>
<pre name="code" class="yml:nogutter:nocontrols">
rendering: ~
web_debug: ~
sf_googlebot_notifier:
  class: sfGooglebotNotifierFilter
security:  ~
# generally, you will want to insert your own filters here
cache:     ~
common:    ~
flash:     ~
execution: ~
</pre>
<p>J&#8217;allais oublier un gand merci au même William Durand pour son autre post <a href="http://www.willdurand.fr/posts/10/syntaxhighlighter-tinymce-et-yml.html">SyntaxHighlighter, TinyMCE et YML</a>, qui m&#8217;a permis d&#8217;intégrer la coloration syntaxique YAML à syntaxHighlighter2 via la brush qu&#8217;il a conçue <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>autre ressource: <a href="http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes:Custom">http://alexgorbatchev.com/wiki/SyntaxHighlighter:Brushes:Custom</a></p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/googlebot-notifier/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>symfony fête son 500ème pulgin</title>
		<link>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/</link>
		<comments>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/#comments</comments>
		<pubDate>Wed, 08 Apr 2009 13:55:19 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=212</guid>
		<description><![CDATA[Le système de plugin est un atout majeur du framework PHP symfony. Il permet de ne pas réinventer la roue à chaque projet : De la gestion d&#8217;utilisateur, au comportement taggable, en passant par le moteur de recherche interne basé sur Lucene, l&#8217;éventail s&#8217;élargit tous les jours (cf la courbe ci dssous)

C&#8217;est pourquoi il est [...]]]></description>
			<content:encoded><![CDATA[<p>Le <a href="http://www.symfony-project.org/plugins/">système de plugin</a> est un atout majeur du <strong>framework PHP symfony</strong>. Il permet de ne pas réinventer la roue à chaque projet : De la gestion d&#8217;utilisateur, au comportement taggable, en passant par le moteur de recherche interne basé sur Lucene, l&#8217;éventail s&#8217;élargit tous les jours (cf la courbe ci dssous)</p>
<p style="text-align: center;"><a href="http://www.symfony-project.org/blog/2009/04/07/tell-us-the-plugins-you-use"><img class="size-full wp-image-213 aligncenter" title="plugin_creation_rate" src="http://blog.mazenod.fr/wp-content/uploads/2009/09/plugin_creation_rate.png" alt="plugin_creation_rate" width="450" height="278" /></a></p>
<div style="text-align: left;">C&#8217;est pourquoi il est maintenant possible de connaître la <a href="http://www.symfony-project.org/plugins/popular/">popularité d&#8217;un plugin symfony</a> avant de l&#8217;intégrer à son projet <img src='http://blog.mazenod.fr/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>
<p>La liste de mes préférés:</p></div>
<ul>
<li><a href="http://www.symfony-project.org/plugins/sfLucenePlugin">sfLucenePlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelActAsTaggableBehaviorPlugin">sfPropelActAsTaggableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelActAsRatableBehaviorPlugin">sfPropelActAsRatableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfThumbnailPlugin">sfThumbnailPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfTagtoolsPlugin">sfTagtoolsPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfFeed2Plugin">sfFeed2Plugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/">sfPropelActAsCommentableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfPropelVersionableBehaviorPlugin">sfPropelVersionableBehaviorPlugin</a></li>
<li><a href="http://www.symfony-project.org/plugins/sfSimpleForumPlugin">sfSimpleForumPlugin</a></li>
</ul>
<p>N.B. ce sont mes plugins préférés pour <strong>symfony 1.0</strong> &#8230; je ne désepère pas de trouver 24h00 pour me fader <a href="http://www.symfony-project.org/jobeet/1_2/">jobeet</a> et passer à la 1.2:-/</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/04/symfony-fete-son-500eme-pulgin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>menu déroulant prototype</title>
		<link>http://blog.mazenod.fr/2009/03/menu-deroulant-prototype/</link>
		<comments>http://blog.mazenod.fr/2009/03/menu-deroulant-prototype/#comments</comments>
		<pubDate>Tue, 10 Mar 2009 08:52:17 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=186</guid>
		<description><![CDATA[Attention c&#8217;est tout une histoire!
Au départ était une simple demande &#171;&#160;nous voulons un menu comme celui de science po!!&#160;&#187;. En général j&#8217;aime bien ce genre de demande parce qu&#8217;on sait où on va et en jetant un oeil au code (si tant est que ce soit du JS et / ou du CSS ça va [...]]]></description>
			<content:encoded><![CDATA[<p>Attention c&#8217;est tout une histoire!<br />
Au départ était une simple demande &laquo;&nbsp;nous voulons un menu comme celui de <a style="text-decoration: none;" href="http://www.sciences-po.fr/portail/">science po</a>!!&nbsp;&raquo;. En général j&#8217;aime bien ce genre de demande parce qu&#8217;on sait où on va et en jetant un oeil au code (si tant est que ce soit du JS et / ou du CSS ça va sans dire) on a rapidement une base de travail.</p>
<p>Il se trouve que là ça partait mal parce que science po est plutôt orienté JQuery et moi plutôt prototype (distribué en natif avec symfony) &#8230; par expérience, les deux librairies ne sont pas compatibles, et en plus je n&#8217;ai pas l&#8217;intention de faire charger deux librairies JS à mes clients: quesiton d&#8217;éthique!</p>
<p>Primo en googlant j&#8217;affine un peu ce que je veux : un menu accordeon écrit avec la librairie prototype.<br />
Une fois formulée ça fait pas un pli, je tombe sur ce tuto, qui paraît pil poil répondre à mes besoins: <a style="text-decoration: underline;" href="http://nettuts.com/javascript-ajax/create-a-simple-intelligent-accordion-effect-using-prototype-and-scriptaculous/">Create a Simple, Intelligent Accordion Effect Using Prototype and Scriptaculous</a>.</p>
<p>Le net c&#8217;est génial me dis je, en essayant de faire fonctionner ce tuto dans mon projet symfony. Il se trouve que les librairies prototype sont automatiquement chargées à chaque page de mon projet puisque mon moteur de recherche, inclus dans toutes les pages, utilise l&#8217;auto-complétion et que cette dernière est gérée par des helpers ajax &#8230;<br />
Donc je n&#8217;ai qu&#8217;à inclure le script du tuto, parmis mes js et le tour est joué.<br />
Oui mais il faut que le js du menu soit inclus après les librairires prototypes (en JS en tout cas il n&#8217;y a pas le choix). Soit, j&#8217;ai déjà eu à gérer l&#8217;ordonnancement de mes CSS via le fichier view.yml :</p>
<pre name="code" class="php:nogutter">stylesheets:    [main, menu, back: { position: last }]</pre>
<p>je tente la même chose avec mon javascript</p>
<pre name="code" class="php:nogutter">javascripts:     [menu: { position: last }]</pre>
<p>Mais là c&#8217;est la déception, ça ne fonctionne pas du tout, il n&#8217;y a pas de moyen propre d&#8217;ordonnancer l&#8217;inclusion de ces JS dans symfony (1.0 en tout cas!)<br />
Re-googling et c&#8217;est <a style="text-decoration: none;" href="http://ruzz.onsymfony.com/post/20591935/symfony-js-include-order">ruzz</a> qui me donne un bon tuyau. J&#8217;adapte le tout en passant par un filtre sfCmsCustomJs.php</p>
<pre name="code" class="php:nogutter">
class sfCmsCustomJs extends sfFilter
{
    public function execute($filterChain)
    {
           if($this-&gt;isFirstCall())
           {

              sfContext::getInstance()-&gt;getResponse()-&gt;setParameter(sfConfig::get('sf_prototype_web_dir').'/js/effects.js',sfConfig::get('sf_prototype_web_dir').'/js/effects.js','helper/asset/auto/javascript');
              sfContext::getInstance()-&gt;getResponse()-&gt;setParameter('menu.js','menu.js','helper/asset/auto/javascript');
              // ajout de javascript conditionné par PHP dans l'entête
             $CustomDynamicJs = "
                <script type="\&quot;text/javascript\&quot;"> alert('".$var."');</script>";

             $response = $this-&gt;getContext()-&gt;getResponse();
             $response-&gt;setContent(str_ireplace('', $CustomDynamicJs.'',$response-&gt;getContent()));
           }
     }
}</pre>
<p>C&#8217;est pas ce que j&#8217;ai fait de plus élégant, mais ça a le mérite de (bien fonctionner) et de permettre d&#8217;ajouter simplement du JS conditioné par PHP.</p>
<p>Bref je trouvais déjà que ça faisait beaucoup pour un simple menu jusqu&#8217;à ce que je constate que ce menu ne marche pas .<br />
Mieux je constate qu&#8217;il est écrit en prototype 1.6.0 et que mon install de symfony (1.0.19) embarque la version 1.5.0 &#8230;<br />
Fin du fin les deux versions n&#8217;ont pas l&#8217;air super compatibles.</p>
<p>Donc Re-Re-Googling : pour un amenu accordéon écrit avec prototype 1.5.0.<br />
J&#8217;ai donc fini par opter pour <a style="text-decoration: none;" href="http://www.stickmanlabs.com/accordion/">ce menu en accordéon</a> qui fait, une fois adapté ce qui n&#8217;est pas rien, très bien l&#8217;affaire.<br />
Un big Up pour Kevin Miller qui rêve d&#8217;un mac book pro &#8230; n&#8217;hésitez pas</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/03/menu-deroulant-prototype/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>sfTagtoolsPlugin</title>
		<link>http://blog.mazenod.fr/2009/03/sftagtoolplugin/</link>
		<comments>http://blog.mazenod.fr/2009/03/sftagtoolplugin/#comments</comments>
		<pubDate>Mon, 02 Mar 2009 09:02:00 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=196</guid>
		<description><![CDATA[Pré-requis: à lire avant tout
sfPropelActAsTaggablePlugin doit être installé. Ce plugin offre des fonctionnalités additionnelles qui utilisent ce plugin. Il n&#8217;ya pas moyen de les utiliser sans ce plugin.
Pour utiliser la page d&#8217;administration et les fonctionnalités de recherche incrémental, vous devez avoir prototype (prototype.js) charger dans vos pages.
Introduction
Ce plugin offre plusieurs fonctionnalités additionnelles autour des fonctionnalités [...]]]></description>
			<content:encoded><![CDATA[<h2>Pré-requis: à lire avant tout</h2>
<p>sfPropelActAsTaggablePlugin doit être installé. Ce plugin offre des fonctionnalités additionnelles qui utilisent ce plugin. Il n&#8217;ya pas moyen de les utiliser sans ce plugin.</p>
<p>Pour utiliser la page d&#8217;administration et les fonctionnalités de recherche incrémental, vous devez avoir prototype (prototype.js) charger dans vos pages.</p>
<h2>Introduction</h2>
<p>Ce plugin offre plusieurs fonctionnalités additionnelles autour des fonctionnalités standards déjà offeretes par l&#8217;excellent sfPropelActAsTaggablePlugin. Ce plugin est moins mature que sfPropelActAsTaggablePlugin mais offre un peu de matière pour développer une interface utilisateur</p>
<h2>Contact</h2>
<p>sfTagtoolsPlugin a été créé par l&#8217;équipe [http://www.punkave.com/ P'unk Avenue]. Veuillez adresse les questions, inquiétudes et patchs à [mailto:tom@punkave.com Tom Boutell].</p>
<h2>Fonctionnalités</h2>
<ul>
<li>recherche inccrémental pour les tags, avec auto complétion sur validation clavier ou clic de souris. Similaires aux solutions flickr, avec lesquels beaucoup d&#8217;utilisateurs cont déjà familier. Voir: lib/helper/tagaheadHelper.php</li>
<li>Le concept de tags mis &laquo;&nbsp;en valeur&nbsp;&raquo;. C&#8217;est juste une manière pratique de mettre certains tags en valeur pour les besoisn de votre propre interface utilisateur Si un tag possède un objet !FeatureTag associé, vous savez que c&#8217;est un tag mis en valeur. Nous offrons cette possibilité de &laquo;&nbsp;mise en valeur&nbsp;&raquo; dans &#8230;</li>
<li>Une page d&#8217;administration ajaxifiée pour renommer, fusionner, supprimer, prioriser et mettre en valeur les tags. Notez que vous ne devez pas forcément d&#8217;utiliser les !FeaturedTag dans la page d&#8217;administration, c&#8217;est une fonctionnalité optionnelle.</li>
<li>Le concept de tags priorisés, implémté par la classe !PriorityTag. Un tag avec une priorité haute apparaîtra en premier dans les résultats retournés par sfTagtoolsToolkit::getBeginningWith(). Une fois de plus cette fonctionnalité est optionnelle, vous pouvez utiliser la recherche incrémental et la page d&#8217;administration sans !PriorityTag. &laquo;&nbsp;Mais qu&#8217;est ce donc que sfTagtoolsToolkit::getBeginningWith()?&nbsp;&raquo; vous demandez vous. Cela nous donne:</li>
<li>Une façon de retrouver les tags classés par ordre de popularité, et de les restreindre éventuellement à ceux commençant par une chaîne de caractère donnée. Voir sfTagtoolsToolkit::getBeginningWith(). Cette méthode a vu le jour en tant que partie de la recherche incrémentale mais, avec une chaîne de caractère vide comme début, est pratique pour créer des tags aussi</li>
<li>Une façon d&#8217;itérer sur les objets d&#8217;un modèle taggable en particulier qui seraient liés à un ensemble défini de tags, de les classer par ordre descendant de tag associé, avec la notion de priorité des tags permet une possibiolité de c lassement supplémentaire.</li>
</ul>
<h2>Philosophie</h2>
<p>sfPropelActAsTaggablePlugin tiens sa magie du fait qu&#8217;il trie les nom des classes d&#8217;objets taggés comme une chaîne de caractère en base de données (taggable_model). C&#8217;est très bien dans la plupart des cas mais cela pose des problèmes quand il s&#8217;agit de classé les choses de manière sémantique et que les taggages éventés qui pointent vers des objets morts ne sont pas pris en compte. Cela pourrait être vu en tant qu&#8217; extension pour une clause SQL IN (utilisé par la méthode de retrieveByPKs des classes peer classes Propel), et le code de Xavier Lacot&#8217;s fait un usage héroïque de cette fonctionnalité. De toute façon ces considérations ont tendance à devenir moins importante, disons le, il s&#8217;agit de compter rapidement les objets taggés d&#8217;un modèle en particullier sans avoir les coûts liés aux méthodes d&#8217;hydratation (pour la construction d&#8217;une page d&#8217;administration par exemple). Cela devient moins important également si vous avez des centaines de milliers d&#8217;enregistrement, créés dans des clauses IN trop grande pour un paquet MySQL (qui ne fonctionne pas de toute façon). C&#8217;est vrai qu&#8217;un identifiant est plus léger qu&#8217;un objet entier, mais quand vous en avez assez c&#8217;est encore mauvais.</p>
<p>Donc si vous voulez compter les objets taggés d&#8217;un modèle en particulier qui exist encore et qui réponde à un critère de &laquo;&nbsp;vie&nbsp;&raquo; ou de &laquo;&nbsp;publication&nbsp;&raquo; sans les hydrater &#8230; bienvenue dans le monde de la requête SQL personnalisée! heureusement j&#8217;ai codé ces requêtes pour vous!<br />
Là aussi vous pouvez ne pas l&#8217;utiliser, sfTagtoolsPlugin fonctionne mieus quand vous décrivez vos classes taggables via app.yml. Cela permet à sfTagtoolsPlugin de palier à son design limiter de sfPropelActAsTaggablePlugin en joignant les tables contenant les classes quand une requête personnalisée ne cherche qu&#8217;à récupérer les informations souhaitées.</p>
<p>&laquo;&nbsp;Est que ce n&#8217;est pas encore un peu lent?&nbsp;&raquo; il y a encore une petite perte de performance sur les tags et le taggage seuls, mais ce n&#8217;est pas signifiant en terme de &laquo;&nbsp;big-O notation&nbsp;&raquo; (au moins pas si mYSQL fait son travaille et qu&#8217;il optimise correctement les requêtes). Enfin, si vous avez un zillion de classes taggable, vous devriez plutôt avoir une seule classe taggable, et la peupler à partir des tables des vrais objets. Sigh&#8230; Ce serait opas génial si Propel comprenait l&#8217;héritage multiple</p>
<h2>Installation</h2>
<p>Installez le plugin. Okay, ce n&#8217;est pas si simple que ça.</p>
<p>vous devez activer le module  tagtools dans votre application. dans mon apps/frontend/config/settings.yml, J&#8217;ai:</p>
<pre name="code" class="php:nogutter:nocontrols">
# Activated modules from plugins or from the symfony core
enabled_modules:        [default, sfGuardAuth, sfGuardUser, sfGuardGroup, sfGuardPermission, tagtools]
</pre>
<p>Vote liste de modules peut bien entendu varier, assurez vous simplement d&#8217;avoir tagtools ajouter à la liste des enabled_modules dans votre configuration.</p>
<p>Vous devrez aussi avoir prototype dans vos pages. dans la section default de votre apps/frontend/config/view.yml, vous pouvez utiliser:</p>
<pre name="code" class="php:nogutter:nocontrols">
javascripts:    [%SF_PROTOTYPE_WEB_DIR%/js/prototype]
</pre>
<p>Pour utiliser la recherche incrémental, vous aurez aussi besoin de tagahead.js:</p>
<pre name="code" class="php:nogutter:nocontrols">
javascripts:    [%SF_PROTOTYPE_WEB_DIR%/js/prototype, %SF_WEB_ROOT%/tagtools/js/tagahead.js]
</pre>
<p>Votre site dervait utiliser d&#8217;autres librairies javascript qui devrait être chargées via une liste séparée par des virgules.</p>
<p>Enfin pour que la recherche incrémentale (&laquo;&nbsp;tagahead&nbsp;&raquo;)  fonctionne bien, vous devez avoir un minimum de configuraiton CSS pour un affichage correct des tags suggérés. Voir sfTagtoolsPlugin/web/css/suggested.css pour plus de détails.</p>
<p>C&#8217;est tout pour l&#8217;essentiel de la configuration. Mais pour tirer pleinement parti du plugin, vous devriez décrire vos classes taggables dans votre app.yml. Et vous devirez vouloir activer le support optionnel des !FeaturedTag et !PriorityTag, bien que le plugin fonctionne bien sans. Voici un exemple:</p>
<pre name="code" class="php:nogutter:nocontrols">
all:
  sfTagtoolsPlugin:
    featured: true
    priority: true
    taggables:
      - name: Release
        label: Releases
        cssclass: releases
      - name: PressKit
        label: Presskits
        cssclass: presskits
      - name: Media
        label: Media
        cssclass: media
    criteria_live: "TAGGABLE.live = true"
    criteria_public: "TAGGABLE.published = true"
</pre>
<p>&laquo;&nbsp;Que sont criteria_live et criteria_public?&nbsp;&raquo; Ces critère sont appliqués pour décider si l&#8217;objet taggé fait référence à un objet en vie. So votre application, comme la pluaprt des notres, implique des objets que l&#8217;utilisateur abandonne en cours de création, alors vous voyez de quoi je parle. le critère criteria_public est identique, Mais il est appliqué seulement si l&#8217;utilisateur est connecté (isAuthenticated a échoué sur le sfUser courant). le mot TAGGABLE est automatiquement rempalcé par le nom de la table associée à l&#8217;objet quand la requête est lancée.</p>
<p>&laquo;&nbsp;Que sont label et cssclass?&nbsp;&raquo; Elles sont utilisés pour la page d&#8217;administration, elles sont donc otpionnelles si vous n&#8217;utilisez pas la page d&#8217;administration. Vous aurez besoin du nom de du champs, qui doit être un nom de classe PHP (pas un nom de table) pour ce type d&#8217;objet.</p>
<p>&laquo;&nbsp;Puis je utiliser la recherche incrémentale sans aucunes de ces configurations?&nbsp;&raquo; Oui, mais sfTagtools ne sera pas en mesure de distinguer les liens vers des objets morts des liens vers des objets vivants. Donc je recommande fortement que vous définissiez au moins vos modèles taggables.</p>
<pre name="code" class="php:nogutter:nocontrols">
sfTagtoolsPlugin:
  taggables:
    - name: Release
      label: Releases
      cssclass: releases
    - name: PressKit
      label: Presskits
      cssclass: presskits
    - name: Media
      label: Media
      cssclass: media
</pre>
<p>Autrement vous verrez dans les suggestions de la recherche incrémentale des tags qui n&#8217;ont plus lieu d&#8217;être, peut être pour une raison valable. Vous verrez aussi un compte gonflé des occurences total pour chaque tag dans le module d&#8217;amdinistration, parce que les taggages ne sont jamais supprimés et que sfTagtools n&#8217;a pas assez d&#8217;informations pour faire la jointure avec les modèles taggables et compter seulement les taggages qui ont de l&#8217;importance.</p>
<p>n&#8217;oubliez pas de vider le cache de votre applicaiton &laquo;&nbsp;symfony cc&nbsp;&raquo;!</p>
<h2>Implementer la recherche incrémentale</h2>
<p>Pour implémenter la recherche incrémentale, utiliser un code comme le suivant:</p>
<pre name="code" class="php:nogutter:nocontrols">
&lt;?php use_helper("tagahead") ?&gt;
&lt;div&gt;
&lt;label for="tags"&gt;Tags&lt;/label&gt;
&lt;?php echo tagahead_input_tag("tags", $tags) ?&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;label for="tags-suggestions"&gt;Suggested Tags&lt;/label&gt;
&lt;div id="tags-suggestions"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;?php echo tagahead_observer('tags') ?&gt;
</pre>
<p>le helper tagahead_input_tag génère une balise input avec le nom spécifié (qui sera aussi son ID). Le helper tagahead_observer génère un code Javascript pour mettre à jour dynamiquement la liste des tags suggérés basée sur ce que l&#8217;utilisateur a déjà tapé..</p>
<h2>Implemener la page d&#8217;amdinistration</h2>
<p>La page de &#8216;administration des tags  a été activée en meêm tant que le module des tags. Elle est aussi sécurisée avec un règle sfTagtoolsPlugin/modules/tagtools/config/security.yml. Cette règle empçeche simplement les utilisateurs d&#8217;accéder à cette page s&#8217;ils ne sont pas authentifiés. Si votre site possède plusieurs niveau de permissions, vous n&#8217;aurrez qu&#8217;à ajuster cette configuration pour que seul les administrateurs puissent supprimer, renommer et fucionner les tags!</p>
<p>Pour accéder à la page d&#8217;administration, visitez simplement:</p>
<pre name="code" class="php:nogutter:nocontrols">http://yoursite/tagtools/admin</pre>
<p>Une fois de plus, vous devez être authentifié si vous utilisez le security.yml livré avec le plugin.</p>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2009/03/sftagtoolplugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>sfTCPDFPlugin</title>
		<link>http://blog.mazenod.fr/2008/10/sftcpdfplugin/</link>
		<comments>http://blog.mazenod.fr/2008/10/sftcpdfplugin/#comments</comments>
		<pubDate>Fri, 31 Oct 2008 12:34:14 +0000</pubDate>
		<dc:creator>mazenovi</dc:creator>
				<category><![CDATA[dev]]></category>
		<category><![CDATA[symfony]]></category>

		<guid isPermaLink="false">http://blog.mazenod.fr/?p=182</guid>
		<description><![CDATA[
la classe met à disposition une couche d&#8217;abstraction pour la  librarie TCPDF. L&#8217;intérêt principal de cette extension de FPDF réside dans le fait que l&#8217;on peut manipuler n&#8217;importe quelle chaîne de carctères en UTF-8.
Installation

Installer le plugin

symfony plugin-install http://plugins.symfony-project.com/sfTCPDFPlugin
ou téléchargez le package et décompresser le dans le répertoire /plugins
téléchargez la  librarie TCPDF
Décompressez là dans [...]]]></description>
			<content:encoded><![CDATA[<div>
<p>la classe met à disposition une couche d&#8217;abstraction pour la  <a href="http://www.tecnick.com/public/code/cp_dpage.php?aiocp_dp=tcpdf">librarie </a><a href="http://www.tecnick.com/public/code/cp_dpage.php?aiocp_dp=tcpdf">TCPDF</a>. L&#8217;intérêt principal de cette extension de FPDF réside dans le fait que l&#8217;on peut manipuler n&#8217;importe quelle chaîne de carctères en UTF-8.</p>
<h2>Installation</h2>
<ul>
<li>Installer le plugin
<pre name="code" class="php:nogutter:nocontrols">
symfony plugin-install http://plugins.symfony-project.com/sfTCPDFPlugin</pre>
<p>ou téléchargez le package et décompresser le dans le répertoire /plugins</li>
<li>téléchargez la <a href="http://sourceforge.net/project/showfiles.php?group_id=128076"> librarie </a><a href="http://sourceforge.net/project/showfiles.php?group_id=128076">TCPDF</a></li>
<li>Décompressez là dans le répertoire <strong>/plugins/sfTCPDFplugin/lib</strong></li>
</ul>
<p>A ce niveau là vous devriez avoir un répertoire <strong>tcpdf</strong> dans <strong>/plugins/sfTCPDFPlugin/lib</strong></p>
<ul>
<li>copiez les fichiers de <strong>/web/</strong> directement dans le répertoire <strong>/web</strong> de votre application</li>
<li>supprimez le cache
<pre name="code" class="php:nogutter:nocontrols">
symfony cc</pre>
</li>
</ul>
<h2>Configuration</h2>
<ul>
<li>initialisez correctement <strong>sf_tcpdf_dir</strong> dans le <strong>config.php</strong> de votre application si la librairie TCPDF n&#8217;est pas dans le répertoire <strong>/plugins/sfTCPDFplugin/lib</strong> (vérifiez <strong>/plugins/sfTCPDFPlugin/config/config.php</strong>)</li>
<li>Si vous voulez des paramètres distincts pour chaque PDF généré vérifiez <strong>/plugins/sfTCPDFPlugin/config/config.php</strong> et <strong>tcpdf/config/tcpdf_config.php</strong>.</li>
<li>Si vous voulez tester, activez le module <strong>sfTCPDF</strong> dans votre fichier <strong>settings.yml</strong>, et ensuite appelez l&#8217;url <strong>sfTCPDF/test</strong> ou <strong>sfTCPDF/test2</strong></li>
</ul>
<h2>Usage</h2>
<pre name="code" class="php:nogutter:nocontrols">
//Hello World test (sfTCPDF/test)
public function executeTest() {
  // pdf object
  $pdf = new sfTCPDF();
  // settings
  $pdf-&gt;SetFont("FreeSerif", "", 12);
  $pdf-&gt;SetMargins(PDF_MARGIN_LEFT, PDF_MARGIN_TOP, PDF_MARGIN_RIGHT);
  $pdf-&gt;setHeaderFont(array(PDF_FONT_NAME_MAIN, *, PDF_FONT_SIZE_MAIN));
  $pdf-&gt;SetHeaderData(PDF_HEADER_LOGO, PDF_HEADER_LOGO_WIDTH, PDF_HEADER_TITLE, PDF_HEADER_STRING);
  $pdf-&gt;setFooterFont(array(PDF_FONT_NAME_DATA, *, PDF_FONT_SIZE_DATA)); $pdf-&gt;SetHeaderMargin(PDF_MARGIN_HEADER);
  $pdf-&gt;SetFooterMargin(PDF_MARGIN_FOOTER);
  // init pdf doc
  $pdf-&gt;AliasNbPages();
  $pdf-&gt;AddPage();
  $pdf-&gt;Cell(80, 10, "Hello World !!! &amp; é € U û ???");
  // output
  $pdf-&gt;Output();
  return sfView::NONE;
}
</pre>
</div>
]]></content:encoded>
			<wfw:commentRss>http://blog.mazenod.fr/2008/10/sftcpdfplugin/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
