<?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>Expériences</title>
	<atom:link href="http://www.enisseo.net/blog/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.enisseo.net/blog</link>
	<description></description>
	<lastBuildDate>Sun, 20 Jun 2010 16:01:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Agile-compatible standard format for commit messages</title>
		<link>http://www.enisseo.net/blog/2010/06/20/agile-compatible-standard-format-for-commit-messages/</link>
		<comments>http://www.enisseo.net/blog/2010/06/20/agile-compatible-standard-format-for-commit-messages/#comments</comments>
		<pubDate>Sun, 20 Jun 2010 16:00:54 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Conception]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[bazaar]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[commit]]></category>
		<category><![CDATA[git]]></category>
		<category><![CDATA[mercurial]]></category>
		<category><![CDATA[message]]></category>
		<category><![CDATA[scm]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=132</guid>
		<description><![CDATA[When commiting changesets, I now write my messages using a special format that seems to me &#8220;Agile-compatible&#8221;. Each line of the message looks like the following: COD Description, where Description is a technical or (as in XOR) client-oriented description of the changeset (the user story summary, for example) and COD is one of: NEW: a [...]]]></description>
			<content:encoded><![CDATA[<p>When commiting changesets, I now write my messages using a special format that seems to me  &#8220;Agile-compatible&#8221;. Each line of the message looks like the following: <code>COD Description</code>, where <code>Description</code> is a technical or (as in XOR) client-oriented description of the changeset (the user story summary, for example) and <code>COD</code> is one of:</p>
<ul>
<li><code>NEW</code>: a new feature or a new element of source code,</li>
<li><code>IMP</code>: an improvement of one of the features of the projet or of the architecture/source code,</li>
<li><code>FIX</code>: a fix of a bug,</li>
<li><code>REM</code>: a removal of a feature or an element of the project (library, useless files&#8230;) -this is rarely used.</li>
</ul>
<p>The Agile compatibility comes from the fact that if every commit must have a message and every message must follow the format, the commiter/developer can only do changes that can be labeled as a new feature, a fix, a feature removal or a (real) improvement. As an example, rewriting a function can only be a part of a changeset if it improves the project in some way: better performances, better readibility.</p>
<p>Some real world examples:</p>
<ul>
<li>the creation of a project is a <code>NEW Base libraries and build scripts</code>,</li>
<li>the first feature is a <code>NEW Authentication of a visitor of the website</code>,</li>
<li>a fix of a feature is a <code>FIX No error message when submitting an empty username in the authentication form</code>,</li>
<li>a change of a feature related to a need is a <code>IMP Moved the username and password fields on one line</code>,</li>
<li>writing the documentation of a function is a <code>IMP Documented the password encryption function</code>,</li>
<li>etc.</li>
</ul>
<p>Finally, using a label per change allows the commiter to have a better view on how to separate the commit in different changesets. Instead of commiting:<br />
<code><br />
FIX PHP error when using a quote in the password field of the authentication form<br />
IMP Getting the user info from the database only once when authentication<br />
</code><br />
The developer can separate the changeset in two different commits (using partial commits or the shelve function of its SCM).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2010/06/20/agile-compatible-standard-format-for-commit-messages/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>File-localized global (configuration) variables in PHP</title>
		<link>http://www.enisseo.net/blog/2010/06/16/file-localized-global-configuration-variables-in-php/</link>
		<comments>http://www.enisseo.net/blog/2010/06/16/file-localized-global-configuration-variables-in-php/#comments</comments>
		<pubDate>Wed, 16 Jun 2010 18:11:15 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[config]]></category>
		<category><![CDATA[global]]></category>
		<category><![CDATA[local]]></category>
		<category><![CDATA[persistent]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[variable]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=123</guid>
		<description><![CDATA[I recently developed some functions that are meant to manage global variables within a PHP script. For example, let us assume you want your script to save some configuration variables from an execution to another. You may want to do something like: &#60;?php ... $config = get_my_script_saved_config_vars(); ... if (!empty($_POST['myVar'])) { $config['myVar'] = $_POST['myVar']; save_my_script_config_vars($config); [...]]]></description>
			<content:encoded><![CDATA[<p>I recently developed some functions that are meant to manage global variables within a PHP script.</p>
<p>For example, let us assume you want your script to save some configuration variables from an execution to another.<br />
You may want to do something like:<br />
<code>&lt;?php<br />
...<br />
$config = get_my_script_saved_config_vars();<br />
...<br />
if (!empty($_POST['myVar']))<br />
{<br />
	$config['myVar'] = $_POST['myVar'];<br />
	save_my_script_config_vars($config);<br />
}<br />
...<br />
if (!empty($config['myVar']))<br />
{<br />
	do_some_actions_using_config_var($config['myVar']);<br />
}<br />
...<br />
?&gt;</code></p>
<p>Often, when dealing with this sort global/persistent configuration variables, you have to use a database or a special file.</p>
<p>Now, simply include this code to your script:<br />
<code>&lt;?php<br />
/**<br />
 * Provides a simple way to manage local persistent variables.<br />
 *<br />
 * @author Enisseo &lt;http://www.enisseo.net><br />
 */<br />
class LocalConfig<br />
{<br />
	/**<br />
	 * Returns the current local configuration.<br />
	 * @return mixed<br />
	 */<br />
	public static function load()<br />
	{<br />
		$file = LocalConfig::_findFile();<br />
		$config = array();<br />
		if (!empty($file))<br />
		{<br />
			$content = file_get_contents($file);<br />
			$result = array();<br />
			if (preg_match('#&lt;\?php\s+/\*CONFIG:(.*?)ENDCONFIG\*/ \?>#i', $content, $result))<br />
			{<br />
				$configStr = trim($result[1]);<br />
				$config = @unserialize($configStr);<br />
			}<br />
		}<br />
		return $config;<br />
	}<br />
	/**<br />
	 * Save the current local configuration.<br />
	 * @param $config mixed<br />
	 */<br />
	public static function save($config)<br />
	{<br />
		$file = LocalConfig::_findFile();<br />
		if (!empty($file))<br />
		{<br />
			$content = file_get_contents($file);<br />
			$configStr = '&lt;?php /*CONFIG: ' . @serialize($config) . ' ENDCONFIG*/ ?>';<br />
			$content = preg_replace('#(&lt;\?php\s+/\*CONFIG:(.*?)ENDCONFIG\*/\s+\?>|^)#i', $configStr, $content);<br />
			file_put_contents($file, $content);<br />
		}<br />
	}<br />
	/**<br />
	 * Returns the caller file.<br />
	 * @return string<br />
	 */<br />
	private static function _findFile()<br />
	{<br />
		$trace = debug_backtrace();<br />
		for ($i = 0; $i < count($trace); $i++)<br />
		{<br />
			if ($trace[$i]['file'] != __FILE__)<br />
			{<br />
				return $trace[$i]['file'];<br />
			}<br />
		}<br />
		return null;<br />
	}<br />
}</code></p>
<p>You can either copy and paste into your script or include a file containing this script. Then, you only have to use the two static functions:<br />
<code>&lt;?php<br />
include_once('LocalConfig.php');<br />
$config = LocalConfig::load();<br />
if (!empty($config['name']))<br />
{<br />
	print_form_asking_for_name();<br />
}<br />
if (!empty($_POST['name']))<br />
{<br />
	$config['name'] = $_POST['name'];<br />
	LocalConfig::save($config);<br />
}<br />
</code></p>
<p>This way you do not have to worry about databases or config files in order to store and manage persistent variables: the variables are stored  within the file, in the begining of the source code.</p>
<p>If you find any bug, please let me know.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2010/06/16/file-localized-global-configuration-variables-in-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>HTML Orienté Objet avec CSS</title>
		<link>http://www.enisseo.net/blog/2009/11/20/html-oriente-objet-avec-css/</link>
		<comments>http://www.enisseo.net/blog/2009/11/20/html-oriente-objet-avec-css/#comments</comments>
		<pubDate>Thu, 19 Nov 2009 23:32:04 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[class]]></category>
		<category><![CDATA[classe]]></category>
		<category><![CDATA[css]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[id]]></category>
		<category><![CDATA[identifiant]]></category>
		<category><![CDATA[microformat]]></category>
		<category><![CDATA[objet]]></category>
		<category><![CDATA[orienté]]></category>
		<category><![CDATA[poo]]></category>
		<category><![CDATA[sémantique]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=114</guid>
		<description><![CDATA[Ou Comment avoir (presque) l&#8217;impression de manipuler de l&#8217;objet en CSS. HTML sémantique Pour cela, il faut partir d&#8217;un code HTML sémantiquement fort. Les règles que j&#8217;utilise sont les suivantes : différencier les types de structures génériques des données spécifiques ; réfléchir à la forme HTML la plus appropriée pour chaque structure générique ; rester [...]]]></description>
			<content:encoded><![CDATA[<p><strong>Ou <cite>Comment avoir (presque) l&#8217;impression de manipuler de l&#8217;objet en CSS</cite>.</strong></p>
<h2>HTML sémantique</h2>
<p>Pour cela, il faut partir d&#8217;un code HTML sémantiquement fort. Les règles que j&#8217;utilise sont les suivantes :</p>
<ol>
<li>différencier les types de structures génériques des données spécifiques ;</li>
<li>réfléchir à la forme HTML la plus appropriée pour chaque structure générique ;</li>
<li>rester simple.</li>
</ol>
<p>La première règle s&#8217;applique lorsqu&#8217;il n&#8217;existe pas de balises HTML existante permettant de décrire exactement le type de la structure. Un paragraphe est un <code>&lt;p&gt;</code>, une liste est un <code>&lt;ul&gt;</code>, mais un menu ? Un fil d&#8217;Ariane ? Une propriété d&#8217;un formulaire ?<br />
Il faut de plus différencier ces structures génériques de leur réalité. Par exemple, une même page peut avoir plusieurs menus (même structure générique) mais l&#8217;un est le menu principal, l&#8217;autre le menu secondaire, etc. De la même façon, il existe plusieurs propriétés dans un formulaire mais chacune désigne une certaine donnée : identifiant, mot de passe, nom, prénom&#8230;</p>
<p>À chaque structure non existante, il est nécessaire de réfléchir à la structure la plus appropriée, à la fois sémantiquement et hiérarchiquement. Communément un menu est représenté par une liste d&#8217;éléments (<code>&lt;ul&gt;</code> et <code>&lt;li&gt;</code>). Un fil d&#8217;Ariane pourrait l&#8217;être par une liste ordonnée (<code>&lt;ol&gt;</code>). Une propriété d&#8217;un formulaire pourrait l&#8217;être par une liste de définitions (<code>&lt;dl&gt;</code>, <code>&lt;dt&gt;</code> pour le label, <code>&lt;dd&gt;</code> pour le champ). Il n&#8217;y a rarement qu&#8217;une seule bonne solution.<br />
Lorsque les propriétés seules ne sont pas suffisantes, une pratique courante est d&#8217;augmenter le sens d&#8217;une balise existante grâce à un attribut HTML, notamment avec l&#8217;attribut <code>class</code> qui permet d&#8217;être différencié en CSS (voir <a href="http://en.wikipedia.org/wiki/Microformat" title="Microformats sur Wikipedia">les microformats</a>). Ainsi, on pourra par exemple ajouter la classe <cite>menu</cite> sur le <code>&lt;ul&gt;</code> pour désigner un menu (<code>&lt;ul class="menu"&gt;</code>), ou encore la classe &#8220;breadcrumb&#8221; sur un <code>&lt;ol&gt;</code> pour désigner un fil d&#8217;Ariane.</p>
<p>Enfin, il est essentiel de rester simple dans la construction du HTML : pas de profusion de <code>&lt;div&gt;</code>, de <code>&lt;p&gt;</code> et de <code>&lt;span&gt;</code> ! Pas besoin d&#8217;encapsuler un <code>&lt;h1&gt;</code> dans un <code>&lt;div class="title"&gt;</code> : le <code>&lt;h1&gt;</code> EST un titre.<br />
Tant que c&#8217;est raisonnablement possible, fusionner les <code>&lt;div&gt;</code>, <code>&lt;p&gt;</code> et <code>&lt;span&gt;</code> avec les balises porteuses de sens et s&#8217;appuyer sur le contexte pour pouvoir distinguer les balises. Ainsi, on peut transformer <code>&lt;div class="ContactPage"&gt;&lt;div class="ContactForm"&gt;&lt;form&gt;...</code> en <code>&lt;div class="ContactPage"&gt;&lt;form&gt;...</code>, voire en <code>&lt;form class="ContactPage&gt;</code>.</p>
<p>Après cette introduction, nous pouvons entrer dans le vif du sujet en commençant par utiliser de manière orientée objet les attributs HTML.</p>
<h2>Classification et identification du HTML</h2>
<p>Deux attributs permettent de s&#8217;approcher de la notion d&#8217;objets en HTML : <cite>class</cite> et <cite>id</cite>. <cite>Class</cite> peut être utilisé pour représenter la ou les classes (au sens <abbr title="Programmation Orientée Objet">POO</abbr> du terme). De la même façon qu&#8217;un objet peut être de la classe <cite>Student</cite> qui hérite de <cite>User</cite>, un <code>&lt;div&gt;</code> a comme attribut <code>class="Student User"</code>. L&#8217;attribut <cite>id</cite> sert quant à lui à identifier une instance d&#8217;un <cite>objet HTML</cite> : la <code>&lt;div id="student3188" class="Student"&gt;</code> correspond ainsi à une instance (appelée <cite>student3188</cite>) d&#8217;une classe <cite>Student</cite>.</p>
<p>Chaque partie du code HTML peut être décrite de cette manière. Par exemple, une liste d&#8217;utilisateurs peut être écrite sous la forme :</p>
<pre>&lt;ul class="Users"&gt;
   &lt;li class="User" id="johndoe"&gt;
      &lt;p class="Name"&gt;John Doe&lt;/p&gt;
      &lt;p class="Age"&gt;&lt;span class="Value"&gt;25&lt;/span&gt; years old&lt;/p&gt;
   &lt;/li&gt;
   &lt;li class="User" id="janedoe"&gt;
      &lt;p class="Name"&gt;Jane Doe&lt;/p&gt;
      &lt;p class="Age"&gt;&lt;span class="Value"&gt;27&lt;/span&gt; years old&lt;/p&gt;
   &lt;/li&gt;
&lt;/ul&gt;</pre>
<p>Une fois que le code HTML du site est organisé de cette manière, il est possible d&#8217;utiliser le CSS pour accéder aux différentes parties.</p>
<h2>Écriture des sélecteurs CSS</h2>
<p>L&#8217;écriture du code CSS s&#8217;appuie fortement sur la caractérisation des balises HTML expliquée précédemment. Grâce aux attributs <cite>class</cite> (sans balise HTML) il va être possible de mettre en forme de manière générique les données. L&#8217;attribut <cite>id</cite> servira quant à lui au besoin pour différencier les cas.<br />
On peut donc écrire par exemple :</p>
<pre>.Users {
   margin: 15px;
}
   .Users .User {
      border: 1px solid grey;
      padding: 10px;
   }
      .Users .User .Name {
         font-weight: bold;
      }
      .Users .User .Age {
         font-size: 0.8em;
      }
         .Users .User .Value {
            font-size: 1.5em;
         }
   .Users .User#janedoe {
      border-color: blue;
   }
</pre>
<p>Cette notation en classes donne l&#8217;impression d&#8217;écrire des accès à des variables en programmation orientée objet. En effet, les langages utilisent souvent le caractère <cite>.</cite> (point) pour accéder à la propriété d&#8217;un objet. Ainsi, on accède à l&#8217;attribut <cite>name</cite> de l&#8217;objet <cite>user</cite> par <code>user.name</code>. L&#8217;utilisation d&#8217;une organisation du code HTML et de sélecteurs CSS basés sur les classes permet d&#8217;approcher syntaxiquement cela : <code>.User .Name</code>.<br />
Le fait de s&#8217;appuyer quasi uniquement sur l&#8217;arborescence de classes permet notamment :</p>
<ul>
<li>d&#8217;éviter les conflits d&#8217;identifiant ;</li>
<li>de pouvoir accéder de manière lisible et compréhensible (sémantique) aux éléments ;</li>
<li>de réduire les soucis de priorité des sélecteurs CSS ;</li>
<li>de rester suffisamment souple et précis pour pouvoir tolérer des modifications du code HTML sans retouche du code CSS (encapsulation dans un autre bloc, modification du type de balise HTML&#8230;).</li>
</ul>
<p>De cette manière les codes HTML et CSS approchent les qualités de modularité et de structuration de la programmation orientée objet.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/11/20/html-oriente-objet-avec-css/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Utiliser switch() pour gérer des processus</title>
		<link>http://www.enisseo.net/blog/2009/11/18/utiliser-switch-pour-gerer-des-processus/</link>
		<comments>http://www.enisseo.net/blog/2009/11/18/utiliser-switch-pour-gerer-des-processus/#comments</comments>
		<pubDate>Wed, 18 Nov 2009 20:44:08 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[condition]]></category>
		<category><![CDATA[false]]></category>
		<category><![CDATA[if]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[process]]></category>
		<category><![CDATA[processus]]></category>
		<category><![CDATA[structure]]></category>
		<category><![CDATA[switch]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=109</guid>
		<description><![CDATA[Voici une façon d&#8217;utiliser l&#8217;instruction switch présente sur de nombreux langages, notamment en PHP : switch (false) { case doStep1(): print("Error at step 1"); break; case doStep2(): print("Error at step 2"); break; case doStep3(): print("Error at step 3"); break; default: print("Success!"); break; } Cette méthode permet notamment de gérer les retours en cas d&#8217;erreur lors [...]]]></description>
			<content:encoded><![CDATA[<p>Voici une façon d&#8217;utiliser l&#8217;instruction <code>switch</code> présente sur de nombreux langages, notamment en PHP :</p>
<pre>switch (false) {
   case doStep1(): print("Error at step 1"); break;
   case doStep2(): print("Error at step 2"); break;
   case doStep3(): print("Error at step 3"); break;
   default: print("Success!"); break;
}</pre>
<p>Cette méthode permet notamment de gérer les retours en cas d&#8217;erreur lors du déroulement du processus. Les étapes vont être exécutées une à une et si l&#8217;une d&#8217;elles échoue (<code>return false</code> par exemple), le traitement associé à cette étape est exécuté puis le processus est terminé.<br />
Si <code>doStep1() </code>retourne <code>true</code>, la comparaison avec le <code>false</code> du switch échoue et le programme tente avec le prochain <code>case</code> défini. Par contre, si <code>doStep1() </code>retourne <code>false</code>, la comparaison correspond (<code>false == false</code>) et le traitement associé au <code>case</code> est donc exécuté &#8211; en l&#8217;occurrence le traitement de l&#8217;erreur.</p>
<p>En somme, le fonctionnement n&#8217;est pas différent de :</p>
<pre>if (doStep1()) {
   if (doStep2()) {
      if (!doStep3()) {
         print("Success!");
      } else {
         print("Error at step 3");
      }
   } else {
      print("Error at step 2");
   }
} else {
   print("Error at step 1");
}</pre>
<p>ou encore de :</p>
<pre>if (!doStep1()) {
   print("Error at step 1");
} else if (!doStep2()) {
   print("Error at step 2");
} else if (!doStep3()) {
   print("Error at step 3");
} else {
   print("Success!");
}</pre>
<p>L&#8217;avantage de la notation en <code>switch</code> réside principalement dans sa lisibilité. En effet, chacune des notations précédente a au moins un défaut. Pour la première, il sont évidents : la cascade de conditions (indentation) et la séparation symétrique entre la condition et le traitement d&#8217;erreur (le traitement de l&#8217;étape 1 est à la fin, le message de succès est au milieu) entraînent une très mauvaise lisibilité du processus. Pour la deuxième, le processus reste lisible mais les conditions peuvent l&#8217;être moins puisqu&#8217;elles testent la <strong>non</strong>-validité de l&#8217;étape. Ainsi la compréhension des étapes est polluée par la négation de la condition : par le signe <code>!</code> en préfixe dans l&#8217;exemple, mais éventuellement par des conditions plus tordues (!a || !b || !c&#8230;).<br />
La structure en <code>switch</code>, pour peu qu&#8217;on la connaisse, offre une lisibilité adaptée au déroulement d&#8217;un processus : chaque étape se situe sur la même colonne, chaque traitement d&#8217;erreur ou de succès est proche de sa cause et les étapes sont lisibles. En faisant abstraction du langage en lui-même, la structure correspond à :</p>
<pre>PROCESS:
   STEP1: ERROR1
   STEP2: ERROR2
   STEP3: ERROR3
   END: SUCCESS</pre>
<p>Cette structure favorise également la décomposition de chaque étape en une fonction claire. Ainsi, au lieu d&#8217;avoir une accumulation de traitements mélangés comme c&#8217;est le cas lors de l&#8217;utilisation d&#8217;un <code>if</code>, la structure incite (en tout cas encourage) à nommer clairement chaque étape et à réaliser la fonction associée :</p>
<pre>switch (false) {
   case enterLogin(): ... break;
   case enterPassword(): ... break;
   case validateForm(): ... break;
   case checkAuthentication(): ... break;
   ...
}</pre>
<p>Lorsque quelqu&#8217;un relit le code, il peut très simplement comprendre le processus en lisant le nom de chaque fonction.</p>
<p>Enfin, le <code>switch</code> offre un léger avantage sur le <code>if</code> : l&#8217;instruction <code>break</code>. Cette dernière permet d&#8217;interrompre le processus à tout moment et peut éviter des constructions complexes. On peut par exemple écrire :</p>
<pre>switch (false) {
   case doStep1():
      if (verbose == 0) break;
      print('Error');
      if (verbose == 1) break;
      print(error_msg());
      break;
   ...
   default:
      if (...) {
         break;
      }
      ...
}</pre>
<p>Ce fonctionnement est moins naturel à réaliser avec un <code>if</code>.</p>
<p>Pour terminer, voici un exemple en syntaxe PHP d&#8217;une structure <code>switch (false)</code> qui mélange vérification et processus :</p>
<pre>switch (false) {
   <i>// checking if the process can be executed</i>
   case doesTheUserWantToExecuteThisProcess():
      break;

   <i>// checking some general conditions</i>
   case isTheUserAuthenticated():
      redirectToLoginPage(); break;

   <i>// checking variables</i>
   case isPosted('title'):
      printError('The title is mandatory.'); break;
   case isPosted('text'):
      printError('The text is mandatory.'); break;
   case isPosted('category'):
      printError('The category is mandatory.'); break;

   <i>// executing the process</i>
   case connectToDatabase():
      printError('Database connection failed.'); break;
   case $article = Article::create(posted('title'), posted('text')):
      printError('The creation of the article failed!'); break;
   case moveArticleToCategory($article, posted('category')):
      printError('The category does not exist.'); break;
   default:
      printSuccess('Your article has been added to the category!');
}</pre>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/11/18/utiliser-switch-pour-gerer-des-processus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MediaDoc : gestion de documents en mode wiki (PHP)</title>
		<link>http://www.enisseo.net/blog/2009/10/24/mediadoc-gestion-de-documents-en-mode-wiki-php/</link>
		<comments>http://www.enisseo.net/blog/2009/10/24/mediadoc-gestion-de-documents-en-mode-wiki-php/#comments</comments>
		<pubDate>Sat, 24 Oct 2009 14:56:27 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[document]]></category>
		<category><![CDATA[documentation]]></category>
		<category><![CDATA[media]]></category>
		<category><![CDATA[mediadoc]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[wiki]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=99</guid>
		<description><![CDATA[Le projet MediaDoc est né d&#8217;un besoin : pouvoir rapidement échanger des médias entre plusieurs personnes tout en associant à ces médias des informations de type documentation. L&#8217;idée est par exemple de pouvoir publier des codes sources, des extensions, etc. tout en permettant de construire simplement la documentation de ces fichiers. Pour télécharger le projet [...]]]></description>
			<content:encoded><![CDATA[<p>Le projet MediaDoc est né d&#8217;un besoin : pouvoir rapidement échanger des médias entre plusieurs personnes tout en associant à ces médias des informations de type documentation. L&#8217;idée est par exemple de pouvoir publier des codes sources, des extensions, etc. tout en permettant de construire simplement la documentation de ces fichiers.</p>
<p>Pour télécharger le projet dans l&#8217;état actuel : <a href="http://www.enisseo.net/blog/wp-content/uploads/2009/07/mediadoc.zip">MediaDoc &#8211; Preview</a>. Il s&#8217;agit d&#8217;une pré-version, non destinée à être utilisée dans un contexte de production. Elle n&#8217;est certainement pas exempte de bugs / failles de sécurité et a été soumise à des contraintes fortes (développement en une dizaine d&#8217;heures pour un seul fichier obligatoire), ce qui rend son code actuellement très sale et difficilement maintenable en l&#8217;état.</p>
<p>Pour l&#8217;utiliser, décompressez le fichier dans un dossier et c&#8217;est tout (normalement). Pas de connexion à une base de données, pas de configuration spéciale.</p>
<p>Le projet utilise par défaut :</p>
<ul>
<li>de l&#8217;URL rewriting : supprimer/renommer le fichier .htaccess à la racine pour ne pas l&#8217;utiliser ;</li>
<li>de l&#8217;archivage des modifications : supprimer/renommer le dossier archives/ à la racine pour ne pas l&#8217;utiliser.</li>
</ul>
<p>Une documentation succincte est fournie de base, mais en explorant un peu le contenu tout devrait être clair, je suppose.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/10/24/mediadoc-gestion-de-documents-en-mode-wiki-php/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Plugin jQuery : tronquage de texte</title>
		<link>http://www.enisseo.net/blog/2009/09/15/plugin-jquery-tronquage-de-texte/</link>
		<comments>http://www.enisseo.net/blog/2009/09/15/plugin-jquery-tronquage-de-texte/#comments</comments>
		<pubDate>Tue, 15 Sep 2009 17:57:34 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[onresize]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[resize]]></category>
		<category><![CDATA[text]]></category>
		<category><![CDATA[texte]]></category>
		<category><![CDATA[tronquage]]></category>
		<category><![CDATA[truncate]]></category>
		<category><![CDATA[wrap]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=79</guid>
		<description><![CDATA[Le but de ce plugin jQuery est de tronquer le texte quand il fait plus d&#8217;une ligne. Typiquement, il s&#8217;agit de transformer : Ceci est un texte beaucoup trop long sur deux lignes. En : Ceci est un texte beaucoup... Le plugin est téléchargeable à cette adresse. Une démonstration est visible sur cette page. L&#8217;intérêt [...]]]></description>
			<content:encoded><![CDATA[<p>Le but de ce plugin jQuery est de tronquer le texte quand il fait plus d&#8217;une ligne. Typiquement, il s&#8217;agit de transformer :</p>
<pre>Ceci est un texte beaucoup
trop long sur deux lignes.</pre>
<p>En :</p>
<pre>Ceci est un texte beaucoup...</pre>
<p>Le plugin est téléchargeable à <a href="http://www.enisseo.net/blog/wp-content/uploads/2009/09/jquery.truncate.js">cette adresse</a>.</p>
<p>Une démonstration est visible <a href="http://www.enisseo.net/blog/wp-content/uploads/2009/09/demoTruncatePlugin.html">sur cette page</a>.</p>
<p>L&#8217;intérêt du plugin est d&#8217;utiliser le <a href="http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/">plugin resize</a>. Ainsi, lors du redimensionnement du texte (soit de la fenêtre, soit du parent ou autre), ce dernier est tronqué correctement.</p>
<p>Il est également possible de réafficher le texte complet au survol de la souris.</p>
<p>Pour l&#8217;utiliser, il suffit d&#8217;écrire :</p>
<pre>$('selector').truncate(options);</pre>
<p>Les options étant :</p>
<ul>
<li><em>wrapZone </em>: le pourcentage (entre 0 et 1) de largeur tronquée au maximum. Le plugin cherchant à tronquer entre les mots, si un mot est plus grand qu&#8217;un certain pourcentage de la largeur, cela risque de déformer le rendu. Ainsi, si on veut autoriser à tronquer au milieu d&#8217;un mot si ce dernier est plus grand que 20% de la largeur, on spécifiera wrapZone = 0.8 ;</li>
<li><em>endMarker </em>: le marqueur de fin, &#8220;&#8230;&#8221; par défaut ;</li>
<li><em>onResize </em>: active la gestion du tronquage lors du redimensionnement du noeud ;</li>
<li><em>displayOnOver </em>: active l&#8217;affichage au survol du texte complet.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/09/15/plugin-jquery-tronquage-de-texte/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Plugin jQuery : &#8220;resize&#8221; event</title>
		<link>http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/</link>
		<comments>http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/#comments</comments>
		<pubDate>Wed, 09 Sep 2009 20:28:21 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[event]]></category>
		<category><![CDATA[javascript]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[onresize]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[resize]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=58</guid>
		<description><![CDATA[Le plugin jQuery resize permet d&#8217;ajouter un évènement &#8220;resize&#8221; (de la même manière que &#8220;click&#8221; ou &#8220;mouseover&#8221;) aux objets jQuery (donc aux nœuds HTML). Vous pouvez le télécharger ici. Il s&#8217;utilise de la même manière que les autres évènements : $('monSelecteurCSS').resize( function(width, height, previousWidth, previousHeight) { // width: la largeur actuelle de l'élément // height: [...]]]></description>
			<content:encoded><![CDATA[<p>Le <a rel="attachment wp-att-59" href="http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/jquery-resize/">plugin jQuery resize</a> permet d&#8217;ajouter un évènement &#8220;resize&#8221; (de la même manière que &#8220;click&#8221; ou &#8220;mouseover&#8221;) aux objets jQuery (donc aux nœuds HTML). Vous pouvez le télécharger <a rel="attachment wp-att-59" href="http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/jquery-resize/">ici</a>.</p>
<p>Il s&#8217;utilise de la même manière que les autres évènements :</p>
<pre>$('monSelecteurCSS').resize(
	function(width, height, previousWidth, previousHeight) {
		// width: la largeur actuelle de l'élément
		// height: la hauteur actuelle de l'élément
		// previousWidth: la largeur de l'élément avant redimensionnement
		// previousHeight: la hauteur de l'élément avant redimensionnement
	});</pre>
<p>Bientôt une application amusante sous la forme d&#8217;un autre plugin jQuery.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/09/09/plugin-jquery-resize-event/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Pourquoi SVN est anti-Agile</title>
		<link>http://www.enisseo.net/blog/2009/09/05/pourquoi-svn-est-anti-agilehow-svn-and-any-others-client-server-vcs-is-anti-agile/</link>
		<comments>http://www.enisseo.net/blog/2009/09/05/pourquoi-svn-est-anti-agilehow-svn-and-any-others-client-server-vcs-is-anti-agile/#comments</comments>
		<pubDate>Sat, 05 Sep 2009 15:07:17 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[Technologies]]></category>
		<category><![CDATA[agile]]></category>
		<category><![CDATA[branche]]></category>
		<category><![CDATA[déploiement]]></category>
		<category><![CDATA[développement]]></category>
		<category><![CDATA[distribué]]></category>
		<category><![CDATA[gestionnaire]]></category>
		<category><![CDATA[intégration]]></category>
		<category><![CDATA[serveur]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[tronc]]></category>
		<category><![CDATA[version]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=45</guid>
		<description><![CDATA[Une politique courante en pratique Agile est d&#8217;avoir un tronc toujours compilable. Cela permet notamment à n&#8217;importe quel développeur qui veut reprendre le code de ne pas tomber sur des erreurs de compilation qu&#8217;il ne maîtrise pas et qui risquent de l&#8217;empêcher de tester ses propres modifications. Par extension, cela permet d&#8217;envisager l&#8217;utilisation de serveurs [...]]]></description>
			<content:encoded><![CDATA[<p>Une politique courante en pratique Agile est d&#8217;avoir un <strong>tronc toujours compilable</strong>. Cela permet notamment à n&#8217;importe quel développeur qui veut reprendre le code de ne pas tomber sur des erreurs de compilation qu&#8217;il ne maîtrise pas et qui risquent de l&#8217;empêcher de tester ses propres modifications. Par extension, cela permet d&#8217;envisager l&#8217;utilisation de serveurs d&#8217;intégration qui permettent de tester en conditions réelles la compilation, le test et le déploiement du produit.</p>
<p>Un problème courant est de pouvoir vérifier <strong>avant enregistrement</strong> sur le tronc si la version est correcte. En effet, si le développeur omet par exemple d&#8217;ajouter au tronc des fichiers nécessaires, la compilation peut fonctionner sur son poste mais pas sur un autre environnement. Trouver d&#8217;où vient la source du problème peut même s&#8217;avérer long et fastidieux.</p>
<p>Une solution permettant de s&#8217;assurer que le tronc est toujours compilable est de faire intervenir dans le processus d&#8217;enregistrement des sources un serveur intermédiaire : le développeur enregistre localement ses modifications, les reporte sur le serveur à partir du gestionnaire de versions. Si la compilation est valide sur le serveur, il peut enregistrer les modifications sur le tronc.</p>
<p>Avec des gestionnaires de versions distibués (Bazaar, Git, Mercurial&#8230;), ce processus est facilement envisageable. Le développeur travaille sur son poste avec une branche locale du tronc. Une fois son travail effectué, il enregistre (<em>commit</em> local) les modifications puis les reporte sur le serveur (<em>push</em>). Quand la modification est validée, il peut reporter la modification sur le tronc (<em>push</em>, également).</p>
<p>Le cas de SVN ou des autres types de gestionnaires de versions est plus complexe à envisager. Il faut en effet créer tout d&#8217;abord une branche à partir du tronc, branche qui recevra les modifications à valider. Le développeur travaille sur cette branche (<em>checkout</em>, <em>update</em>) et y enregistre ses modifications (<em>commit</em>). Une fois validée, il les récupère à partir du serveur (<em>checkout</em>, <em>update</em>). Quand la modification est validée, il ne reste à faire qu&#8217;une fusion entre la branche et le tronc.</p>
<p>En somme, ce processus est adaptable mais nécessite plus d&#8217;efforts : les modèles de gestionnaires de versions distribués abstraient les branches et simplifient les passages de modifications entre branches par rapport aux modèles client-serveur.  Ces derniers rendent compliquée la tâche de reporter une modification d&#8217;une machine à une autre (d&#8217;un poste de développeur à un serveur d&#8217;intégration, par exemple). De manière générale, un gestionnaire de versions distribué s&#8217;adaptera mieux aux processus et politiques de développement.</p>
<p>À lire, au sujet des gestionnaires de versions et des processus :</p>
<ul>
<li><a href="http://martinfowler.com/bliki/FeatureBranch.html">http://www.perforce.com/perforce/papers/bestpractices.html</a></li>
<li><a href="http://martinfowler.com/bliki/FeatureBranch.html">http://martinfowler.com/bliki/FeatureBranch.html</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/09/05/pourquoi-svn-est-anti-agilehow-svn-and-any-others-client-server-vcs-is-anti-agile/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Plugin jQuery pour l&#8217;affichage en clair des mots de passe au survol</title>
		<link>http://www.enisseo.net/blog/2009/08/01/jquery-affichage-mots-de-passe-au-survol/</link>
		<comments>http://www.enisseo.net/blog/2009/08/01/jquery-affichage-mots-de-passe-au-survol/#comments</comments>
		<pubDate>Sat, 01 Aug 2009 15:19:08 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Programmation]]></category>
		<category><![CDATA[affichage]]></category>
		<category><![CDATA[jquery]]></category>
		<category><![CDATA[mot]]></category>
		<category><![CDATA[passe]]></category>
		<category><![CDATA[password]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[solution]]></category>
		<category><![CDATA[survol]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=22</guid>
		<description><![CDATA[Le problème a été relevé récemment dans plusieurs articles sur la sécurité : doit-on vraiment masquer les mots de passe dans les formulaires ? Solution La solution que je propose est relativement simple : lorsque le curseur de la souris survole le champ de mot de passe, celui-ci est affiché en clair. Sinon, il est [...]]]></description>
			<content:encoded><![CDATA[<p>Le problème a été relevé récemment dans plusieurs articles sur la sécurité : <a title="The Pros and Cons of Password Masking" href="http://www.schneier.com/blog/archives/2009/07/the_pros_and_co.html">doit-on vraiment masquer les mots de passe dans les formulaires ?</a></p>
<h3>Solution</h3>
<div id="attachment_38" class="wp-caption alignright" style="width: 130px"><img class="size-full wp-image-38" title="Mot de passe en clair au survol" src="http://www.enisseo.net/blog/wp-content/uploads/2009/08/password.gif" alt="Mot de passe en clair au survol" width="120" height="60" /><p class="wp-caption-text">Mot de passe en clair au survol</p></div>
<p>La solution que je propose est relativement simple : lorsque le curseur de la souris survole le champ de mot de passe, celui-ci est affiché en clair. Sinon, il est masqué. L&#8217;explication est donnée en fin d&#8217;article.</p>
<p>Voici un premier jet de plugin jQuery qui permet de mettre en œuvre ce comportement :</p>
<pre>(function($){
  $.fn.reveal = function() {
    this.filter('input[type="password"]').mouseover(function() {
      $(this).replaceWith(
        $('&lt;input name="'+$(this).attr('name')+'" type="text" /&gt;')
          .attr('class', $(this).attr('class'))
          .val($(this).val())
          .mouseout(function() {
            $(this).replaceWith(
              $('&lt;input name="'+$(this).attr('name')+'" type="password" /&gt;')
                .attr('class', $(this).attr('class'))
                .val($(this).val())
                .reveal()
            );
          })
      );
    });
    return this;
  };
})(jQuery);</pre>
<p>Le plugin est relativement sommaire et peu complet (il correspond à une utilisation que j&#8217;en fait pour un projet personnel). Cependant, le principe est simple à comprendre et il est donc adaptable à d&#8217;autres situations.</p>
<p>Pour l&#8217;utiliser, il suffit d&#8217;appeler la fonction <code>reveal()</code> sur un objet jQuery (exemple : <code>$('input.monPassword').reveal();</code>).</p>
<h3>Explication</h3>
<p>Les arguments relevés dans l&#8217;article sont tout à fait judicieux : ne pas afficher le mot de passe est sujet à erreurs plus fréquentes et risque d&#8217;entraîner les utilisateurs à choisir des mots de passe plus faciles à taper. Des solutions plus ou moins bonnes ont été proposées, notamment par arc90 (<a title="HalfMask" href="http://lab.arc90.com/2009/07/halfmask.php">HalfMask</a> et <a title="HashMask" href="http://lab.arc90.com/2009/07/hashmask.php">HashMask</a>). J&#8217;ai pu également lire une solution basée sur une case à cocher qui permettrait d&#8217;afficher ou masquer le mot de passe.</p>
<p>En ce qui me concerne, à l&#8217;exposé du problème j&#8217;ai pu identifier deux types d&#8217;utilisateurs : les utilisateurs novices ou standards, pour qui le masquage du mot de passe peut être un inconvénient et les utilisateurs avancés qui sont habitués à taper des mots de passe complexes correctement, et qui préfère généralement le masquage par défaut. La solution de la case à cocher est selon moi trop contraignante : le comportement par défaut risque d&#8217;agacer un des types d&#8217;utilisateurs.</p>
<p>En réfléchissant à la façon dont les deux types d&#8217;utilisateurs entraient un mot de passe (et plus généralement des données de formulaire), j&#8217;ai pu détacher un comportement relativement commun :  un utilisateur standard va très souvent cliquer sur le champ de formulaire pour l&#8217;indiquer tandis que l&#8217;utilisateur avancé va utiliser les contrôles clavier. Ainsi, la solution peut adopter un état par défaut (masquage ou affichage en clair) qui est deviné par rapport à ce comportement.</p>
<p>En l&#8217;occurrence, non seulement les utilisateurs standards cliquent sur le champ du formulaire, mais en général ils laissent le curseur dessus. C&#8217;est ce comportement précis qui dicte la solution expliquée plus haut.</p>
<p>Sans forcément être la solution idéale, je pense qu&#8217;elle a quelques avantages :</p>
<ul>
<li>un état par défaut plus adapté au type d&#8217;utilisateur ;</li>
<li>un comportement relativement simple à comprendre et utiliser (plus qu&#8217;une case à cocher, a priori) ;</li>
<li>une réponse très générale, applicable à beaucoup d&#8217;autres cas d&#8217;utilisation ;</li>
<li>une sécurité une fois que l&#8217;utilisateur a compris le principe.</li>
</ul>
<p>Le principal point faible est actuellement que ce comportement n&#8217;est pas naturel pour des personnes qui sont habituées à ne jamais voir leur mot de passe, et l&#8217;utilisateur standard est donc souvent surpris de lire son mot de passe en clair lorsqu&#8217;il lève les yeux du clavier. Une habitude est nécessaire pour que celui-ci pense à retirer la souris du champ du formulaire si une autre personne est présente dans la pièce.</p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 86px; width: 1px; height: 1px;">(function($){<br />
$.fn.reveal = function() {<br />
this.filter(&#8216;input[type="password"]&#8216;).mouseover(function() {<br />
$(this).replaceWith(<br />
$(&#8216;&lt;input type=&#8221;text&#8221; name=&#8221;&#8216;+$(this).attr(&#8216;name&#8217;)+&#8217;&#8221; /&gt;&#8217;)<br />
.attr(&#8216;class&#8217;, $(this).attr(&#8216;class&#8217;))<br />
.val($(this).val())<br />
.mouseout(function() {<br />
$(this).replaceWith(<br />
$(&#8216;&lt;input type=&#8221;password&#8221; name=&#8221;&#8216;+$(this).attr(&#8216;name&#8217;)+&#8217;&#8221; /&gt;&#8217;)<br />
.attr(&#8216;class&#8217;, $(this).attr(&#8216;class&#8217;))<br />
.val($(this).val())<br />
.reveal()<br />
);<br />
})<br />
);<br />
});<br />
return this;<br />
};</div>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/08/01/jquery-affichage-mots-de-passe-au-survol/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Gérer les redirections et erreurs des formulaires HTML</title>
		<link>http://www.enisseo.net/blog/2009/07/22/gerer-les-redirections-et-erreurs-des-formulaires-html/</link>
		<comments>http://www.enisseo.net/blog/2009/07/22/gerer-les-redirections-et-erreurs-des-formulaires-html/#comments</comments>
		<pubDate>Wed, 22 Jul 2009 16:50:06 +0000</pubDate>
		<dc:creator>Enisseo</dc:creator>
				<category><![CDATA[Conception]]></category>
		<category><![CDATA[erreur]]></category>
		<category><![CDATA[formulaire]]></category>
		<category><![CDATA[html]]></category>
		<category><![CDATA[message]]></category>
		<category><![CDATA[redirection]]></category>
		<category><![CDATA[succès]]></category>
		<category><![CDATA[traitement]]></category>
		<category><![CDATA[url]]></category>
		<category><![CDATA[validation]]></category>

		<guid isPermaLink="false">http://www.enisseo.net/blog/?p=16</guid>
		<description><![CDATA[La création de formulaires HTML amène invariablement à une question d&#8217;architecture : comment gérer les  redirections et les messages d&#8217;erreur ou de succès qui peuvent avoir lieu lors de la validation de la saisie de l&#8217;utilisateur ? La réponse que j&#8217;ai adoptée se base sur quelques règles de conception web simples, applicables à l&#8217;ensemble d&#8217;un [...]]]></description>
			<content:encoded><![CDATA[<p>La création de formulaires HTML amène invariablement à une question d&#8217;architecture : comment gérer les  redirections et les messages d&#8217;erreur ou de succès qui peuvent avoir lieu lors de la validation de la saisie de l&#8217;utilisateur ?</p>
<p>La réponse que j&#8217;ai adoptée se base sur quelques règles de conception web simples, applicables à l&#8217;ensemble d&#8217;un site et qui permettent d&#8217;offrir à l&#8217;utilisateur un certain confort de navigation :</p>
<ul>
<li>l&#8217;utilisateur doit être en mesure de se servir pleinement les fonctions de base de son navigateur (précédent, suivant, rafraîchir, mise dans les favoris&#8230;) ;</li>
<li>pour une URL donnée, le contenu doit rester identique ou très similaire ;</li>
<li>à l&#8217;inverse, un contenu identique ne doit pas se trouver sur plusieurs URL différentes.</li>
</ul>
<p>Soit un formulaire en page /monformulaire.php et contenant plusieurs champs qui peuvent entraîner des erreurs de saisie par l&#8217;utilisateur (exemple : email incorrect, nombre trop grand, date invalide, etc.). En cas d&#8217;erreur de la saisie on souhaite afficher un message d&#8217;avertissement à l&#8217;utilisateur et réafficher le formulaire (avec éventuellement le champ erroné clairement indiqué, par exemple sur fond rouge pour faire dans l&#8217;originalité).</p>
<p>Suivant les principes énoncés plus haut, le formulaire devra toujours avoir comme URL /monformulaire.php : après soumission du formulaire l&#8217;utilisateur doit donc se trouver au final sur la page /monformulaire.php. L&#8217;idée est ici de donner comme URL de soumission du formulaire la même URL que l&#8217;affichage du formulaire : <code>&lt;form action="/monformulaire.php" ...</code></p>
<p>Les avantages de cette technique sont les suivants :</p>
<ul>
<li>les erreurs éventuelles lors de la soumission n&#8217;ont pas besoin d&#8217;être transmises à d&#8217;autres pages comme c&#8217;est le cas en cas de redirections ;</li>
<li>l&#8217;utilisateur peut pleinement utiliser les fonctions précédent/suivant de son navigateur pour par exemple retrouver les valeurs des champs de formulaire ;</li>
<li>en cas de réactualisation de la page, le navigateur propose de reposter les champs du formulaire et l&#8217;utilisateur se retrouve donc sur la même page (et on reste donc dans le respect de la première règle énoncée, ouf !) ;</li>
<li>en cas de chargement direct de la page (par exemple si la page a été enregistrée dans les favoris) c&#8217;est bien le formulaire qui est chargé avec des valeurs vides, comme pouvait s&#8217;y attendre l&#8217;utilisateur ;</li>
<li>d&#8217;un point de vue plus technique, tout le code de gestion et d&#8217;affichage du formulaire se situe sur une même page.</li>
</ul>
<p>Par conséquent, c&#8217;est seulement en cas de succès que l&#8217;utilisateur est redirigé, ce qui apparaît finalement plus logique du point de vue de la navigation : page précédente renvoie sur le formulaire valide, rafraîchir la page ne propose pas de reposter le formulaire&#8230;</p>
<div id="attachment_25" class="wp-caption alignright" style="width: 269px"><img class="size-full wp-image-25" title="game" src="http://www.enisseo.net/blog/wp-content/uploads/2009/07/game.gif" alt="Cercle ou carré ?" width="259" height="203" /><p class="wp-caption-text">Cercle ou carré ?</p></div>
<p><em>La conception me fait penser à ces jeux pour enfants qui consistent à faire entrer des formes en bois dans les trous qui correspondent. Trouver une bonne solution architecturale est un peu comme trouver la bonne forme : d&#8217;abord on essaye le triangle qui dépasse fortement, puis l&#8217;étoile, beaucoup trop compliquée, et on arrive enfin à mettre la main sur le cercle et tout rentre parfaitement. En conception, quand on tombe sur la bonne solution tout devient clair et logique, tout s&#8217;assemble parfaitement. La mauvaise solution, le carré, c&#8217;est celle qui rentre seulement en forçant, en limant les coins et en laissant plein d&#8217;espaces vides, et qui reste bloquée dans le trou.</em></p>
<p><em>Alors, cette solution, cercle ou carré ?</em></p>
<div id="_mcePaste" style="overflow: hidden; position: absolute; left: -10000px; top: 365px; width: 1px; height: 1px;">au fin al</div>
]]></content:encoded>
			<wfw:commentRss>http://www.enisseo.net/blog/2009/07/22/gerer-les-redirections-et-erreurs-des-formulaires-html/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

