summaryrefslogtreecommitdiff
path: root/demos/blog-tutorial/protected/pages
diff options
context:
space:
mode:
authortof <>2007-08-31 09:58:35 +0000
committertof <>2007-08-31 09:58:35 +0000
commit2fd00de2170fcb16b5214c7839c18f0dacab8cdb (patch)
tree797a7f6daa2b56aa7d141fab67f926f4dde323d0 /demos/blog-tutorial/protected/pages
parentfdc057236aa26dbf98ef3e5cb6d5b22cb6e05efe (diff)
Full translation of Blog Tutorial in French. Thanks to Eric.M !
Diffstat (limited to 'demos/blog-tutorial/protected/pages')
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/CreateContact.page203
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/Setup.page161
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/ShareLayout.page182
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/directories.gifbin0 -> 3611 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/directories2.gifbin0 -> 4147 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/directories3.gifbin0 -> 3531 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day1/fr/output.gifbin0 -> 13379 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/ConnectDB.page47
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/CreateAR.page177
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/CreateDB.page70
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/ER.gifbin0 -> 4444 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/ER.vsdbin0 -> 73216 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/directories.gifbin0 -> 4580 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day2/fr/directories2.gifbin0 -> 6795 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/Auth.page106
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/CreateAdminUser.page159
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/CreateEditUser.page199
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/CreateLoginUser.page161
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/CreateNewUser.page212
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/Overview.page42
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/directories.gifbin0 -> 10329 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/output.gifbin0 -> 10006 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/output2.gifbin0 -> 9222 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day3/fr/output3.gifbin0 -> 9464 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/CreateEditPost.page136
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/CreateListPost.page182
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/CreateNewPost.page139
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/CreateReadPost.page144
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/Overview.page28
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/directories.gifbin0 -> 11129 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/output.gifbin0 -> 3406 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/output2.gifbin0 -> 6326 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/output3.gifbin0 -> 11874 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day4/fr/output4.gifbin0 -> 11916 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/ErrorLogging.page158
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/Performance.page66
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/Summary.page41
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/UseTheme.page138
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/output.gifbin0 -> 4282 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/output2.gifbin0 -> 7798 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/Day5/fr/output3.gifbin0 -> 5190 bytes
-rwxr-xr-xdemos/blog-tutorial/protected/pages/fr/Overview.page8
-rwxr-xr-xdemos/blog-tutorial/protected/pages/fr/Requirements.page37
43 files changed, 2792 insertions, 4 deletions
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/CreateContact.page b/demos/blog-tutorial/protected/pages/Day1/fr/CreateContact.page
new file mode 100755
index 00000000..e85d8bfe
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/CreateContact.page
@@ -0,0 +1,203 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page Contact</h1>
+
+<p>
+Nous avons créé une page par défaut <tt>Home.page</tt> en utilisant les <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.CommandLine">outils en ligne de commande de PRADO</a>. Cette page est relativement statique parce qu'elle ne contient que du contenu HTML. Dans cette session, nous allons créer une page dynamique dénommée <tt>Contact</tt>.
+</p>
+
+<p>
+Le but de cette page est de collecter les retours d'informations des utilisateurs Web concernant notre outil de blog. Pour atteindre ce but, nous envisageons d'utiliser un formulaire qui sera à remplir. Dans ce formulaire, nous demanderons le nom de l'utilisateur, son adresse email et son commentaire. Après que le formulaire ai été rempli et envoyé, un email avec le commentaire sera envoyé à l'administrateur.
+</p>
+
+<p>
+Pour créer la page <tt>Contact</tt>, nous avons besoin de 2 fichiers dans le dossier <tt>pages</tt> : le fichier de gabarit <tt>Contact.page</tt> et le fichier de classe PHP <tt>Contact.PHP</tt>.
+</p>
+
+<img src="<%~ directories2.gif %>" class="output" />
+
+<com:InfoBox>
+Une <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Pages">page</a> doit avoir un fichier de <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1">gabarit</a> (extension <tt>.page</tt>) ou un fichier de classe PHP, ou les deux :
+</p>
+
+<ul>
+<li>Une page avec seulement un gabarit est généralement une page statique, comme la page d'accueil que nous avons déjà créée ;</li>
+<li>Une page avec seulement un fichier de classe PHP produit le code HTML directement à partir de l'exécution du script.
+<li>Une page avec un gabarit et un fichier de classe PHP combine les avantages des deux : un gabarit pour facilement organiser la présentation de la page et un fichier de classe PHP pour produire le contenu dynamique.</li>
+</ul>
+</com:InfoBox>
+
+
+<h2>Création de la page gabarit</h2>
+
+<p>
+Nous allons premièrement créer le fichier gabarit de la page <tt>Contact</tt>.
+</p>
+
+<p>
+Nous utilisons un fichier gabarit pour organiser la présentation de notre formulaire. Dans notre gabarit, nous utilisons des <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.TextBox">champs de saisie</a> pour collecter le nom de l'utilisateur, son email et son commentaire. D'autre part, nous utilisons des <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Validation">validateurs</a> pour nous assurer que l'utilisateur a bien fourni les éléments avant d'envoyer le formulaire. Le contenu complet du gabarit est le suivant,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<html>
+<head><title>Mon Blog - Contact</title></head>
+<body>
+<h1>Contact</h1>
+<p>Veuillez remplir le formulaire suivant pour me laisser vos impressions au sujet de mon blog. Merci !</p>
+
+&lt;com:TForm>
+
+<span>Votre nom:</span>
+&lt;com:TRequiredFieldValidator ControlToValidate="Name"
+ ErrorMessage="Veuillez indiquer votre nom."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Name" />
+
+<br/>
+<span>Votre email:</span>
+&lt;com:TRequiredFieldValidator ControlToValidate="Email"
+ ErrorMessage="Veuillez indiquer votre email."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator ControlToValidate="Email"
+ ErrorMessage="Vous avez saisi un email invalide."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+
+<br/>
+<span>Commentaires:</span>
+&lt;com:TRequiredFieldValidator ControlToValidate="Feedback"
+ ErrorMessage="Veuillez saisir un commentaire."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Feedback"
+ TextMode="MultiLine"
+ Rows="10"
+ Columns="40" />
+
+<br/>
+&lt;com:TButton Text="Envoyer" OnClick="submitButtonClicked" />
+
+&lt;/com:TForm>
+
+</body>
+</html>
+</com:TTextHighlighter>
+
+<p>
+Comme vous pouvez le voir, un fichier gabarit ressemble énormément à un fichier HTML classique. La principale différence concerne le fichier gabarit qui contient quelques balises <tt>&lt;com:&gt;</tt>. Chaque balise <tt>&lt;com:&gt;</tt> fait référence à un <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Controls">contrôle</a> dont les propriétés sont initialisées grâce aux paires nom-valeur de la balise. Par exemple, la balise <tt>&lt;com:TButton&gt;</tt> fait référence au contrôle <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Button">TButton</a> qui affiche un bouton permettant à l'utilisateur de soumettre le formulaire. Pour une syntaxe complète, veuillez vous référer au <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1">Tutoriel de démarrage rapide</a>.
+
+<com:InfoBox>
+PRADO fournit un contrôle pour chaque type de balise HTML. Par exemple, <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.TextBox">TTextBox</a> affiche un champ de saisie, <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.List">TDropDownList</a> affiche une liste déroulante. Chaque contrôle est un composant auquel on peut accéder par code et dont les propriétés sont modifiables.
+</com:InfoBox>
+
+
+<p>
+Avant le contrôle <tt>TTextBox</tt>, le gabarit utilise aussi plusieurs validateurs qui permettent de s'assurer que les données saisies sont bien conformes à notre attente. Par exemple, pour nous assurer que l'adresse email est valide, nous utilisons les deux validateurs suivants,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<span>Your Email:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Email"
+ ErrorMessage="Veuillez indiquer votre email."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator
+ ControlToValidate="Email"
+ ErrorMessage="Vous avez saisi un email invalide."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+<br/>
+</com:TTextHighlighter>
+
+<p>
+Ci-dessous, un résumé des contrôles utilisés dans le gabarit :
+</p>
+
+<ul>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TForm">TForm</a> affiche un formulaire HTML. Chaque contrôle de saisie doit être au sein d'un TForm. Et le plus important, au plus un <tt>TForm</tt> peut apparaitre dans une page.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TTextBox">TTextBox</a> affiche un champ de saisie utilisateur.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TRequiredFieldValidator">TRequiredFieldValidator</a> s'assure que le contrôle de saisie associé n'est pas vide quand le formulaire est envoyé.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TEmailAddressValidator">TEmailAddressValidator</a> s'assure que le champ de saisie contient une adresse email <i>valide</i> quand le formulaire est envoyé.</li>
+</ul>
+
+<com:TipBox>
+Ecrire des gabarits seulement avec un éditeur de texte peut être pénible et pas vraiment intuitif pour les designers. Pour faciliter ceci, PRADO inclus dans cette version, une extension pour Dreamweaver qui permet la complétion automatique des balises PRADO (ceci inclut le nom des balises, le nom des propriétés, le nom des évènements, etc.).
+</com:TipBox>
+
+<h2>Création du fichier de classe PHP</h2>
+
+<p>
+Nous allons maintenant créer le fichier de classe PHP <tt>Contact.PHP</tt>. Ce fichier est nécessaire parce que nous devons agir après la soumission du formulaire.
+</p>
+
+<p>
+Notez les lignes dans le fichier gabarit. Elles indiquent que lorsque l'utilisateur soumet le formulaire, la méthode <tt>submitButtonClicked()</tt> doit être appelé. Ici, <tt>OnClick</tt> est le nom de l'évènement et la méthode correspondante doit être défini dans le fichier de classe PHP.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+ &lt;com:TButton Text="Submit" OnClick="submitButtonClicked" />
+</com:TTextHighlighter>
+
+<p>
+Nous écrirons donc le fichier de classe suivant :
+</p>
+
+<com:TTextHighlighter CssClass="source">
+<?PHP
+class Contact extends TPage
+{
+ /**
+ * Gestionnaire d'évènement pour OnClick (bouton submit).
+ * @param TButton le bouton qui a générer l'évènement
+ * @param TEventParameter les paramètres de l'évènement (null dans ce cas)
+ */
+ public function submitButtonClicked($sender, $param)
+ {
+ if ($this->IsValid) // vérifie que les validations sont Ok
+ {
+ // récupère le nom de l'utilisateur, son email et son commentaire
+ $name = $this->Name->Text;
+ $email = $this->Email->Text;
+ $feedback = $this->Feedback->Text;
+
+ // envoie un email à l'administrateur avec les informations
+ $this->mailFeedback($name, $email, $feedback);
+ }
+ }
+
+ protected function mailFeedback($name, $email, $feedback)
+ {
+ // implémentation de l'envoi de l'email
+ }
+}
+?>
+</com:TTextHighlighter>
+
+<p>
+Le code précédent est largement explicite. En fait, nous avons juste montré le principe d'un gestionnaire d'évènement. Dans le gestionnaire d'évènement <tt>submitButtonClicked()</tt>, nous récupérons les éléments saisies par l'utilisateur. Par exemple, <tt>$this->Name->Text</tt> retourne la valeur de la propriété <tt>Text</tt> du contrôle <tt>Name</tt> qui est un contrôle permettant la saisie du nom de l'utilisateur.
+</p>
+
+<com:InfoBox>
+Le nom de la classe héritant de TPage doit être le même que le nom du fichier. C'est aussi une nécessité pour écrire n'importe quelle classe de composant PRADO.
+</com:InfoBox>
+
+<h2>Test</h2>
+
+<p>
+Notre nouvelle page <tt>Contact</tt> peut être testée en naviguant à l'URL <tt>http://hostname/blog/index.PHP?page=Contact</tt>. Si vous cliquez sur le bouton "envoyer" sans avoir saisi de données, vous verrez apparaitre des messages d'erreurs à côté des champs de saisie. Si vous entrez toutes les informations nécessaires, la méthode <tt>mailFeedback()</tt> sera appelée.
+</p>
+
+<img src="<%~ output.gif %>" class="output" />
+
+<p>
+Une amélioration possible à notre page serait d'afficher un message de confirmation après que l'utilisateur ai envoyé le formulaire. Il serait aussi envisageable de rediriger le navigateur vers une adresse différente si toutes les informations ont été saisies correctement. Nous laisserons aux lecteurs la mise en place de ces fonctionnalités.
+</p>
+
+<com:InfoBox>
+Chaque validateur représente une règle de validation. Un champ de saisie unique peut être associé à un ou plusieurs validateurs. Les validateurs effectuent les vérifications aussi bien du côté client que du côté serveur. Côté client (navigateur), les validations sont effectuées grâce à du javascript, côté serveur, elles sont effectuées en PHP. Les validations côté client peuvent être désactivées tandis que celles côté serveur ne peuvent l'être. Ceci permet de s'assurer que les règles de validation sont toujours appliquées.
+</com:InfoBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/Setup.page b/demos/blog-tutorial/protected/pages/Day1/fr/Setup.page
new file mode 100755
index 00000000..22a7891c
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/Setup.page
@@ -0,0 +1,161 @@
+<com:TContent ID="Main">
+
+<h1>Installation</h1>
+
+<p>
+Nous commencerons par la mise en place de la structure des dossiers et fichiers requis par la plupart des applications développées avec PRADO. Nous allons utiliser <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.CommandLine">les outils en ligne de commande</a> pour atteindre ce but.
+</p>
+
+<p>Nous partons du principe que le nom du dossier qui contiendra l'application est <tt>blog</tt> et que l'URL qui permet d'accéder à ce dossier est : <tt>http://hostname/blog/</tt> (remplacer <tt>hostname</tt> par le nom de votre serveur).
+</p>
+
+<p>A l'intérieur du dossier blog, nous utilisons <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.CommandLine">les outils en ligne de commande</a> avec comme commande (remplacer <tt>path/to</tt> par le chemin d'installation du framework PRADO):
+</p>
+<com:TTextHighlighter CssClass="source cli">
+php path/to/prado-cli.php -c .
+</com:TTextHighlighter>
+
+<p>
+L'utilisation de cette commande permet de créer la structure de dossier et fichiers suivante:
+</p>
+
+<img src="<%~ directories.gif %>" class="output" />
+
+<p>
+Nous avons dorénavant, un squellette d'application PRADO accessible par l'URL <tt>http://hostname/blog/index.php</tt> et qui affiche une page contenant le message "Welcome to PRADO".
+</p>
+
+<p>
+Il est de notre intérêt d'en apprendre plus à propos des dossiers et fichiers que nous venons de créer.
+</p>
+
+<h2>Les fichiers initiaux</h2>
+
+<h3>Le script principal de l'application</h3>
+
+<p>
+Toutes les applications PRADO ont un point d'entrée, habituellement nommé <tt>index.php</tt>. Dans la plupart des cas, c'est le seul script qui est directement accessible par les utilisateurs. Cela réduit les risques que les utilisateurs puissent lancer des scripts serveur auquels ils ne devraient pas avoir accès.
+</p>
+
+<p>
+Le but principal de ce script est d'initialiser l'environnement PRADO et de gérer toutes les requêtes utilisateurs. Ce script contient habituellement les commandes PHP suivantes,
+</p>
+
+<com:TTextHighlighter CssClass="source">
+<?php
+// include prado.php which contains basic PRADO classes
+require_once('path/to/prado.php');
+// create a PRADO application instance
+$application = new TApplication;
+// run the application and handle user requests
+$application->run();
+?>
+</com:TTextHighlighter>
+
+<com:InfoBox>
+Le nom du script ne doit pas être obligatoirement <tt>index.php</tt>. Il peut porter n'importe quel nom à partir du moment ou le serveur peut l'interpréter comme étant un script PHP5. Par exemple, sur certains hébergements mutualisés, le script devra porter le nom <tt>index.php5</tt>, ce qui permettra au serveur Web de le traiter correctement.
+</com:InfoBox>
+
+<h3>Le fichier de configuration de l'application</h3>
+<p>
+Le fichier optionnel XML <tt>application.xml</tt> contient la <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.AppConfig">configuration de l'application</a>. Son but principal est de permettre de configurer l'application qui sera créée par le script principal. Par exemple, nous pouvons activer le système de <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Logging">log</a> pour notre application par le biais du fichier de configuration.
+</p>
+
+<p>
+Le fichier <tt>application.xml</tt> est pour le moment presque vide. De ce fait, nous pouvons le supprimer parce que l'application n'utilise pour le moment que des fonctionnalités de base. Au fur et à mesure que nous avancerons, nous ferons référence régulièrement au fichier <tt>application.xml</tt> et vous expliquerons comment configurer l'application.
+</p>
+
+
+<h3>La page d'accueil</h3>
+
+<p>
+La page d'accueil <tt>Home.page</tt> (aussi dénommée page par défaut) est la seule <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Pages">page</a> créée par les outils en ligne de commande de PRADO. C'est le contenu de ce fichier qui est affiché quand l'utilisateur navigue à l'adresse <tt>http://hostname/blog/index.php</tt>.
+</p>
+
+<p>
+Le contenu du fichier <tt>Home.page</tt> respecte le <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1">format de template</a> qui pour la plupart du temps est du code HTML agrémenté de quelques balises spécifiques à PRADO. Par exemple, dans <tt>Home.page</tt> nous voyons du code HTML pur :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<html>
+<head>
+ <title>Welcome to PRADO</title>
+</head>
+<body>
+<h1>Welcome to PRADO!</h1>
+</body>
+</html>
+</com:TTextHighlighter>
+
+
+<h2>Les dossiers initiaux</h2>
+
+<h3>Le dossier <tt>protected</tt></h3>
+
+<p>
+Le dossier <tt>protected</tt>, aussi connu sous le nom <i>chemin de base de l'application</i>, est le dossier racine qui contient les pages, les gabarits, les fichiers de configuration, les données, etc. Le nom <tt>protected</tt> indique que ce dossier doit être masqué des personnes qui consultent le site, ceci parce que les fichiers dans ce dossier contiennent la plupart du temps des données sensibles.
+</p>
+
+<p>
+Les différents serveurs Web ont différents moyens de "protéger" un dossier. Pour Apache, le moyen le plus simple est de créer dans le dossier un fichier nommé .htaccess avec le contenu <tt>deny from all</tt>.
+</p>
+
+
+<h3>Les dossiers <tt>protected/runtime</tt> et <tt>assets</tt></h3>
+
+<p>
+Les dossiers <tt>protected/runtime</tt> et <tt>assets</tt> sont deux dossiers qui doivent avoir l'autorisation "en écriture" pour le serveur Web. Le dossier <tt>runtime</tt> contient des données sensibles (ie: fichier de configuration déjà analysé) générées à l'exécution de PRADO tandis que le dossier <tt>assets</tt> contient les ressources qui doivent être publiques (ie: les images, les fichiers javascript).
+</p>
+
+<com:InfoBox>
+Il n'y a aucun souci à supprimer les dossiers et les fichiers contenus dans <tt>protected/runtime</tt> et <tt>assets</tt>. Il est recommandé aux développeurs de nettoyer ces dossiers lors d'une mise à jour de PRADO.
+</com:InfoBox>
+
+
+<h3>Le dossier <tt>pages</tt></h3>
+
+<p>
+The <tt>pages</tt> directory is the <i>root page directory</i> holding all <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Pages">pages</a> in a PRADO application. It bears an analogy to the <tt>htdocs</tt> directory for the Apache httpd Web server.
+</p>
+
+<p>
+Nous avons déjà vu comment accéder la page d'accueil. Pour accéder à n'importe quelle page situé dans le dossier <tt>pages</tt>, il faut utiliser l'URL suivante <tt>http://hostname/blog/index.php?page=chemin.vers.NomdelaPage</tt>. En fonction de cette URL, PRADO recherche une page dénommée <tt>NomdelaPage</tt> dans le dossier <tt>pages/chemin/vers</tt>. L'URL que nous avons utilisée précédemment pour accéder à la page d'accueil correspond à <tt>http://hostname/blog/index.php?page=Home</tt>.
+
+
+<h2>Personnalisation</h2>
+
+<p>
+Il est tout à fait possible de personnaliser le nom et l'emplacement des fichiers et dossiers décrit précédemment.
+</p>
+
+<p>
+Par exemple, pour améliorer la sécurité, certains pourraient désirer déplacer la totalité du dossier <tt>protected</tt> à un emplacement inaccessible par le Web. Pour faire cela, utilisez la commande PHP suivante pour initialiser l'instance de l'application PRADO dans le script principal :
+</p>
+
+<com:TTextHighlighter CssClass="source">
+$application = new TApplication( 'path/to/protected' );
+</com:TTextHighlighter>
+
+<p>
+Pour changer l'emplacement du dossier racine des pages et le nom de la page d'accueil, il est possible de modifier le fichier de configuration <tt>application.xml</tt> de cette manière :
+</p>
+
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<application id="blog" mode="Debug">
+ <services>
+ <service id="page"
+ class="TPageService"
+ BasePath="path.to.pages"
+ DefaultPage="NewHome"
+ />
+ </services>
+</application>
+</com:TTextHighlighter>
+
+<p>
+En avançant dans l'apprentissage de PRADO, vous verrez que PRADO est très souple et qu'il est possible de personnaliser la plupart des comportements de base. Nous décrirons d'autres techniques au fur et à mesure de ce tutoriel.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/ShareLayout.page b/demos/blog-tutorial/protected/pages/Day1/fr/ShareLayout.page
new file mode 100755
index 00000000..cefff770
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/ShareLayout.page
@@ -0,0 +1,182 @@
+<com:TContent ID="Main">
+
+<h1>Partager les modèles de gabarit</h1>
+
+<p>
+Dans cette section, nous allons utiliser la fonctionnalité <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.MasterContent">gabarit principal/contenu</a> de PRADO pour partager une mise en page commune sur tout notre site. Les mises en page communes font référence aux parties qui sont identiques ou presque pour un ensemble de pages. Par exemple, dans notre outil de blog, toutes les pages partagent le même entête, pied de page et la même barre latérale contenant les liens. La solution la plus radicale est de répéter sur chaque page les parties communes. Par contre, cette approche est une source d'erreurs et difficile à maintenir. La fonctionnalité <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.MasterContent">gabarit principal/contenu</a> nous permets de traiter les parties communes comme un contrôle qui centralise la logique applicative et la présentation de chaque page.
+</p>
+
+<com:InfoBox>
+Il est aussi possible de partager les parties communes grâce à <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1">l'inclusion de gabarits</a>, un peu comme l'inclusion de fichier php. L'inconvénient de l'inclusion de gabarits est que l'on ne peut pas partager la logique applicative.
+</com:InfoBox>
+
+
+<h2>Création du gabarit principal</h2>
+
+<p>
+Nous allons maintenant créer le gabarit principal <tt>MainLayout</tt> qui représente les parties communes partagées par toutes nos pages. Le contrôle <tt>MainLayout</tt> est un contrôle de gabarit qui hérite de <tt>TTemplateControl</tt>. Il a besoin d'un fichier de gabarit <tt>MainLayout.tpl</tt> et d'un fichier de classe <tt>MainLayout.php</tt> situés dans le même dossier. Pour faciliter la maintenance, nous allons créer le nouveau dossier <tt>protected/layouts</tt> pour les accueillir.
+</p>
+
+<img src="<%~ directories3.gif %>" class="output" />
+
+<p>
+Pour le moment, <tt>MainLayout</tt> contient seulement un entête simple et un pied de page, comme décrit ci-après. Plus tard, nous ajouterons une barre latérale. Les lecteurs sont encouragés à ajouter des fonctionnalités.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<html>
+&lt;com:THead />
+<body>
+&lt;com:TForm>
+<div id="page">
+
+<div id="header">
+<h1>Mon blog PRADO</h1>
+</div>
+
+<div id="main">
+&lt;com:TContentPlaceHolder ID="Main" />
+</div>
+
+<div id="footer">
+&lt;%= PRADO::poweredByPrado() %>
+</div>
+
+</div>
+&lt;/com:TForm>
+</body>
+</html>
+</com:TTextHighlighter>
+
+<p>
+Ci-dessus, le contenu du fichier de gabarit <tt>MainLayout.tpl</tt>. Trois nouvelles balises sont utilisées.
+</p>
+
+<ul>
+<li><tt>&lt;com:TContentPlaceHolder&gt;</tt> represente un contrôle <a href="http://www.pradosoft.com/docs/classdoc/TContentPlaceHolder">TContentPlaceHolder</a>. Il définit l'emplacement dans le gabarit où le contenu sera inséré. Dans notre cas, le contenu proviendra des pages qui utiliseront notre gabarit principal.</li>
+<li><tt>&lt;com:THead&gt;</tt> représente un contrôle <a href="http://www.pradosoft.com/docs/classdoc/THead">THead</a> qui correspond à la balise &lt;head&gt; d'un document HTML. Ceci permet à PRADO de manipuler la balise &lt;head&gt; comme un composant (ie: définir le titre de la page, ajouter des feuilles de styles CSS.)</li>
+<li><tt>&lt;%= %&gt;</tt> est une <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates2#et">balise de configuration</a>. Elle affiche le résultat de l'évaluation de l'expression à l'endroit où elle se situe.</li>
+</ul>
+
+
+<p>
+Le fichier de classe <tt>MainLayout.php</tt> est très simple :
+</p>
+
+<com:TTextHighlighter CssClass="source">
+<?php
+class MainLayout extends TTemplateControl
+{
+}
+?>
+</com:TTextHighlighter>
+
+<com:InfoBox>
+L'extension des fichiers de gabarit est <tt>.page</tt>, tandis que pour les gabarits autres que les pages c'est <tt>.tpl</tt>. Ceci permet de différencier les pages des autres contrôles. Les deux utilisent la même <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1">syntaxe de gabarit</a>. Pour les pages, le fichier de classe est optionnel (par défaut hérite de <tt>TPage</tt>), tandis que pour les contrôles, les fichiers de classes sont obligatoires. Comme pour Java, le nom de la classe doit être le même que le nom du fichier de classe. Faites attention à la casse sur les systèmes Linux/Unix.
+</com:InfoBox>
+
+<h2>Utilisation du gabarit principal</h2>
+<p>
+Pour utiliser notre gabarit principal nouvellement créé, nous allons modifier nos fichiers <tt>Home.page</tt> et <tt>Contact.page</tt>. En particulier, nous devons supprimer les entêtes et pied de page parce que le gabarit principal a la responsabilité de les afficher ; par ailleurs, nous devons indiquer aux deux pages que leur gabarit principal est <tt>MainLayout</tt>.
+</p>
+
+<p>
+Ci-dessous, le contenu de <tt>Contact.page</tt> après les modifications :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ MasterClass="Application.layouts.MainLayout" Title="Mon blog - Contact" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Contact</h1>
+<p>Veuillez remplir le formulaire suivant pour me laisser vos impressions au sujet de mon blog. Merci !</p>
+
+...champs de saisie et validateurs pour le nom d'utilisateur...
+
+...champs de saisie et validateurs pour l'email...
+
+...champs de saisie et validateurs pour le commentaire...
+
+&lt;com:TButton Text="Envoyer" OnClick="submitButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Le contenu entre les balises <tt>&lt;com:TContent&gt;</tt> sera inséré dans l'emplacement réservé par <tt>&lt;com:TContentPlaceHolder&gt;</tt> dans le gabarit principal.
+</p>
+
+<com:InfoBox>
+Il est possible d'avoir plusieurs <tt>TContentPlaceHolder</tt> dans un gabarit principal et plusieurs <tt>TContent</tt> dans un fichier de contenu. Ils sont associés par leurs propriétés <tt>ID</tt>. Il est aussi possible de définir un contenu comme étant le gabarit principal d'un autre contenu, ceci en plaçant une balise <tt>TContentPlaceHolder</tt> à l'endroit désiré. Ceci est appelé <i>gabarits principaux imbriqués</i>
+</com:InfoBox>
+
+<p>
+A côté de la balise <tt>&lt;com:TContent&gt;</tt>, nous avons vu une nouvelle balise <tt>&lt;%@ %&gt;</tt>, qui est dénommé une <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.Templates1#tct">balise de contrôle de gabarit</a>. Elle contient des paires nom-valeur utilisées pour initialiser les propriétés correspondantes du propriétaire de gabarit, dans notre cas, la page <tt>Contact</tt>.
+</p>
+
+<p>
+En définissant la propriété <tt>MasterClass</tt> comme étant de type <tt>Application.layouts.MainLayout</tt>, nous avons indiqué à la page <tt>Contact</tt> d'utiliser <tt>MainLayout</tt> comme gabarit principal. Ici, nous avons utilisé un <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Components">espace de noms</a> pour nous référer à la classe <tt>MainLayout</tt>.
+</p>
+
+<com:InfoBox>
+Les espaces de noms sont largement utilisés en programmation PRADO. Ils sont utilisés conjointement avec les <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Fundamentals.Components">alias de chemins</a>. PRADO définit deux alias de chemins: <tt>System</tt> fait référence au dossier <tt>framework</tt> de l'installation PRADO, et <tt>Application</tt> fait référence au dossier <tt>protected</tt>.
+L'espace de noms <tt>Application.layouts.MainLayout</tt> peut ainsi être traduit par <tt>protected/layouts/MainLayout</tt> ce qui est précisément le nom du fichier (sans l'extension <tt>.php</tt>) de la classe <tt>MainLayout</tt>.
+</com:InfoBox>
+
+
+<h2>Autres possibilités pour spécifier le gabarit principal</h2>
+
+<p>
+Il y a plusieurs alternatives pour spécifier le gabarit principal.
+</p>
+
+<p>
+Vous pouvez définir le gabarit principal comme ci-dessous pour pouvoir en changer dynamiquement.
+</p>
+
+<com:TTextHighlighter CssClass="source">
+<?php
+class Contact extends TPage
+{
+ public function onPreInit($param)
+ {
+ parent::onPreInit($param);
+ $this->MasterClass='Path.To.NewLayout';
+ }
+
+ // ...
+}
+?>
+</com:TTextHighlighter>
+
+<p>
+Ci-dessus, nous indiquons d'utiliser le gabarit principal <tt>MasterClass</tt> dans la méthode <tt>onPreInit()</tt> qui est héritée de <tt>TPage</tt>. Cette méthode est appelé par PRADO juste après que l'instance de la page soit créée. Nous pouvons ainsi déclarer au moment où la page est requise quel gabarit principal utiliser. Par exemple, quand la page est requise par un utilisateur enregistré, nous pouvons utiliser le gabarit A, et le gabarit B si l'utilisateur qui demande la page est un invité.
+</p>
+
+<p>
+Nous pouvons aussi spécifier quel gabarit principal utiliser dans le fichier de <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.AppConfig">configuration de l'application</a> ou encore dans le fichier de <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.PageConfig">configuration de la page</a>. Ci-dessous, le fichier de configuration de l'application modifié pour notre blog.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<application id="blog" mode="Debug">
+ <!-- configuration for available services -->
+ <services>
+ <service id="page" class="TPageService" DefaultPage="Home">
+ <!-- initial properties set for all pages -->
+ <pages MasterClass="Application.layouts.MainLayout" />
+ </service>
+ </services>
+</application>
+</com:TTextHighlighter>
+
+<p>
+En faisant cela, nous évitons de définir le gabarit principal dans chaque page. Si nous décidons d'utiliser un autre gabarit principal, il nous suffit de changer le fichier de configuration de l'application. Pour cette raison, dans notre blog, nous utiliserons cette approche.
+</p>
+
+<com:InfoBox>
+Il y a un ordre qui permet de savoir quel fichier gabarit principal utiliser s'il est spécifié à plusieurs endroits. En particulier <tt>onPreInit()</tt> est prioritaire au fichier de configuration de la page qui est lui même prioritaire au fichier de configuration de l'application. Ainsi, si vous spécifiez <tt>MainLayout</tt> dans le fichier de configuration de l'application/page et que vous spécifiez <tt>SpecialLayout</tt> dans <tt>Contact.page</tt>, ce sera le dernier qui sera pris en compte.
+</com:InfoBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/directories.gif b/demos/blog-tutorial/protected/pages/Day1/fr/directories.gif
new file mode 100755
index 00000000..884e15bc
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/directories2.gif b/demos/blog-tutorial/protected/pages/Day1/fr/directories2.gif
new file mode 100755
index 00000000..edf264d0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/directories2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/directories3.gif b/demos/blog-tutorial/protected/pages/Day1/fr/directories3.gif
new file mode 100755
index 00000000..3451935f
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/directories3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day1/fr/output.gif b/demos/blog-tutorial/protected/pages/Day1/fr/output.gif
new file mode 100755
index 00000000..9ad2bfb8
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day1/fr/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/ConnectDB.page b/demos/blog-tutorial/protected/pages/Day2/fr/ConnectDB.page
new file mode 100755
index 00000000..bc817c92
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/ConnectDB.page
@@ -0,0 +1,47 @@
+<com:TContent ID="Main">
+
+<h1>Connexion à la base</h1>
+
+
+<p>
+Pour pouvoir utiliser la base de données que nous venons de créer, nous devons tout d'abord établir une connexion.
+</p>
+
+<p>
+Nous allons utiliser <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.DAO">Data Access Objects (DAO)</a> pour établir une couche d'abstraction. Si dans le futur nous décidions d'utiliser un autre SGBD (ie: PostgreSQl, Oracle, ...), il nous suffirait de modifier la chaine de connexion (DSN) et nous pourrions conserver notre code PHP intact.
+</p>
+
+<com:NoteBox>
+Pour utiliser DAO, nous devons installer et activer la librairie <a href="http://www.php.net/manual/en/ref.pdo.php">PHP PDO extension</a> <i>ainsi que</i> un driver PDO d'accès aux données (dans notre cas, c'est le driver PDO SQLite). Ceci peut être fait facilement sous Windows en incluant dans le fichier <tt>php.ini</tt> les lignes suivantes:
+<com:TTextHighlighter CssClass="source">
+extension=php_pdo.dll
+extension=php_pdo_sqlite.dll
+</com:TTextHighlighter>
+</com:NoteBox>
+
+<p>
+Pour une meilleure abstraction de notre base de données, nous utilisons aussi la fonctionnalité <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a> (AR). Chaque enregistrement est ainsi représenté par un objet qui a la possibilité d'exécuter des requêtes, de mettre à jour les données, de les supprimer et ceci sans écrire la moindre commande SQL.
+</p>
+
+<p>
+Nous modifions notre fichier de configuration de l'application <tt>protected/application.xml</tt> en insérant les lignes suivantes, qui indiquent à <tt>Active Record</tt> comment se connecter à notre base de données.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<modules>
+ <module id="db" class="System.Data.TDataSourceConfig">
+ <database ConnectionString="sqlite:protected/data/blog.db" />
+ </module>
+ <module class="System.Data.ActiveRecord.TActiveRecordConfig" ConnectionID="db" />
+</modules>
+</com:TTextHighlighter>
+
+<p>
+Dans la configuration précédente, nous avons ajouté deux <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Modules">modules</a> à notre application. Le module <tt>TDataSourceConfig</tt> est configuré avec la chaine de connexion <tt>sqlite:protected/data/blog.db</tt> qui pointe vers notre base de données. Cette connexion est utilisée par le module <tt>TActiveRecordConfig</tt> qui est requis pour l'utilisation d'Active Record.
+</p>
+
+<com:InfoBox>
+Il est tout à fait possible de définir plusieurs connexion de base de données dans notre fichier de configuration. Pour plus de détails, veuillez vous référer à la <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">documentation Active Record</a>. Il est, de même possible, d'établir une connexion à une base de données en utilisant du code PHP au travers du composant <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.DAO">TDbConnection</a>.
+</com:InfoBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/CreateAR.page b/demos/blog-tutorial/protected/pages/Day2/fr/CreateAR.page
new file mode 100755
index 00000000..82c1dbf8
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/CreateAR.page
@@ -0,0 +1,177 @@
+<com:TContent ID="Main">
+
+<h1>Création des classes Active Record</h1>
+
+<p>
+Nous avons besoin de définir deux classes <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a>, <tt>UserRecord</tt> et <tt>PostRecord</tt>, qui représentent respectivement les tables <tt>users</tt> et <tt>posts</tt>. Les classes Active Record doivent hériter de la classe <tt>ActiveRecord</tt>, et doivent définir les propriétés qui correspondent aux champs de la table correspondante.
+</p>
+
+<p>
+Pour une meilleure organisation de notre arborescence, nous créons un nouveau dossier <tt>protected/database</tt> qui contiendra nos deux classes. Nous modifions notre fichier de configuration de l'application en y insérant les lignes suivantes. Ceci est équivalent à inclure le dossier <tt>protected/database</tt> à notre chemin d'inclusion de PHP (<tt>include_path</tt>). Cela nous permet d'utiliser nos classes sans avoir besoin de les inclure explicitement.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<paths>
+ <using namespace="Application.database.*" />
+</paths>
+</com:TTextHighlighter>
+
+<p>
+Au lieu de créer nos classes manuellement, nous allons utiliser les <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.CommandLine">outils en ligne de commande de PRADO</a> pour qu'il nous génère les classes.
+</p>
+
+<p>
+Dans le dossier <tt>blog</tt>, lancer la commande suivante pour entrer dans le mode interactif de l'outil en ligne de commande:
+</p>
+
+<com:TTextHighlighter CssClass="source cli">
+php path/to/prado-cli.php shell .
+</com:TTextHighlighter>
+
+<p>
+Vous devriez voir
+</p>
+
+<com:TTextHighlighter CssClass="source cli" Language="text">
+Command line tools for Prado 3.1.0.
+** Loaded PRADO appplication in directory "protected".
+PHP-Shell - Version 0.3.1
+(c) 2006, Jan Kneschke <jan@kneschke.de>
+
+>> use '?' to open the inline help
+
+>>
+</com:TTextHighlighter>
+
+<p>
+A l'invite de commande, entrer les deux commandes suivantes pour créer les classes <tt>UserRecord</tt> et <tt>PostRecord</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source cli" Language="text">
+>> generate users Application.database.UserRecord
+
+>> generate posts Application.database.PostRecord
+</com:TTextHighlighter>
+
+<p>
+Ici, nous avons utilisés les <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Components">espaces de noms</a> pour indiquer où les classes seront créées. Le chemin <tt>Application.database.UserRecord</tt> indique que nous désirons que la classe soit insérée dans <tt>protected/database/UserRecord.php</tt>.
+</p>
+
+<p>
+Nous devrions voir l'arborescence suivante ainsi que nos deux nouveaux fichiers dans le dossier <tt>protected/database</tt>:
+</p>
+
+<img src="<%~ directories2.gif %>" class="output" />
+
+<p>
+Si vous consultez le fichier <tt>PostRecord</tt>, vous devriez voir le contenu suivant:
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class PostRecord extends TActiveRecord
+{
+ const TABLE='posts';
+ public $post_id;
+ public $author_id;
+ public $create_time;
+ public $title;
+ public $content;
+ public $status;
+
+ public static function finder($className=__CLASS__)
+ {
+ return parent::finder($className);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Comme vous pouvez le constater, pour chaque champs de la table <tt>posts</tt>, la classe déclare un membre correspondant. La constante <tt>TABLE</tt> indique le nom de la table que gère la classe <tt>PostRecord</tt>. La méthode statique <tt>finder()</tt> permet d'effectuer des requêtes et de lire les données sous forme d'objets <tt>PostRecord</tt>.
+</p>
+<p>
+Vous pouvez utiliser l'outil en ligne de commande pour tester nos nouvelles classes. En restant dans le mode interactif de l'outil en ligne de commande, vous pouvez saisir les commandes PHP et voir ce qui suit. Vous pouvez tester des commandes telles que <tt>UserRecord::finder()->findAll()</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source cli" Language="text">
+>> PostRecord::finder()->findAll()
+array
+(
+ [0] => PostRecord#1
+ (
+ [post_id] => '1'
+ [author_id] => 'admin'
+ [create_time] => '1175708482'
+ [title] => 'first post'
+ [content] => 'this is my first post'
+ [status] => '0'
+ [TActiveRecord:_readOnly] => false
+ [TActiveRecord:_connection] => null
+ [TComponent:_e] => array()
+ )
+)
+</com:TTextHighlighter>
+
+<h1>Relations entre Posts et Users</h1>
+<p>
+Rappellez-vous qu'il y a une relation entre les tables <tt>users</tt> et <tt>posts</tt>. Le diagramme des relations est indiqué ci-après.
+</p>
+
+<img src="<%~ ER.gif %>" class="output" />
+
+<p>
+A partir de ce diagramme, nous voyons que la table <tt>posts</tt> contient un champ <tt>author_id</tt>. Le champ <tt>author_id</tt> est une clé étrangère qui référence la table <tt>users</tt>. En particulier, les valeurs du champ <tt>author_id</tt> doivent apparaitre dans dans le champ <tt>username</tt> de la table <tt>users</tt>. Une des conséquences de cette relation, en réfléchissant orienté objet, est que chaque "post" repose sur un "author" et qu'un "author" peut avoir plusieurs "posts".
+</p>
+
+<p>
+Nous pouvons modéliser les relations entre <tt>posts</tt> and <tt>users</tt> dans nos classes <tt>PostRecord</tt> and <tt>UserRecord</tt> en les modifiant comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class PostRecord extends TActiveRecord
+{
+ //... propriétés et méthodes comme précédemment
+
+ public $author; //contient un objet UserRecord
+
+ protected static $RELATIONS=array
+ (
+ 'author' => array(self::BELONGS_TO, 'UserRecord'),
+ );
+}
+</com:TTextHighlighter>
+
+<p>
+La propriété statique <tt>$RELATIONS</tt> de la classe <tt>PostRecord</tt> définit que la propriété <tt>$author</tt> fait référence à un <tt>UserRecord</tt>. Dans le tableau : <tt>array(self::BELONGS_TO, 'UserRecord')</tt>, le premier élément défini le type de relation, dans notre cas, <tt>self::BELONGS_TO</tt>. Le deuxième élément est le nom de l'objet en relation, dans notre cas <tt>UserRecord</tt>. La classe <tt>UserRecord</tt> est définie comme ci-dessous, la différence est que chaque objet <tt>UserRecord</tt> contient plusieurs <tt>PostRecord</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class UserRecord extends TActiveRecord
+{
+ //... propriétés et méthodes comme précédemment
+
+ public $posts=array(); //contient un tableau de PostRecord
+
+ protected static $RELATIONS=array
+ (
+ 'posts' => array(self::HAS_MANY, 'PostRecord'),
+ );
+}
+</com:TTextHighlighter>
+
+<p>
+Un tableau de <tt>UserRecord</tt> ainsi que les messages correspondants peuvent être lu de la manière suivante :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+$users = UserRecord::finder()->withPosts()->findAll();
+</com:TTextHighlighter>
+
+<com:TipBox>
+La méthode <tt>withXXX()</tt> (avec XXX qui est le nom de la propriété de la relation, dans notre cas <tt>Posts</tt>) lit les données correspondantes de <tt>PostRecords</tt> en utilisant une deuxième requête (mais pas en utilisant une jointure). La méthode <tt>withXXX()</tt> accepte les mêmes arguments que les autres méthodes <tt>finder</tt> de l'objet Active record, ie : <tt>withPosts('status = ?', 0)</tt>.
+</com:TipBox>
+
+<p>
+Plus d'informations sont disponibles dans le manuel de démarrage rapide <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a>.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/CreateDB.page b/demos/blog-tutorial/protected/pages/Day2/fr/CreateDB.page
new file mode 100755
index 00000000..d982e388
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/CreateDB.page
@@ -0,0 +1,70 @@
+<com:TContent ID="Main">
+
+<h1>Création de la base</h1>
+<
+<p>
+La plupart des applications Web utilisent une base de données pour conserver les informations. Notre blog n'est pas une exception. Dans cette section, nous allons décrire comment écrire une application qui interagit avec une base de données. Nous allons utiliser les deux moyens suivants <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.DAO">database access object (DAO)</a> et <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a>.
+</p>
+
+<p>
+Pour ce tutoriel, nous avons simplifié les besoins, nous aurons juste à gérer les utilisateurs et les messages. Nous allons donc créer deux tables <tt>users</tt> et <tt>posts</tt>, comme décrit dans le diagramme ci-après.
+</p>
+
+<img src="<%~ ER.gif %>" class="output" />
+
+<p>
+Nous utilisons une base de données SQLite 3 pour conserver nos données. La première étape est de convertir notre diagramme en commandes SQL et de l'enregistrer dans le fichier <tt>protected/schema.sql</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source">
+/* création de la table utilisateurs */
+CREATE TABLE users (
+ username VARCHAR(128) NOT NULL PRIMARY KEY,
+ email VARCHAR(128) NOT NULL,
+ password VARCHAR(128) NOT NULL, /* mot de passe en clair */
+ role INTEGER NOT NULL, /* 0: utilisateur normal, 1: administrateur */
+ first_name VARCHAR(128),
+ last_name VARCHAR(128)
+);
+
+/* création de la table messages */
+CREATE TABLE posts (
+ post_id INTEGER NOT NULL PRIMARY KEY,
+ author_id VARCHAR(128) NOT NULL
+ CONSTRAINT fk_author REFERENCES users(username),
+ create_time INTEGER NOT NULL, /* UNIX timestamp */
+ title VARCHAR(256) NOT NULL, /* titre du message */
+ content TEXT, /* corps du message */
+ status INTEGER NOT NULL /* 0: publié; 1: brouillon; 2: en attente; 2: accès interdit */
+);
+
+/* insertion de quelques données initiales */
+INSERT INTO users VALUES ('admin', 'admin@example.com', 'demo', 1, 'Qiang', 'Xue');
+INSERT INTO users VALUES ('demo', 'demo@example.com', 'demo', 0, 'Wei', 'Zhuo');
+INSERT INTO posts VALUES (NULL, 'admin', 1175708482, 'first post', 'this is my first post', 0);
+</com:TTextHighlighter>
+
+<com:NoteBox>
+La contrainte <tt>fk_author</tt> est ignorée par SQlite vu que SQLite ne supporte pas les <a href="http://www.sqlite.org/omitted.html">clés étrangères</a>.
+Néanmoins, nous gardons cette contrainte dans le but de pouvoir porter notre blog sur d'autres SGBD. Par ailleurs, nous utilisons la capacité <a href="http://www.sqlite.org/autoinc.html">auto incrémenté</a> du champ <tt>posts.post_id</tt> si celui-ci est renseigné à NULL lors de l'insertion.
+</com:NoteBox>
+
+<p>
+Nous utilisons après ceci, les <a href="http://www.sqlite.org/download.html">outils en ligne de commande SQLite</a> pour créer la base de données. Nous avons créé un dossier <tt>protected/data</tt> qui contiendra le fichier de base de données. Nous lançons maintenant la ligne de commande suivante dans le dossier <tt>protected/data</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source cli">
+sqlite3 blog.db < ../schema.sql
+</com:TTextHighlighter>
+
+<p>
+La base de données est ainsi créée dans le fichier <tt>protected/data/blog.db</tt> et nous pouvons maintenant voir la l'arborescence suivante :
+</p>
+
+<img src="<%~ directories.gif %>" class="output" />
+
+<com:NoteBox>
+Il est nécessaire pour SQLite que le dossier <tt>protected/data</tt> et le fichier <tt>protected/data/blog.db</tt> soient accessibles en écriture par le serveur Web.
+</com:NoteBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/ER.gif b/demos/blog-tutorial/protected/pages/Day2/fr/ER.gif
new file mode 100755
index 00000000..7a5397b3
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/ER.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd b/demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd
new file mode 100755
index 00000000..474833fd
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/directories.gif b/demos/blog-tutorial/protected/pages/Day2/fr/directories.gif
new file mode 100755
index 00000000..797ef932
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day2/fr/directories2.gif b/demos/blog-tutorial/protected/pages/Day2/fr/directories2.gif
new file mode 100755
index 00000000..b053b4c6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day2/fr/directories2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/Auth.page b/demos/blog-tutorial/protected/pages/Day3/fr/Auth.page
new file mode 100755
index 00000000..0cced90b
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/Auth.page
@@ -0,0 +1,106 @@
+<com:TContent ID="Main">
+
+<h1>Authentification et Autorisation</h1>
+
+
+<p>
+Avant que nous n'implémentions la gestion des utilisateurs, nous devons activer les modules <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Advanced.Auth">authentification et autorisation</a>.
+</p>
+
+
+<p>
+Nous ajoutons deux nouveaux modules à notre fichier de configuration de l'application.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<modules>
+ ...modules TDataSourceConfig et TActiveRecordConfig ...
+
+ <module id="auth"
+ class="System.Security.TAuthManager"
+ UserManager="users"
+ LoginPage="users.LoginUser" />
+
+ <module id="users"
+ class="System.Security.TDbUserManager"
+ UserClass="Application.BlogUser" />
+</modules>
+</com:TTextHighlighter>
+
+<p>
+Le module <a href="http://www.pradosoft.com/docs/classdoc/TAuthManager">TAuthManager</a> gère le processus d'authentification et d'autorisation. Il utilise le module <tt>users</tt> comme gestionnaire d'utilisateur (voir ci-après). En spécifiant la propriété <tt>LoginPage</tt>, nous indiquons au module d'authentification de rediriger vers la page <tt>LoginUser</tt> quand il est nécessaire de s'authentifier. Nous décrirons comment créer la page <tt>LoginUser</tt> ci-après.
+</p>
+
+<p>
+Le module <tt>user</tt> est une classe de type <a href="http://www.pradosoft.com/docs/classdoc/TDbUserManager">TDbUserManager</a> qui est responsable de la vérification et de la validation des utilisateurs et qui enregistre dans une session PHP les données utilisateurs. La propriété <tt>UserClass</tt> est initialisée comme étant de type <tt>Application.BlogUser</tt>, ceci indique au module <tt>user</tt> de chercher une classe <tt>BlogUser</tt> dans le dossier <tt>protected</tt> (rappellez-vous que l'alias <tt>Application</tt> fait référence au dossier <tt>protected</tt>) et d'utiliser cette classe pour conserver les données utilisateurs dans une session.
+Le module <tt>user</tt> est une classe de type <a href="http://www.pradosoft.com/docs/classdoc/TDbUserManager">TDbUserManager</a> qui est responsable de la vérification et de la validation des utilisateurs et qui enregistre dans une session PHP les données utilisateurs. La propriété <tt>UserClass</tt> est initialisée comme étant de type <tt>Application.BlogUser</tt>, ceci indique au module <tt>user</tt> de chercher une classe <tt>BlogUser</tt> dans le dossier <tt>protected</tt> (rappellez-vous que l'alias <tt>Application</tt> fait référence au dossier <tt>protected</tt>) et d'utilisez cette classe pour conserver les données utilisateurs dans une session.
+</p>
+
+<p>
+Comme vous pourrez le constater dans les sections suivantes, dans les différents contrôles et pages, nous pourrons utiliser <tt>$this->User</tt> pour accéder à l'objet qui contient les informations de l'utilisateur actuellement connecté.
+</p>
+
+
+<p>
+Ci-dessous les détails de l'implémentation de la classe <tt>BlogUser</tt>. Remarquez que les <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Database.ActiveRecord">Active Record</a> sont utilisés pour exécuter une requête. Par exemple, nous utilisons <tt>UserRecord::finder()->findByPk($username)</tt> pour chercher la valeur de <tt>$username</tt> dans la table <tt>users</tt> et ceci par la clé primaire.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+// Include TDbUserManager.php file which defines TDbUser
+Prado::using('System.Security.TDbUserManager');
+
+/**
+ * La classe BlogUser.
+ * BlogUser représente les données utilisateurs à conserver en session.
+ * L'implémentation par défaut conserve le nom et le rôle de l'utilisateur.
+ */
+class BlogUser extends TDbUser
+{
+ /**
+ * Créer un objet de type BlogUser basé sur le nom de l'utilisateur.
+ * Cette méthode est requise par TDbUser. Cet objet vérifie si l'utilisateur
+ * est bien présent en base de données. Si oui, un objet BlogUser
+ * est créé et initialisé.
+ * @param string le nom de l'utilisateur
+ * @return l'objet BlogUser, null si le nom de l'utilisateur est invalide.
+ */
+ public function createUser($username)
+ {
+ // utilise l'Active Record UserRecord pour chercher l'utilisateur username
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if($userRecord instanceof UserRecord) // si trouvé
+ {
+ $user=new BlogUser($this->Manager);
+ $user->Name=$username; // enregistre le nom de l'utilisateur
+ $user->Roles=($userRecord->role==1?'admin':'user'); // et son rôle
+ $user->IsGuest=false; // l'utilisateur n'est pas un invité
+ return $user;
+ }
+ else
+ return null;
+ }
+
+ /**
+ * Vérifie que le nom d'utilisateur et son mot de passe sont correct.
+ * Cette méthode est requise par TDbUser.
+ * @param string le nom de l'utilisateur
+ * @param string le mot de passe
+ * @return boolean en fonction de la validité de la vérification.
+ */
+ public function validateUser($username,$password)
+ {
+ // utilise l'Active Record UserRecord pour vérifier le nom d'utilisateur couplé au mot de passe.
+ return UserRecord::finder()->findBy_username_AND_password($username,$password)!==null;
+ }
+
+ /**
+ * @return boolean indiquant si l'utilisateur est un administrateur.
+ */
+ public function getIsAdmin()
+ {
+ return $this->isInRole('admin');
+ }
+}
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/CreateAdminUser.page b/demos/blog-tutorial/protected/pages/Day3/fr/CreateAdminUser.page
new file mode 100755
index 00000000..04571c43
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/CreateAdminUser.page
@@ -0,0 +1,159 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page d'administration des utilisateurs <tt>AdminUser</tt></h1>
+
+<p>
+La page <tt>AdminUser</tt> affiche la liste de tous les comptes utilisateurs, ainsi l'administrateur peut effectuer les tâches de maintenance. Par simplification, les tâches administratives pour notre blog seront la mise à jour des utilisateurs et la suppression.
+</p>
+
+
+<p>
+Nous allons lister les utilisateurs dans une table HTML. Chaque ligne correspondra à un compte utilisateur, les colonnes suivantes seront affichées:
+</p>
+
+<ul>
+<li>Pseudo - affiche le pseudo de l'utilisateur. Dans chaque cellule un lien sera affiché qui nous dirigera vers la page <a href="?page=Day3.CreateEditUser">EditUser</a>.</li>
+<li>Email - affiche l'email.</li>
+<li>Administrateur - indique si le compte est celui d'un administrateur.</li>
+<li>Commande - affiche une colonne de bouton "supprimer". En cliquant sur un de ces boutons, la suppression du compte sera effectuée.</li>
+</ul>
+
+<p>
+Nous créons deux fichiers <tt>protected/pages/users/AdminUser.page</tt> et <tt>protected/pages/users/AdminUser.php</tt> qui contiendront respectivement le gabarit et la classe.
+</p>
+
+<h2>Création du gabarit</h2>
+<p>
+Nous allons utiliser un contrôle <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.DataGrid">TDataGrid</a> pour afficher les données. Suivant l'analyse précédente, nous allons configurer quatre colonnes:
+</p>
+
+<ul>
+<li><a href="http://www.pradosoft.com/docs/classdoc/THyperLinkColumn">THyperLinkColumn</a> affiche le pseudo. L'URL sera construite suivant les instructions de la propriété <tt>DataNavigateUrlFormatString</tt>.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TBoundColumn">TBoundColumn</a> affiche l'email.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TCheckBoxColumn">TCheckBoxColumn</a> utilise des cases à cocher pour indiquer si le compte est un compte administrateur.</li>
+<li><a href="http://www.pradosoft.com/docs/classdoc/TButtonColumn">TButtonColumn</a> affiche un bouton "Supprimer".</li>
+</ul>
+
+
+<p>Le gabarit complet est affiché ci-après:</p>
+
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon Blog - Administration des comptes utilisateurs" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Administration des comptes utilisateurs</h1>
+
+<a href="&lt;%= $this->Service->constructUrl('users.NewUser')%>">Créer un nouvel utilisateur</a>
+<br/>
+
+&lt;com:TDataGrid ID="UserGrid"
+ DataKeyField="username"
+ AutoGenerateColumns="false"
+ OnDeleteCommand="deleteButtonClicked">
+
+ &lt;com:THyperLinkColumn
+ HeaderText="Pseudo"
+ DataTextField="username"
+ DataNavigateUrlField="username">
+ &lt;prop:DataNavigateUrlFormatString>#
+ $this->Service->constructUrl('users.EditUser',array('username'=>{0}))
+ &lt;/prop:DataNavigateUrlFormatString>
+ &lt;/com:THyperLinkColumn>
+
+ &lt;com:TBoundColumn
+ HeaderText="Email"
+ DataField="email" />
+
+ &lt;com:TCheckBoxColumn
+ HeaderText="Administrateur"
+ DataField="role" />
+
+ &lt;com:TButtonColumn
+ HeaderText="Commande"
+ Text="Supprimer"
+ ButtonType="PushButton"
+ CommandName="delete" />
+
+&lt;/com:TDataGrid>
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Création du fichier de classe</h2>
+
+
+<p>
+Dans le gabarit précédent, le bouton <tt>OnDeleteCommand</tt> déclenche l'évènement <tt>deleteButtonClicked()</tt> que nous devons implémenter dans le fichier de classe. De plus, la grille de données doit être renseignée avec les informations utilisateurs lorsque la page est initialisée. Nous écrivons donc notre fichier de classe comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class AdminUser extends TPage
+{
+ /**
+ * Remplis la grille avec la liste des utilisateurs.
+ * Cette méthode est appelée lors de l'initialisation de la page.
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // lit tout les comptes utilisateurs
+ $this->UserGrid->DataSource=UserRecord::finder()->findAll();
+ // et les associes à la grille
+ $this->UserGrid->dataBind();
+ }
+
+ /**
+ * Supprime un compte utilisateur.
+ * Cette méthode répond à l'évènement OnDeleteCommand.
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function deleteButtonClicked($sender,$param)
+ {
+ // récupère l'identifiant du bouton sur lequel on a cliqué
+ $item=$param->Item;
+ // récupère auprès de la grille la clé primaire correspondante à l'identifiant
+ $username=$this->UserGrid->DataKeys[$item->ItemIndex];
+ // supprime le compte utilisateur en utilisant la clé primaire
+ UserRecord::finder()->deleteByPk($username);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dans le code précédent, la méthode <tt>deleteButtonClicked()</tt> est appelée quand on clique sur le bouton "Supprimer". Pour savoir à quelle ligne appartenait le bouton, nous utilisons la propriété <tt>Item.ItemIndex</tt> du paramètre de l'évènement. Avec cet index, nous recherchons quelle est la clé primaire de la ligne grâce à la propriété <tt>DataKeys</tt>.
+Dans le code précédent, la méthode <tt>deleteButtonClicked()</tt> est appelée quand on clique sur le bouton "Supprimer". Pour savoir à quelle ligne appartenait le bouton, nous utilisons la propriété <tt>Item.ItemIndex</tt> du paramètre de l'évènement. Avec cet index, nous recherchons quelle est la clé primaire de la ligne grâce à la propriété <tt>DataKeys</tt>.
+</p>
+
+<com:TipBox>
+Tous les <a href="http://www.pradosoft.com/docs/classdoc/TDataBoundControl">contrôles liés</a> sont basé sur le même modèle. C'est à dire, définition de la propriété <tt>DataSource</tt> pour savoir d'où proviennent les données et appel à la méthode <tt>dataBind()</tt> pour effectivement lier les données au contrôle.
+</com:TipBox>
+
+<h2>Ajout de la vérification des droits d'accès</h2>
+<p>
+Vu que seuls les administrateurs doivent pouvoir accéder à la page <tt>AdminUser</tt>, nous devons modifier notre fichier de configuration de page <tt>protected/pages/users/config.xml</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <allow pages="NewUser,AdminUser" roles="admin" />
+ <deny users="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<h2>Test</h2>
+<!-- <h2>Testing</h2> -->
+<p>
+Pour tester notre page <tt>AdminUser</tt>, nous naviguons à l'adresse <tt>http://hostname/blog/index.php?page=users.AdminUser</tt>. Il peut vous être demandé de vous connecter en tant qu'administrateur auparavant si ce n'est déjà fait. Le résultat suivant apparaitra.
+</p>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/CreateEditUser.page b/demos/blog-tutorial/protected/pages/Day3/fr/CreateEditUser.page
new file mode 100755
index 00000000..f83a30f5
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/CreateEditUser.page
@@ -0,0 +1,199 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page de mise à jour des utilisateurs <tt>EditUser</tt></h1>
+
+<p>
+La page <tt>EditUser</tt> ressemble beaucoup à la page <a href="?page=Day3.CreateNewUser">NewUser</a>. La différence principale est le fait que lorsque la page <tt>EditUser</tt> est requise, les champs sont renseignés avec les données de l'utilisateur en cours. Une autre différence est que la page <tt>EditUser</tt> est accessible à un utilisateur standard.
+</p>
+
+<p>
+Pour définir quels sont les comptes qui peuvent être modifiés, nous allons utiliser les règles suivantes :
+</p>
+<ul>
+<li>Si l'utilisateur actuel est un administrateur, il peut modifier n'importe quel compte utilisateur en spécifiant le pseudo de l'utilisateur dans l'URL sous la forme ?username='le nom'. Par exemple : <tt>http://hostname/blog/index.php?page=users.EditUser&username=demo</tt>.</li>
+<li>Si l'utilisateur actuel est un administrateur et qu'il n'a pas précisé d'username, ce sont les informations de l'administrateur qui seront mise à jour.</li>
+<li>Si l'utilisateur actuel est un utilisateur standard, seules les données de son compte seront accessibles et il ne pourra pas modifier son rôle.</li>
+</ul>
+
+<p>
+Nous créons deux fichiers <tt>protected/pages/users/EditUser.page</tt> et <tt>protected/pages/users/EditUser.php</tt> qui contiendront respectivement le gabarit et la classe.
+</p>
+
+<h2>Création du gabarit</h2>
+p>
+Comme vous avez pu le deviner, la page <tt>EditUser</tt> est fortement ressemblante à la page <tt>NewUser</tt>. En dehors du titre de la page et du bouton "envoyer", il y a trois différences principales.
+</p>
+
+<ul>
+<li>Le champ de saisie "username" est remplacé par un contrôle <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Label">TLabel</a> vu qu'il n'est pas autorisé de modifier son pseudo;</li>
+<li>Le validateur pour le champ "password" est supprimé. Si l'utilisateur ne fournit pas de mot de passe durant la modification, cela indique que l'utilisateur ne veut pas en changer.</li>
+<li>Le champ "role" est entouré d'un <tt>TControl</tt>, ce qui nous permet de la rendre visible ou invisible en fonction de rôle de l'utilisateur connecté. Si l'utilisateur n'est pas un administrateur, le champ "role" ne sera pas affiché. Les utilisateurs standard n'ont pas le droit de modifier leur rôle.</li>
+</ul>
+
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon blog - Mise à jour utilisateur" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Mise à jour utilisateur</h1>
+
+<span>Pseudo:</span>
+&lt;com:TLabel ID="Username" />
+
+<br/>
+<span>Mot de passe:</span>
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+<span>Confirmation mot de passe:</span>
+&lt;com:TCompareValidator
+ ControlToValidate="Password"
+ ControlToCompare="Password2"
+ ErrorMessage="Différence entre le mot de passe et la confirmation."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password2" TextMode="Password" />
+
+<br/>
+<span>Email:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Email"
+ ErrorMessage="Veuillez indiquer votre email."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator
+ ControlToValidate="Email"
+ ErrorMessage="Vous avez indiqué un mot de passe invalide."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+
+&lt;com:TControl Visible="&lt;%= $this->User->IsAdmin %>">
+<br/>
+<span>Role:</span>
+<br/>
+&lt;com:TDropDownList ID="Role">
+ &lt;com:TListItem Text="Utilisateur standard" Value="0" />
+ &lt;com:TListItem Text="Administrateur" Value="1" />
+&lt;/com:TDropDownList>
+&lt;/com:TControl>
+
+<br/>
+<span>Prénom:</span>
+<br/>
+&lt;com:TTextBox ID="FirstName" />
+
+<br/>
+<span>Nom:</span>
+<br/>
+&lt;com:TTextBox ID="LastName" />
+
+<br/>
+&lt;com:TButton Text="Enregistrer" OnClick="saveButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Création du fichier de classe</h2>
+
+
+<p>
+En suivant les indications du gabarit, nous devons écrire une page de classe qui initialise les champs avec les données de l'utilisateur. De plus, la classe doit implémenter la méthode <tt>saveButtonClicked()</tt> appelée par l'évènement <tt>OnClick</tt> du bouton "Enregistrer".
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class EditUser extends TPage
+{
+ /**
+ * Initialise les champs avec les données de l'utilisateur.
+ * Cette méthode est appelée par le framework lorsque la page est initialisée.
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(!$this->IsPostBack) // est-ce que c'est le premier appel à la page
+ {
+ // Lit les informations de l'utilisateur. C'est équivalent à :
+ // $userRecord=$this->getUserRecord();
+ $userRecord=$this->UserRecord;
+
+ // Rempli les contrôles avec les données de l'utilisateur
+ $this->Username->Text=$userRecord->username;
+ $this->Email->Text=$userRecord->email;
+ $this->Role->SelectedValue=$userRecord->role;
+ $this->FirstName->Text=$userRecord->first_name;
+ $this->LastName->Text=$userRecord->last_name;
+ }
+ }
+
+ /**
+ * Enregistre les modifications si tous les validateurs sont Ok.
+ * Cette méthode répond à l'évènement OnClick du bouton "Enregistrer".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function saveButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // toutes les validations Ok ?
+ {
+ // Lit les informations de l'utilisateur.
+ $userRecord=$this->UserRecord;
+
+ // Enresgistre les valeurs dans les champs de la BDD
+ $userRecord->username=$this->Username->Text;
+ // mets à jour le mot de passe s'il n'est pas vide
+ if(!empty($this->Password->Text))
+ $userRecord->password=$this->Password->Text;
+ $userRecord->email=$this->Email->Text;
+ // mets à jour le rôle si l'utilisateur actuel est un administrateur
+ if($this->User->IsAdmin)
+ $userRecord->role=(int)$this->Role->SelectedValue;
+ $userRecord->first_name=$this->FirstName->Text;
+ $userRecord->last_name=$this->LastName->Text;
+
+ // enregistre les modifications dans la BDD
+ $userRecord->save();
+
+ // redirige vers la page d'accueil
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+ }
+
+ /**
+ * Retourne l'utilisateur qui doit être mis à jour.
+ * @return UserRecord l'utilisateur qui doit être modifié.
+ * @throws THttpException si l'utilisateur n'existe pas.
+ */
+ protected function getUserRecord()
+ {
+ // l'utilisateur à modifié est l'utilisateur actuellement connecté
+ $username=$this->User->Name;
+ // si la variable GET 'username' n'est pas vide et que l'utilisateur actuel
+ // est un administrateur, nous utilisons la variable GET à la place
+ if($this->User->IsAdmin && $this->Request['username']!==null)
+ $username=$this->Request['username'];
+
+ // lit les données de l'utilisateur par Active Record
+ $userRecord=UserRecord::finder()->findByPk($username);
+ if(!($userRecord instanceof UserRecord))
+ throw new THttpException(500,'Username is invalid.');
+ return $userRecord;
+ }
+}
+</com:TTextHighlighter>
+
+<com:TipBox>
+La méthode <tt>onInit()</tt> est appelée par PRADO lors du <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Pages">cycle de vie de la page</a>. Les autres méthodes couramment surchargées sont <tt>onPreInit()</tt>, <tt>onLoad()</tt> et <tt>onPreRender()</tt>.
+</com:TipBox>
+
+<h2>Test</h2>
+
+<p>
+Pour tester la page <tt>EditUser</tt>, rendons-nous à l'URL <tt>http://hostname/blog/index.php?page=users.EditUser&username=demo</tt>. Il vous sera peut-être demandé de vous authentifier auparavant si vous n'êtes pas déjà connecté. Essayez de vous connecter avec différents comptes (ie: admin/demo, demo/demo) et remarquez comment la page évolue différemment.
+</p>
+
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/CreateLoginUser.page b/demos/blog-tutorial/protected/pages/Day3/fr/CreateLoginUser.page
new file mode 100755
index 00000000..07e97b87
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/CreateLoginUser.page
@@ -0,0 +1,161 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page <tt>LoginUser</tt></h1>
+
+<p>
+La page <tt>LoginUser</tt> affiche un formulaire et gère l'authentification de l'utilisateur. Comme décrit dans <a href="?page=Day3.Auth">authentification et autorisation</a>, le navigateur est automatiquement redirigé vers la page <tt>LoginUser</tt> quand un utilisateur essaye d'accéder à une page protégée, telle que la page d'administration des utilisateurs.
+</p>
+
+<p>
+Le processus de la page <tt>LoginUser</tt> est similaire à celui de la page <a href="?page=Day1.CreateContact">Contact</a>:
+</p>
+<ol>
+<li>Quand un utilisateur accède à la page <tt>LoginUser</tt>, un formulaire est affiché;</li>
+<li>L'utilisateur remplit les champs, nom de l'utilisateur et mot de passe et clique sur le bouton "envoyer";</li>
+<li>La classe <tt>LoginUser</tt> reçoit l'évènement "login" et lance la séquence d'authentification;</li>
+<li>Si le nom d'utilisateur et le mot de passe sont corrects, le système l'enregistre en session et le redirige vers la page protégée demandée. Dans le cas contraire, un message "mot de passe invalide" est affiché.
+</ol>
+
+<p>
+Nous créons les deux fichiers <tt>protected/pages/users/LoginUser.page</tt> et <tt>protected/pages/users/LoginUser.php</tt> qui enregistre le gabarit et la classe respectivement.
+</p>
+
+<h2>Création du gabarit</h2>
+
+<p>
+Ci-après est affiché le gabarit pour <tt>LoginUser</tt>. Comme vous pouvez le constater, la page contient un champ de saisie pour le nom de l'utilisateur et un autre pour le mot de passe. Le nom de l'utilisateur est requis, ce que le validateur <tt>TRequiredFieldValidator</tt> contrôle. La validité du mot de passe est assurée par le validateur <a href="http://www.pradosoft.com/demos/quickstart/index.php?page=Controls.Validation">TCustomValidator</a> qui fait un appel à la méthode <tt>validateUser()</tt> de la classe. La page contient aussi un bouton "envoyer" qui fait un appel à <tt>loginButtonClicked()</tt> quand il est activé.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="My Blog - Login" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Connexion</h1>
+
+<span>Votre nom:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Username"
+ ErrorMessage="Veuillez indiquer votre nom."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Username" />
+
+<br/>
+<span>Mot de passe:</span>
+&lt;com:TCustomValidator
+ ControlToValidate="Password"
+ ErrorMessage="vous avez saisi un mot de passe invalide."
+ Display="Dynamic"
+ OnServerValidate="validateUser" />
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+&lt;com:TButton Text="Envoyer" OnClick="loginButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<h2>Création de la classe</h2>
+
+<p>
+Tout comme la page <a href="?page=Day1.CreateContact">Contact</a>, la page <tt>LoginUser</tt> a aussi besoin d'un fichier de classe qui implémente les évènements générés dans le fichier gabarit. Ici, nous avons besoin de deux méthodes : <tt>validateUser()</tt> et <tt>loginButtonClicked()</tt>. Dans <tt>validateUser()</tt>, nous utilisons le <a href="?page=Day3.Auth">gestionnaire d'authentification</a> pour vérifier si le nom d'utilisateur et le mot de passe sont valides. Si c'est le cas, le gestionnaire d'authentification créé automatiquement une session utilisateur avec les données correspondantes.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class LoginUser extends TPage
+{
+ /**
+ * Vérifie la validité du nom d'utilisateur et du mot de passe.
+ * Cette méthode implémente l'évènement <tt>OnServerValidate</tt> du validateur <tt>TCustomValidator</tt>.
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function validateUser($sender,$param)
+ {
+ $authManager=$this->Application->getModule('auth');
+ if(!$authManager->login($this->Username->Text,$this->Password->Text))
+ $param->IsValid=false; // indique au validateur que la validation à échoué
+ }
+
+ /**
+ * Rédirige le navigateur vers l'URL originellement demandée si la validation est Ok.
+ * Cette méthode implémente l'évènement <tt>OnClick</tt> du bouton "envoyer".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function loginButtonClicked($sender,$param)
+ {
+ if($this->Page->IsValid) // toutes les validations sont ok ?
+ {
+ // récupère l'URL de la page protégée qui avait été demandée par l'utilisateur
+ $url=$this->Application->getModule('auth')->ReturnUrl;
+ if(empty($url)) // l'utilisateur à accéder à la page de connexion directement
+ $url=$this->Service->DefaultPageUrl;
+ $this->Response->redirect($url);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+
+<h2>Test</h2>
+
+<p>
+Nous avons donc créé la page <tt>LoginUser</tt>. Nous pouvons la tester en naviguant à l'URL <tt>http://hostname/blog/index.php?page=users.LoginUser</tt>. Rappellez-vous que la dans la section <a href="?page=Day2.CreateDB">Création de la base</a>, nous avons déjà créé deux comptes utilisateurs (nom d'utilisateur/mot de passe) <tt>admin/demo</tt> et <tt>demo/demo</tt>. Nous pouvons donc les utiliser pour tester notre page de connexion.
+</p>
+
+<img src="<%~ output.gif %>" class="output"/>
+
+<h2>Ajout des liens de connexion/déconnexion à notre gabarit principal</h2>
+<p>
+Pour permettre à l'utilisateur d'accéder directement aux pages de connexion/déconnexion, nous modifions le gabarit principal <tt>MainLayout</tt>. En particulier, nous ajoutons un lien vers la page <tt>LoginUser</tt>. Nous ajoutons aussi un lien "se déconnecter" qui permet à l'utilisateur de se déconnecter.
+</p>
+
+<p>
+Nous modifions le pied de page de notre gabarit principal <tt>MainLayout</tt>. La visibilité des liens vers "se connecter" et "se déconnecter" dépend du statut de l'utilisateur. Si l'utilisateur n'est pas encore connecté, ie: <tt>$this->User->IsGuest</tt> est vrai, alors le lien "se connecter" est visible tandis que le lien "se déconnecter" ne l'est pas et inversement s'il est connecté.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div id="footer">
+&lt;com:THyperLink Text="Se connecter"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('users.LoginUser') %>"
+ Visible="&lt;%= $this->User->IsGuest %>" />
+
+&lt;com:TLinkButton Text="Se déconnecter"
+ OnClick="logoutButtonClicked"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+
+<br/>
+&lt;%= PRADO::poweredByPrado() %>
+</div>
+</com:TTextHighlighter>
+
+<p>
+Vu que le lien "se déconnecter" génère l'évènement <tt>OnClick</tt> avec comme nom d'évènement <tt>logoutButtonClicked()</tt>, nous devons modifier le fichier de classe de <tt>MainLayout</tt> comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class MainLayout extends TTemplateControl
+{
+ /**
+ * Déconnecte un utilisateur.
+ * Cette méthode répond à l'évènement OnClick du lien "se déconnecter".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function logoutButtonClicked($sender,$param)
+ {
+ $this->Application->getModule('auth')->logout();
+ $url=$this->Service->constructUrl($this->Service->DefaultPage);
+ $this->Response->redirect($url);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Maintenant si nous visitons n'importe quelle page de notre blog, nous verrons apparaitre un lien en pied de page. Le lien affiche "se connecter" si nous ne sommes pas connectés et "se déconnecter" dans le cas contraire. Si nous cliquons sur le lien "se déconnecter", nous sommes redirigés vers la page d'accueil et le lien "se connecter" apparait indiquant que nous ne sommes plus connectés.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/CreateNewUser.page b/demos/blog-tutorial/protected/pages/Day3/fr/CreateNewUser.page
new file mode 100755
index 00000000..144a686a
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/CreateNewUser.page
@@ -0,0 +1,212 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page nouvel utilisateur <tt>NewUser</tt></h1>
+
+<p>
+La page <tt>NewUser</tt> est fournie à l'administrateur pour créer des nouveaux comptes utilisateurs. Elle doit afficher un formulaire qui permet la saisie des informations d'un nouveau compte. Tel que défini dans la <a href="?page=Day2.CreateDB">base de données</a>, nous devons prévoir la saisie des informations suivantes :
+</p>
+
+<ul>
+<li><tt>username</tt> - string, pseudo de l'utilisateur, obligatoire et unique</li>
+<li><tt>email</tt> - string, email, obligatoire et unique</li>
+<li><tt>password</tt> - string, mot de passe, obligatoire</li>
+<li><tt>role</tt> - integer, rôle, obligatoire (0 ou 1)</li>
+<li><tt>first_name</tt> - string, prénom, optionnel</li>
+<li><tt>last_name</tt> - string, nom, optionnel</li>
+</ul>
+
+<p>
+Nous créons deux fichiers, <tt>protected/pages/users/NewUser.page</tt> et <tt>protected/pages/users/NewUser.php</tt> qui contiendront respectivement le gabarit et la classe.
+</p>
+
+<h2>Création du gabarit</h2>
+<p>
+En fonction de l'analyse précédente, nous créons le gabarit comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon Blog - Nouvel utilisateur" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Création nouvel utilisateur</h1>
+
+<span>Pseudo:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Username"
+ ErrorMessage="Veuillez indiquer un pseudo."
+ Display="Dynamic" />
+&lt;com:TCustomValidator
+ ControlToValidate="Username"
+ ErrorMessage="Désolé, le pseudo choisi est déjà utilisé. Veuillez en saisir un autre."
+ OnServerValidate="checkUsername"
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Username" />
+
+<br/>
+<span>Mot de passe:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Password"
+ ErrorMessage="Veuillez indiquer un mot de passe."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password" TextMode="Password" />
+
+<br/>
+<span>Confirmation mot de passe:</span>
+&lt;com:TCompareValidator
+ ControlToValidate="Password"
+ ControlToCompare="Password2"
+ ErrorMessage="Différence entre le mot de passe et la confirmation."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Password2" TextMode="Password" />
+
+<br/>
+<span>Email:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="Email"
+ ErrorMessage="Veuillez indiquer votre email."
+ Display="Dynamic" />
+&lt;com:TEmailAddressValidator
+ ControlToValidate="Email"
+ ErrorMessage="Vous avez indiqué un mot de passe invalide."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="Email" />
+
+<br/>
+<span>Rôle:</span>
+<br/>
+&lt;com:TDropDownList ID="Role">
+ &lt;com:TListItem Text="Utilisateur standard" Value="0" />
+ &lt;com:TListItem Text="Administrateur" Value="1" />
+&lt;/com:TDropDownList>
+
+<br/>
+<span>Prénom:</span>
+<br/>
+&lt;com:TTextBox ID="FirstName" />
+
+<br/>
+<span>Nom:</span>
+<br/>
+&lt;com:TTextBox ID="LastName" />
+
+<br/>
+&lt;com:TButton Text="Ajouter" OnClick="createButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Le gabarit est très proche du gabarit de la page <tt>Contact</tt> et de la page <tt>LoginUser</tt>. Il consiste principalement en deux champs de saisie et de plusieurs validateurs. Certains champs de saisie sont associés à plusieurs validateurs vu qu'il est nécessaire de vérifier plusieurs règles.
+</p>
+
+
+<h2>Création du fichier de classe</h2>
+
+<p>
+En fonction du gabarit précédent, nous constatons que nous avons besoin d'une classe qui implémente deux gestionnaires d'évènements : <tt>checkUsername()</tt> (appellé par le premier validateur dans l'évènement <tt>OnServerValidate</tt>) et <tt>createButtonClicked()</tt> (appellé par l'évènement <tt>OnClick</tt> du bouton "create"). ainsi, nous écrirons la classe comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class NewUser extends TPage
+{
+ /**
+ * Vérifie si le nom d'utilisateur existe dans la base de données.
+ * Cette méthode répond à l'évènement OnServerValidate du validateur username.
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function checkUsername($sender,$param)
+ {
+ // valide si l'utilisateur existe
+ $param->IsValid=UserRecord::finder()->findByPk($this->Username->Text)===null;
+ }
+
+ /**
+ * Créer un nouveau compte utilisateur si tous les champs sont valides.
+ * Cette méthode répond à l'évènement OnClick du bouton "create".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function createButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // si toutes les validations sont ok
+ {
+ // rempli l'objet UserRecord avec les données saisies
+ $userRecord=new UserRecord;
+ $userRecord->username=$this->Username->Text;
+ $userRecord->password=$this->Password->Text;
+ $userRecord->email=$this->Email->Text;
+ $userRecord->role=(int)$this->Role->SelectedValue;
+ $userRecord->first_name=$this->FirstName->Text;
+ $userRecord->last_name=$this->LastName->Text;
+
+ // l'enregistre dans la base de données par la méthode save de l'Active Record
+ $userRecord->save();
+
+ // redirige l'utilisateur vers la page d'accueil
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dans le code précédent, l'appel à la méthode <tt>save()</tt> insère un enregistrement dans la table <tt>users</tt>. Cette fonctionnalité est fournie par l'objet <a href="http://www.pradosoft.com/demos/quickstart/?page=Database.ActiveRecord">Active Record</a>.
+</p>
+
+<com:NoteBox>
+Par simplification, le pseudo dans notre blog est sensible à la casse. Dans beaucoup de systèmes, le pseudo est insensible à la casse. Il faudrait donc prévoir un traitement particulier lors de la création d'un nouvel utilisateur, ainsi que dans la partie <a href="?page=Day3.Auth">authentification</a>. De même, les espaces en début et fin de pseudo devrait être supprimés.
+Par simplification, le pseudo dans notre blog est sensible à la casse. Dans beaucoup de systèmes, le pseudo est insensible à la casse. Il faudrait donc prévoir un traitement particulier lors de la création d'un nouvel utilisateur, ainsi que dans la partie <a href="?page=Day3.Auth">authentification</a>. De même les espaces en début et fin de pseudo devrait être supprimés.
+Par simplification, le pseudo dans notre blog est sensible à la casse. Dans beaucoup de systèmes, le pseudo est insensible à la casse. Il faudrait donc prévoir un traitement particulier lors de la création d'un nouvel utilisateur, ainsi que dans la partie <a href="?page=Day3.Auth">authentification</a>. De même les espaces en début et fin de pseudo devrait être supprimés.
+</com:NoteBox>
+
+
+<h2>Test</h2>
+<p>
+Pour tester la page <tt>NewUser</tt>, il suffit de naviguer à l'URL <tt>http://hostname/blog/index.php?page=users.NewUser</tt>. vous devriez voir apparaitre la page suivante. Essayez de saisir différentes informations et remarquez comment les données sont validées. Si toutes les règles sont valides, nous devrions avoir inséré un nouvel utilisateur et être redirigés vers la page d'accueil.
+</p>
+
+<img src="<%~ output2.gif %>" class="output"/>
+
+
+<h2>Ajout de la vérification des droits d'accès</h2>
+<p>
+Durant le test, vous vous êtes peut-être demandé : Est-ce que la page <tt>NewUser</tt> ne devrait être accessible qu'aux administrateurs ? Oui, ceci est dénommé <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Auth">autorisation</a>. Nous allons maintenant décrire comment ajouter cette vérification d'accès à la page <tt>NewUser</tt>.
+</p>
+
+<p>
+Une façon simple serait de vérifier dans le code de la classe si <tt>$this->User->IsAdmin</tt> est vrai, dans le cas contraire, une redirection vers la page de connexion <tt>LoginUser</tt> serait faite.
+</p>
+
+<p>
+PRADO propose une approche complémentaire de vérification des droits. Pour ce faire, nous devons utiliser un fichier de <a href="http://www.pradosoft.com/demos/quickstart/?page=Configurations.PageConfig">configuration de page</a>. Créer un fichier <tt>protected/pages/users/config.xml</tt> avec le contenu suivant :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <allow pages="NewUser" roles="admin" />
+ <deny users="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<p>
+Le fichier de configuration de page contient les règles d'accès aux pages contenues dans le dossier <tt>protected/pages/users</tt>. Ce fichier indique que la page <tt>NewUser</tt> peut être vue par les utilisateurs dont le rôle est "admin" (concernant le mot "admin" voir <a href="?page=Day3.Auth">BlogUser.createUser()</a>), et toutes les autres pages de ce dossier interdites aux utilisateurs anonymes (<tt>users="?"</tt> signifie utilisateur anonyme).
+</p>
+
+<p>
+Dorénavant, si nous naviguons à la page <tt>NewUser</tt> en tant qu'anonyme, nous serons redirigés vers la page <tt>LoginUser</tt>. Si notre connexion est acceptée, nous serons redirigés en retour vers la page <tt>NewUser</tt>
+</p>
+
+<com:TipBox>
+Le fichier de configuration de pages peut contenir d'autres éléments que les règles d'autorisations. Par exemple, il pourrait inclure un <a href="http://www.pradosoft.com/demos/quickstart/?page=Fundamentals.Modules">module</a> tout comme nous l'avons fait pour le fichier de <a href="?page=Day2.ConnectDB">configuration de l'application</a>. Dans une application PRADO, chaque dossier de pages peut contenir un fichier de configuration de pages qui s'applique à tous les fichiers du dossier ainsi qu'aux sous dossiers.
+</com:TipBox>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/Overview.page b/demos/blog-tutorial/protected/pages/Day3/fr/Overview.page
new file mode 100755
index 00000000..306434e1
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/Overview.page
@@ -0,0 +1,42 @@
+<com:TContent ID="Main">
+
+<h1>Vue d'ensemble de la gestion des utilisateurs</h1>
+<!-- <h1>User Management Overview</h1> -->
+
+<p>
+Dans cette section, nous allons créer les pages concernant la gestion des utilisateurs. En particulier, nous allons implémenter les fonctionnalités suivantes: connexion/déconnexion des utilisateurs, création d'un utilisateur, mise à jour/suppression des utilisateurs.
+</p>
+<!-- <p>
+In this section, we create pages that are related with user management. In particular, we implement these required features: user login/logout, creating new user account and updating/deleting user accounts.
+</p> -->
+
+<p>
+En accord avec les buts à atteindre, nous devons créer les pages suivantes. Pour une meilleure organisation de notre code, ces pages seront créées dans le dossier <tt>protected/pages/users</tt>.
+</p>
+<!-- <p>
+According to the requirements, we need to create the following pages. To better organize our code, these user-related pages will be created under a new directory <tt>protected/pages/users</tt>.
+</p> -->
+
+<ul>
+ <li><tt>LoginUser</tt> affiche le formulaire de connexion.</li>
+ <li><tt>NewUser</tt> pour la création d'un compte utilisateur.</li>
+ <li><tt>EditUser</tt> permet à un utilisateur enregistré de mettre à jour son profil.</li>
+ <li><tt>AdminUser</tt> permet à l'administrateur de gérer les comptes utilisateurs, y compris la gestion des droits d'accès et la suppression d'un compte.</li>
+</ul>
+<!-- <ul>
+ <li><tt>LoginUser</tt> displays a login form to login a user.</li>
+ <li><tt>NewUser</tt> creates a new user account.</li>
+ <li><tt>EditUser</tt> allows a registered user to update his profile.</li>
+ <li><tt>AdminUser</tt> allows the administrator to manage the user accounts, including setting permission level and deleting a user account.</li>
+</ul> -->
+
+<p>
+Après avoir fini cette section, nous devrions obtenir l'arborescence suivante :
+</p>
+<!-- <p>
+After finishing this section, we shall expect to see the following directories and files:
+</p> -->
+
+<img src="<%~ directories.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/directories.gif b/demos/blog-tutorial/protected/pages/Day3/fr/directories.gif
new file mode 100755
index 00000000..f59fda58
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/output.gif b/demos/blog-tutorial/protected/pages/Day3/fr/output.gif
new file mode 100755
index 00000000..0d812dd0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/output2.gif b/demos/blog-tutorial/protected/pages/Day3/fr/output2.gif
new file mode 100755
index 00000000..749255d6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day3/fr/output3.gif b/demos/blog-tutorial/protected/pages/Day3/fr/output3.gif
new file mode 100755
index 00000000..a11ee653
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day3/fr/output3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/CreateEditPost.page b/demos/blog-tutorial/protected/pages/Day4/fr/CreateEditPost.page
new file mode 100755
index 00000000..09220322
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/CreateEditPost.page
@@ -0,0 +1,136 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page modification d'un message <tt>EditPost</tt></h1>
+
+
+<p>
+La page <tt>EditPost</tt> est fournie aux auteurs et administrateurs pour modifier les messages. Comme la page <a href="?page=Day4.CreateNewPost">NewPost</a>, elle affiche un formulaire permettant de modifier les données d'un message.
+</p>
+
+
+<p>
+Nous créons deux fichiers <tt>protected/pages/posts/EditPost.page</tt> et <tt>protected/pages/posts/EditPost.php</tt> contenant respectivement le gabarit et la classe de notre page.
+</p>
+
+<h2>Création du gabarit</h2>
+<p>
+Le gabarit de la page <tt>EditPost</tt> est très proche de celui de la page <tt>NewPost</tt> template. Seul le titre et le texte du bouton sont différents.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon Blog - Modification Message" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Modification message</h1>
+
+<span>Titre:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="TitleEdit"
+ ErrorMessage="Veuillez indiquer un titre."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="TitleEdit" Columns="50" />
+
+<br/>
+<span>Message:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="ContentEdit"
+ ErrorMessage="Veuillez indiquer le contenu du message."
+ Display="Dynamic" />
+<br/>
+&lt;com:THtmlArea ID="ContentEdit" />
+
+<br/>
+&lt;com:TButton Text="Enregistrer" OnClick="saveButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Création du fichier de classe</h2>
+
+
+<p>
+La classe de <tt>EditPage</tt> est un peu plus complexe que celle de la page <tt>NewPage</tt> parce qu'elle doit lire les informations auparavant. Elle doit aussi vérifier les autorisations. En particulier, elle doit s'assurer que le message ne puisse être modifié que par l'auteur ou par un administrateur. Ces vérifications d'autorisation ne sont pas fournies par PRADO.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class EditPost extends TPage
+{
+ /**
+ * initialise les contrôles de saisies avec les données du message.
+ * cette méthode est appelée lors de l'initialisation de la page
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // récupère les données de l'utilisateur. Equivalent à:
+ // $postRecord=$this->getPost();
+ $postRecord=$this->Post;
+ // vérification des droits: seul l'auteur ou un administrateur peuvent modifier le message
+ if($postRecord->author_id!==$this->User->Name && !$this->User->IsAdmin)
+ throw new THttpException(500,'Vous n êtes pas autoriser à modifier ce message.');
+
+ if(!$this->IsPostBack) // est-ce le premier appel à la page
+ {
+ // rempli les contrôles avec les données du message
+ $this->TitleEdit->Text=$postRecord->title;
+ $this->ContentEdit->Text=$postRecord->content;
+ }
+ }
+
+ /**
+ * Enregistre si toutes les validations sont Ok
+ * cette méthode répond à l'évènement OnClick du bouton "Enregistrer".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function saveButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // toutes les validations sont ok ?
+ {
+ // récupère les données de l'utilisateur. Equivalent à:
+ // $postRecord=$this->getPost();
+ $postRecord=$this->Post;
+
+ // affecte les données saisies aux champs de la BDD
+ $postRecord->title=$this->TitleEdit->SafeText;
+ $postRecord->content=$this->ContentEdit->SafeText;
+
+ // enregistre les données par la méthode save de l'Active Record
+ $postRecord->save();
+
+ // redirige le navigateur vers la page ReadPost
+ $url=$this->Service->constructUrl('posts.ReadPost',array('id'=>$postRecord->post_id));
+ $this->Response->redirect($url);
+ }
+ }
+
+ /**
+ * retourne les données du message devant être modifiées.
+ * @return PostRecord les données devant être modifiés.
+ * @throws THttpException si le message est inexistant.
+ */
+ protected function getPost()
+ {
+ // l'ID du message devant être modifié passé par un paramètre GET
+ $postID=(int)$this->Request['id'];
+ // utilise Active Record pour lire le message correspondant à cet ID
+ $postRecord=PostRecord::finder()->findByPk($postID);
+ if($postRecord===null)
+ throw new THttpException(500,'Message inexistant.');
+ return $postRecord;
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Test</h2>
+<p>
+Pour tester notre page <tt>EditPost</tt>, authentifiez-vous auparavant et allez à l'URL <tt>http://hostname/blog/index.php?page=EditPost&id=1</tt>. Cette URL peut aussi être atteinte par le bouton <tt>Modifier</tt> de notre page de détail.
+</p>
+
+<img src="<%~ output4.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/CreateListPost.page b/demos/blog-tutorial/protected/pages/Day4/fr/CreateListPost.page
new file mode 100755
index 00000000..ab6ddfc4
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/CreateListPost.page
@@ -0,0 +1,182 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page d'affichage des messages <tt>ListPost</tt></h1>
+
+<p>
+La page <tt>ListPost</tt> affiche les derniers messages sous forme de liste. S'il y a trop de messages, ils seront affichés dans différentes pages.
+</p>
+
+<p>
+Avant que nous ne passions à l'implémentation, nous voudrions que notre page d'accueil pointe vers la page à venir <tt>ListPost</tt>, ceci dans le but d'afficher la liste des derniers messages dès qu'un utilisateur se connecte au site. Pour cela, nous allons modifier le fichier de configuration de l'application <tt>protected/application.xml</tt> de cette manière.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<services>
+ <service id="page" class="TPageService" DefaultPage="posts.ListPost">
+ <pages MasterClass="Application.layouts.MainLayout" />
+ </service>
+</services>
+</com:TTextHighlighter>
+
+<p>
+Nous alons maintenant créer le gabarit et le fichier de classe pour notre page <tt>ListPost</tt> : <tt>protected/pages/posts/ListPost.page</tt> et <tt>protected/pages/posts/ListPost.php</tt>.
+</p>
+
+<h2>Création du gabarit</h2>
+<p>
+Pour satisfaire les fonctionnalités de notre page <tt>ListPost</tt>, nous allons utiliser deux contrôles dans notre gabarit.
+</p>
+<ul>
+<li><a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Repeater">TRepeater</a>: ce contrôle permet d'afficher principalement une liste d'éléments. La présentation de chacun ce ces éléments peut être défini soit par un gabarit interne, soit par un gabarit externe (choix que nous avons fait).</li>
+<li><a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Pager">TPager</a>: ce contrôle permet de faire la pagination d'une liste d'éléments. Il interagit avec l'utilisateur pour définir quelle page doit être affiché dans un <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.List">contrôle de liste</a> (ie: <tt>TListBox</tt>) ou dans un <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.Data">contrôle de données</a> (ie: <tt>TRepeater</tt>).</li>
+</ul>
+<p>
+Ci-dessous le contenu du gabarit :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon Blog" %>
+
+&lt;com:TContent ID="Main">
+
+&lt;com:TRepeater ID="Repeater"
+ ItemRenderer="Application.pages.posts.PostRenderer"
+ AllowPaging="true"
+ AllowCustomPaging="true"
+ PageSize="5"
+ />
+
+&lt;com:TPager ControlToPaginate="Repeater" OnPageIndexChanged="pageChanged" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Dans la partie répétée <tt>TRepeater</tt>, nous indiquons que l'affichage du contenu est délégué à l'élément <tt>PostRenderer</tt> que nous allons créer après. Pour permettre à PRADO de trouver cette classe, nous fournissons l'espace de noms complet <tt>Application.pages.posts.PostRenderer</tt>, qui correspond au fichier <tt>protected/pages/posts/PostRenderer.php</tt>.
+</p>
+
+<p>
+Nous définissons aussi quelques propriétés complémentaires du <tt>TRepeater</tt> pour activer la pagination. Et nous définissons la propriété <tt>ControlToPaginate</tt> du <tt>TPager</tt> afin qu'il sache quelle est la zone répetée à paginer.
+</p>
+
+
+<h2>Création du fichier de classe</h2>
+<p>
+En fonction du gabarit précédent, nous pouvons voir que notre fichier de classe doit implémenter un gestionnaire d'évènement pour <tt>pageChanged()</tt> (déclenché par <tt>OnPageIndexChanged</tt> du <tt>TPager</tt>). Nous devons aussi remplir les données qui apparaitront dans le <tt>TRepeater</tt>. Ci-dessous le source complet du fichier de classe :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class ListPost extends TPage
+{
+ /**
+ * Initialise le TRepeater.
+ * Cette méthode est appelé par le framework lors de l'initialisation de la page
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ if(!$this->IsPostBack) // la page est chargée pour la première fois ?
+ {
+ // récupère le nombre total de messages
+ $this->Repeater->VirtualItemCount=PostRecord::finder()->count();
+ // rempli le TRepeater avec les données
+ $this->populateData();
+ }
+ }
+
+ /**
+ * Gestionnaire d'évènement pour OnPageIndexChanged du TPager.
+ * Cette méthode est appelée lors du changement de page
+ */
+ public function pageChanged($sender,$param)
+ {
+ // change l'index de la page courante par le nouvel index
+ $this->Repeater->CurrentPageIndex=$param->NewPageIndex;
+ // rempli de nouveau le TRepeater
+ $this->populateData();
+ }
+
+ /**
+ * détermine quelle page doit être affichée et remplie
+ * TRepeater avec les données lues
+ */
+ protected function populateData()
+ {
+ $offset=$this->Repeater->CurrentPageIndex*$this->Repeater->PageSize;
+ $limit=$this->Repeater->PageSize;
+ if($offset+$limit>$this->Repeater->VirtualItemCount)
+ $limit=$this->Repeater->VirtualItemCount-$offset;
+ $this->Repeater->DataSource=$this->getPosts($offset,$limit);
+ $this->Repeater->dataBind();
+ }
+
+ /**
+ * lis les données à partir de la base de données en utilisant les fonctionnalités offset et limit.
+ */
+ protected function getPosts($offset, $limit)
+ {
+ // construit les critères de la requête
+ $criteria=new TActiveRecordCriteria;
+ $criteria->OrdersBy['create_time']='desc';
+ $criteria->Limit=$limit;
+ $criteria->Offset=$offset;
+ // lit les messages en fonction des critères précédents
+ return PostRecord::finder()->withAuthor()->findAll($criteria);
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Création du <tt>PostRenderer</tt></h2>
+
+<p>
+Nous devons toujours créer la classe <tt>PostRenderer</tt>. Elle définit la manière dont sera affichée chaque ligne de notre <tt>TRepeater</tt>. Nous la créons en tant que gabarit de contrôle, ce qui nous permet d'utiliser notre système de gabarit. Le fichier de gabarit ainsi que notre fichier de classe seront sauvegardés respectivement sous <tt>PostRenderer.tpl</tt> et <tt>PostRenderer.php</tt> dans le dossier <tt>protected/pages/posts</tt>.
+</p>
+<h3>Création du gabarit pour <tt>PostRenderer</tt></h3>
+<p>
+Le gabarit définit la présentation des différentes informations d'un message : titre, nom, heure, contenu. Nous lions le titre à la page <tt>ReadPost</tt> qui affiche le détail du message.
+</p>
+<p>
+L'expression <tt>$this->Data</tt> fait référence aux données provenant du <tt>TRepeater</tt>. Dans notre cas, c'est un objet de type <tt>PostRecord</tt>. Remarquez comment nous accédons au nom de l'auteur du message par <tt>$this->Data->author->username</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div class="post-box">
+<h3>
+&lt;com:THyperLink Text="&lt;%# $this->Data->title %>"
+ NavigateUrl="&lt;%# $this->Service->constructUrl('posts.ReadPost',array('id'=>$this->Data->post_id)) %>" />
+</h3>
+
+<p>
+Auteur:
+&lt;com:TLiteral Text="&lt;%# $this->Data->author->username %>" /><br/>
+Heure:
+&lt;com:TLiteral Text="&lt;%# date('m/d/Y h:m:sa', $this->Data->create_time) %>" />
+</p>
+
+<p>
+&lt;com:TLiteral Text="&lt;%# $this->Data->content %>" />
+</p>
+</div>
+</com:TTextHighlighter>
+
+<h3>Création du fichier de classe pour <tt>PostRenderer</tt></h3>
+
+<p>
+Notre classe est très simple, elle hérite de <tt>TRepeaterItemRenderer</tt> et ne contient aucun autre code.
+</p>
+<com:TTextHighlighter CssClass="source" Language="php">
+class PostRenderer extends TRepeaterItemRenderer
+{
+}
+</com:TTextHighlighter>
+
+<h2>Test</h2>
+<p>
+Pour tester la page <tt>ListPost</tt>, naviguons à l'URL <tt>http://hostname/blog/index.php</tt> (rappellez-vous, nous avons défini <tt>ListPost</tt> comme étant notre page d'accueil). Nous devrions obtenir le résultat suivant. vu que nous n'avons qu'un seul message pour le moment, le contrôle de pagination n'apparait pas. Plus tard, quand nous aurons fini la page <tt>NewPost</tt>, nous pourrons ajouter des messages et revenir ici pour tester notre contrôle de pagination.
+</p>
+
+<img src="<%~ output.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/CreateNewPost.page b/demos/blog-tutorial/protected/pages/Day4/fr/CreateNewPost.page
new file mode 100755
index 00000000..45bc4394
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/CreateNewPost.page
@@ -0,0 +1,139 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page nouveau message <tt>NewPost</tt></h1>
+
+<p>
+La page <tt>NewPost</tt> permet aux utilisateurs authentifiés de créer des nouveaux messages. Elle doit afficher un formulaire permettant la saisie des informations du message.
+</p>
+
+
+<p>
+Parce que la page <tt>NewPost</tt> ne peut être vu que par les utilisateurs authentifiés, nous ajoutons un fichier de configuration de page <tt>config.xml</tt> dans le dossier <tt>protected/pages/posts</tt>. Cette configuration indique que les invités ne peuvent voir les pages <tt>NewPost</tt> et <tt>EditPost</tt> qui sera implémentée dans la section suivante.
+</p>
+
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+ <authorization>
+ <deny pages="NewPost,EditPost" roles="?" />
+ </authorization>
+</configuration>
+</com:TTextHighlighter>
+
+<p>
+Vu le nombre grandissant de pages, nous allons modifier le pied de page de notre <tt>gabarit principal</tt> pour qu'il inclus des liens vers : la page d'accueil, la page nouvel utilisateur <a href="?page=Day3.CreateNewUser">NewUser</a> (visible seulement par les administrateurs), et la page à venir : nouveau message <tt>NewPost</tt> (visible seulement par les utilisateurs authentifiés).
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+<div id="footer">
+&lt;com:THyperLink Text="Accueil"
+ NavigateUrl="&lt;%= $this->Service->DefaultPageUrl %>" />
+
+&lt;com:THyperLink Text="Nouveau message"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('posts.NewPost') %>"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+
+&lt;com:THyperLink Text="Nouvel utilisateur"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('users.NewUser') %>"
+ Visible="&lt;%= $this->User->IsAdmin %>" />
+...autres liens...
+</div>
+</com:TTextHighlighter>
+
+<p>
+Nous allons maintenant créer deux fichiers <tt>protected/pages/posts/NewPost.page</tt> et <tt>protected/pages/posts/NewPost.php</tt> contenant respectivement le gabarit et la classe de notre page.
+</p>
+
+
+<h2>Création du gabarit</h2>
+<p>
+Le gabarit de <tt>NewPost</tt> contient une référence à un <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.TextBox">TTextBox</a> pour saisir le titre de notre message et à un <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.HtmlArea">THtmlArea</a> pour saisir le contenu. Ce dernier est un éditeur WYSIWYG HTML. Pour contrôler les valeurs saisies, nous associons des validateurs aux contrôles précédents.
+</p>
+
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;%@ Title="Mon Blog - Nouveau Message" %>
+
+&lt;com:TContent ID="Main">
+
+<h1>Création nouveau message</h1>
+
+<span>Titre:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="TitleEdit"
+ ErrorMessage="Veuillez indiquer un titre."
+ Display="Dynamic" />
+<br/>
+&lt;com:TTextBox ID="TitleEdit" Columns="50" />
+
+<br/>
+<span>Message:</span>
+&lt;com:TRequiredFieldValidator
+ ControlToValidate="ContentEdit"
+ ErrorMessage="Veuillez indiquer le contenu du message."
+ Display="Dynamic" />
+<br/>
+&lt;com:THtmlArea ID="ContentEdit" />
+
+<br/>
+&lt;com:TButton Text="Ajouter" OnClick="createButtonClicked" />
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+
+<h2>Création du fichier de classe</h2>
+<p>
+Dans le gabarit précédent, nous voyons que la fonction principale de notre page est l'appel de la méthode <tt>createButtonClicked()</tt> implémenté par un évènement <tt>OnClick</tt> attaché au bouton <tt>Ajouter</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class NewPost extends TPage
+{
+ /**
+ * création d'un nouveau message si toutes les données sont valides.
+ * cette méthode est appelée par l'évènement OnClick du bouton "Ajouter".
+ * @param mixed sender : celui qui a généré l'évènement
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function createButtonClicked($sender,$param)
+ {
+ if($this->IsValid) // tous les validateurs sont Ok ?
+ {
+ // créer un nouvel objet PostRecord avec les données du formulaire
+ $postRecord=new PostRecord;
+ // utiliser SafeText à la place de Text évite les attaques XSS
+ $postRecord->title=$this->TitleEdit->SafeText;
+ $postRecord->content=$this->ContentEdit->SafeText;
+ $postRecord->author_id=$this->User->Name;
+ $postRecord->create_time=time();
+ $postRecord->status=0;
+
+ // enregistre les données dans la BDD par la méthode save de l'Active Record
+ $postRecord->save();
+
+ // redirige le navigateur vers le message nouvellement créé
+ $url=$this->Service->constructUrl('posts.ReadPost',array('id'=>$postRecord->post_id));
+ $this->Response->redirect($url);
+ }
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Test</h2>
+<p>
+Pour tester notre page <tt>NewPost</tt>, identifiez-vous auparavant et cliquez sur le lien <tt>Nouveau message</tt> dans le pied de page. Le navigateur affiche le résultat suivant avec comme URL <tt>http://hostname/blog/index.php?page=NewPost</tt>.
+</p>
+
+<com:InfoBox>
+Quand vous visitez la page <tt>NewPost</tt> pour la première fois, vous pourrez remarquer qu'elle mettra plusieurs secondes avant de s'afficher. Ceci est dû au fait que PRADO a besoin de décompresser et de publier le code javascript et les images pour l'éditeur WYSIWYG du contrôle <tt>THtmlArea</tt>. Ceci est fait une fois pour toutes.
+</com:InfoBox>
+
+<com:TipBox>
+Pour tester la fonctionnalité de pagination que nous avons mise en place dans la page <a href="?page=Day4.CreateListPost">ListPost</a>, nous pouvons créer cinq messages ou plus et regardez ce qu'il se passe sur la page d'accueil. Le contrôle <tt>TPager</tt> de la page <tt>ListPost</tt> affiche cinq éléments par page.
+</com:TipBox>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/CreateReadPost.page b/demos/blog-tutorial/protected/pages/Day4/fr/CreateReadPost.page
new file mode 100755
index 00000000..7877a800
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/CreateReadPost.page
@@ -0,0 +1,144 @@
+<com:TContent ID="Main">
+
+<h1>Création de la page détail d'un message <tt>ReadPost</tt></h1>
+
+<p>
+La page <tt>ReadPost</tt> affiche le détail d'un message. Pour les utilisateurs autorisés, un lien sera disponible permettant de modifier ou de supprimer le message.
+</p>
+
+<p>
+Nous créons deux fichiers <tt>protected/pages/posts/ReadPost.page</tt> et <tt>protected/pages/posts/ReadPost.php</tt> qui contiendront respectivement notre gabarit et notre classe.
+</p>
+
+<h2>Création du gabarit</h2>
+
+<p>
+Le gabarit de <tt>ReadPost</tt> est très proche du gabarit de <tt>PostRenderer</tt>, chacun d'eux affiche le détail d'un message. La différence est que la page <tt>ReadPost</tt> doit afficher deux boutons, permettant aux utilisateurs autorisés de modifier ou supprimer le message.
+</p>
+
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;com:TContent ID="Main">
+
+<h2>
+&lt;com:TLiteral Text="&lt;%= $this->Post->title %>" />
+</h2>
+
+&lt;com:TControl Visible="&lt;%= $this->canEdit() %>">
+ <a href="&lt;%= $this->Service->constructUrl('EditPost',array('id'=>$this->Post->post_id))%>">Modifier</a> |
+ &lt;com:TLinkButton Text="Supprimer"
+ OnClick="deletePost"
+ Attributes.onclick="javascript:if(!confirm('Etes vous sûr ?')) return false;" />
+&lt;/com:TControl>
+
+<p>
+Auteur:
+&lt;com:TLiteral Text="&lt;%= $this->Post->author->username %>" /><br/>
+Heure:
+&lt;com:TLiteral Text="&lt;%= date('m/d/Y h:m:sa', $this->Post->create_time) %>" />
+</p>
+
+<p>
+&lt;com:TLiteral Text="&lt;%= $this->Post->content %>" />
+</p>
+
+&lt;/com:TContent>
+</com:TTextHighlighter>
+
+<p>
+Plusieurs expressions PHP sont utilisées dans le gabarit. L'expression <tt>$this->Post</tt> fait référence à la propriété définie dans la classe de <tt>ReadPost</tt>. Elle représente l'objet <tt>PostRecord</tt> correspondant au message actuel.
+</p>
+
+
+<com:InfoBox>
+Même si nous utilisons régulièrement des expressions dans nos gabarits, nous n'en abusons pas. Une des règles principales pour savoir si l'on doit utiliser une expression est <i>l'expression doit être une propriété ou une simple mise en forme d'une propriété</i>. En suivant cette ligne de conduite, nous nous assurons d'une bonne séparation entre le contenu et la présentation, sans perdre en flexibilité.
+</com:InfoBox>
+
+
+<p>
+Nous pouvons aussi remarquer dans le gabarit précédent, que, nos deux boutons sont entourés d'un <tt>TControl</tt> dont la propriété 'visible' est déterminée par l'expression <tt>$this->canEdit()</tt>. Pour le bouton <tt>Supprimer</tt>, nous utilisons une boite de dialogue javascript pour confirmer la suppression du message.
+Nous pouvons aussi remarquer dans le gabarit précédent, que, nos deux boutons sont entourés d'un <tt>TControl</tt> dont la propriété 'visible' est déterminée par l'expression <tt>$this->canEdit()</tt>. Pour le bouton <tt>Supprimer</tt>, nous utilisons une boite de dialogue javascript pour confirmer la suppression du message.
+</p>
+
+<com:InfoBox>
+Tous les contrôles PRADO, ont une propriété très utile <tt>Attributes</tt> qui accepte n'importe quelle paire de valeurs (nom-valeur). La plupart des contrôles PRADO répercutent directement ces informations dans la balise HTML. Par exemple, dans le bouton <tt>Supprimer</tt> nous définissons <tt>onclick</tt> qui est directement reporté dans la balise <tt>&lt;a&gt;</tt> sous forme d'un attribut <tt>onclick</tt>.
+</com:InfoBox>
+
+
+
+<h2>Création du fichier de classe</h2>
+
+<p>
+Dans le gabarit précédent, nous voyons que notre classe doit implémenter le gestionnaire d'évènement <tt>deletePost()</tt> (attaché à l'évènement <tt>Onclick</tt> de notre bouton <tt>Supprimer</tt>). Nous devons aussi lire les données du message dont l'ID est passé par un paramètre GET.
+</p>
+
+
+<com:InfoBox>
+Nous implémentons la fonctionnalité suppression dans le classe <tt>ReadPost</tt> parce qu'il est classique de faire ainsi. Quand l'utilisateur clique sur le bouton <tt>Supprimer</tt>, une boite de dialogue demande confirmation de la suppression. Si l'utilisateur confirme, l'évènement <tt>OnClick</tt> du bouton <tt>Supprimer</tt> est déclenché.
+</com:InfoBox>
+
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class ReadPost extends TPage
+{
+ private $_post;
+ /**
+ * lis les données du message.
+ * cette méthode est appelée lors de l'initialisation de la page
+ * @param mixed param : paramètres de l'évènement
+ */
+ public function onInit($param)
+ {
+ parent::onInit($param);
+ // id du message passé par un paramètre GET
+ $postID=(int)$this->Request['id'];
+ // lis le message ainsi que les données correspondantes à l'auteur
+ $this->_post=PostRecord::finder()->withAuthor()->findByPk($postID);
+ if($this->_post===null) // si l'id du message est invalide
+ throw new THttpException(500, 'Impossible de trouver le message demandé.');
+ // défini le titre de la page comme étant celui du message
+ $this->Title=$this->_post->title;
+ }
+
+ /**
+ * @return PostRecord retourne l'objet PostRecord correspondant au message
+ */
+ public function getPost()
+ {
+ return $this->_post;
+ }
+
+ /**
+ * supprime le message actuellement visualisé
+ * cette méthode est appelée par l'évènement OnClick du bouton "Supprimer"
+ */
+ public function deletePost($sender,$param)
+ {
+ // seul l'auteur ou un administrateur peuvent supprimer le message
+ if(!$this->canEdit())
+ throw new THttpException('Nous n'êtes pas autorisé à effectuer cette action.');
+ // le supprime de la base de données
+ $this->_post->delete();
+ // redirige le navigateur vers la page d'accueil
+ $this->Response->redirect($this->Service->DefaultPageUrl);
+ }
+
+ /**
+ * @return boolean infiquant si le message peut être modifier ou supprimer par l'utilisateur actuel
+ */
+ public function canEdit()
+ {
+ // seul l'auteur ou un administrateur peuvent modifier/supprimer le message
+ return $this->User->Name===$this->Post->author_id || $this->User->IsAdmin;
+ }
+}
+</com:TTextHighlighter>
+
+<h2>Test</h2>
+<p>
+Pour tester notre page <tt>ReadPost</tt>, allons à l'URL <tt>http://hostname/blog/index.php</tt> et cliquons sur le titre du seul message affiché. Notre navigateur devrait afficher le résultat suivant avec l'URL <tt>http://hostname/blog/index.php?page=ReadPost&id=1</tt>. Notez que si vous n'êtes pas connecté, les deux boutons ne sont pas visibles.
+</p>
+
+<img src="<%~ output2.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/Overview.page b/demos/blog-tutorial/protected/pages/Day4/fr/Overview.page
new file mode 100755
index 00000000..baaf13af
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/Overview.page
@@ -0,0 +1,28 @@
+<com:TContent ID="Main">
+
+<h1>Vue d'ensemble de la gestion des messages</h1>
+
+<p>
+Dans cette section, nous allons créer les pages correspondantes à la gestion des messages. En particulier, nous mettrons en place les quatre opérations de base (Création-Lecture-Modification-Suppression) (CRUD:Create-Retrieve-Update-Delete).
+</p>
+
+
+<p>
+Nous allons créer les nouvelles pages dans le dossier <tt>protected/pages/posts</tt> créé à cet effet.
+</p>
+
+
+<ul>
+ <li><tt>ListPost</tt> affiche la liste des messages triés par ordre de date décroissante.</li>
+ <li><tt>ReadPost</tt> affiche le détail d'un message.</li>
+ <li><tt>NewPost</tt> permet aux utilisateurs enregistrés de créer un nouveau message.</li>
+ <li><tt>EditPost</tt> permet à l'auteur et aux administrateurs de modifier un message.</li>
+</ul>
+
+<p>
+Après avoir fini cette section, nous devrions obtenir l'arborescence suivante :
+</p>
+
+<img src="<%~ directories.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/directories.gif b/demos/blog-tutorial/protected/pages/Day4/fr/directories.gif
new file mode 100755
index 00000000..5ba55184
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/directories.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/output.gif b/demos/blog-tutorial/protected/pages/Day4/fr/output.gif
new file mode 100755
index 00000000..8c1caea8
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/output2.gif b/demos/blog-tutorial/protected/pages/Day4/fr/output2.gif
new file mode 100755
index 00000000..7078e6c6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/output3.gif b/demos/blog-tutorial/protected/pages/Day4/fr/output3.gif
new file mode 100755
index 00000000..ff1834a4
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/output3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day4/fr/output4.gif b/demos/blog-tutorial/protected/pages/Day4/fr/output4.gif
new file mode 100755
index 00000000..b1208a0d
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day4/fr/output4.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/ErrorLogging.page b/demos/blog-tutorial/protected/pages/Day5/fr/ErrorLogging.page
new file mode 100755
index 00000000..8e7c77e6
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/ErrorLogging.page
@@ -0,0 +1,158 @@
+<com:TContent ID="Main">
+
+<h1>Gestion et journalisation d'erreur</h1>
+
+<p>
+Si vous tentez de naviguez à l'URL <tt>http://hostname/blog/index.php?page=EditPost&id=100</tt>, vous verrez la page d'erreur suivante parce que le message avec l'ID 100 n'existe pas pour le moment. Nous voudrions personnaliser cette page d'erreur de manière à ce qu'elle garde la présentation générale du site. Nous voudrions aussi journaliser cette erreur pour étudier le comportement des utilisateurs. Dans cette section, nous allons mettre en place ces deux fonctionnalités.
+</p>
+
+<img src="<%~ output2.gif %>" class="output" />
+
+<com:InfoBox>
+Une des tâches importantes dans les applications Web est la <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Error">gestion des erreurs</a> ainsi que leurs <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Logging">journalisation</a>. Il y a deux types d'erreurs qui peuvent se produire dans une application PRADO : celles provenant des développeurs et celles des utilisateurs. Les premières doivent être résolues avant que l'application ne soit en production, tandis que les deuxièmes sont généralement un problème de prise en charge au niveau du design et doivent être gérées proprement (ie: journaliser cette erreur et indiquer à l'utilisateur que faire après). PRADO fournit un ensemble de fonctionnalités très utiles pour gérer et journaliser les erreurs.
+</com:InfoBox>
+
+
+<h2>Personnalisation de la gestion d'erreur</h2>
+
+<p>
+PRADO charge de manière implicite un module de gestion d'erreurs. Nous voulons personnaliser ce module pour qu'il affiche une page spécifique pour les erreurs causées par les utilisateurs. Nous modifions donc notre application comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module class="Application.BlogErrorHandler" />
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+La classe <tt>BlogErrorHandler</tt> comme spécifiée ci-dessus est un nouveau gestionnaire d'erreur que nous allons créer après. Il étend et remplace le module par défaut <tt>TErrorHandler</tt>.
+</p>
+
+<p>
+Nous créons un fichier nommé <tt>protected/BlogErrorHandler.php</tt> avec le contenu suivant. La classe <tt>BlogErrorHandler</tt> surcharge deux méthodes de <tt>TErrorHandler</tt> :
+</p>
+<ul>
+ <li><tt>getErrorTemplate()</tt> - cette méthode renvoie le gabarit utilisé pour afficher un message d'erreur utilisateur.</li>
+ <li><tt>handleExternalError()</tt> - cette méthode est appelée lorsqu'une erreur utilisateur se produit et elle affiche l'erreur.</li>
+</ul>
+<com:TTextHighlighter CssClass="source" Language="php">
+Prado::using('System.Exceptions.TErrorHandler');
+Prado::using('Application.BlogException');
+
+class BlogErrorHandler extends TErrorHandler
+{
+ /**
+ * Renvoi le fichier gabarit utilisé pour afficher l'erreur.
+ * Cette méthode surcharge la méthode originale.
+ */
+ protected function getErrorTemplate($statusCode,$exception)
+ {
+ // on utilise notre propre gabarit pour BlogException
+ if($exception instanceof BlogException)
+ {
+ // récupère le chemin du fichier de gabarit : protected/error.html
+ $templateFile=Prado::getPathOfNamespace('Application.error','.html');
+ return file_get_contents($templateFile);
+ }
+ else // sinon on utilise le gabarit par défaut.
+ return parent::getErrorTemplate($statusCode,$exception);
+ }
+
+ /**
+ * Gère les erreurs causées par les utilisateurs.
+ * Cette méthode surcharge la méthode originale.
+ * Elle est appelée lorsqu'une exception utilisateur est générée.
+ */
+ protected function handleExternalError($statusCode,$exception)
+ {
+ // Journaliser l'erreur (seulement pour BlogException)
+ if($exception instanceof BlogException)
+ Prado::log($exception->getErrorMessage(),TLogger::ERROR,'BlogApplication');
+ // appelle l'implémentation de la classe parente
+ parent::handleExternalError($statusCode,$exception);
+ }
+}
+</com:TTextHighlighter>
+
+<p>
+Dans le code précédent, nous spécifions que lorsqu'une exception de type <tt>BlogException</tt> est générée, nous utilisons le gabarit <tt>protected/error.html</tt> pour afficher l'erreur. Par ailleurs, nous devons créer la classe <tt>BlogException</tt> et remplacer toutes les occurrences de <tt>THttpException</tt> dans notre code (par exemple dans les pages <a href="?page=Day3.CreateEditUser">EditUser</a> et <a href="?page=Day4.CreateReadPost">ReadPost</a>). Nous devons aussi créer le gabarit <tt>protected/error.html</tt>. La classe <tt>BlogException</tt> hérite de <tt>THttpException</tt> et est vide. Le fichier de classe est enregistré sous <tt>protected/BlogException.php</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="php">
+class BlogException extends THttpException
+{
+}
+</com:TTextHighlighter>
+
+<p>
+Ci-dessous le contenu du gabarit <tt>protected/error.html</tt>. Remarquez que ce gabarit n'est pas un gabarit PRADO, ceci parce qu'il ne reconnait qu'un nombre limité de mots clés, par exemple <tt>%%ErrorMessage%%</tt>, <tt>%%ServerAdmin%%</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<html>
+<head>
+<title>%%ErrorMessage%%</title>
+</head>
+<body>
+<div id="page">
+<div id="header">
+<h1>Mon Blog</h1>
+</div>
+<div id="main">
+<p style="color:red">%%ErrorMessage%%</p>
+<p>
+Une erreur est apparue lors du traitement de votre demande.
+</p>
+<p>
+Si vous pensez que c'est une erreur de notre serveur, veuillez contacter <a href="mailto:%%ServerAdmin%%">webmaster</a>.
+</p>
+</div>
+</body>
+</html>
+</com:TTextHighlighter>
+
+
+<h2>Journalisation des erreurs</h2>
+
+<p>
+Dans la méthode <tt>handleExternalError()</tt> de <tt>BlogErrorHandler</tt>, nous appelons <tt>Prado::log()</tt> pour journaliser l'erreur si elle est de type <tt>BlogException</tt>. L'erreur est stockée en mémoire. Pour enregistrer le journal d'erreur sur un support non volatil, tel que le disque dur ou une base de données, nous devons indiquer à PRADO comment procéder. Ceci est fait par la configuration d'application suivante :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module id="log" class="System.Util.TLogRouter">
+ <route class="TFileLogRoute" Categories="BlogApplication" />
+ </module>
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+Dans le code ci-dessus, nous ajoutons une "route" pour enregistrer le journal d'erreur dans un fichier. Nous spécifions aussi le filtre de catégorie <tt>BlogApplication</tt>, de manière à ce que seules les erreurs de type <tt>BlogApplication</tt> soient sauvegardées. Cette possibilité permet de réduire la taille du journal et d'en améliorer la lisibilité.
+</p>
+
+<h2>Test</h2>
+<p>
+Pour voir comme notre blog se comporte suite à une demande invalide, nous naviguons à l'URL <tt>http://hostname/blog/index.php?page=posts.ReadPost&id=100</tt>. Nous devrions voir la page suivante qui est différente de celle vue précédemment.
+</p>
+
+<img src="<%~ output3.gif %>" class="output" />
+
+<p>
+Si nous regardons dans le dossier <tt>protected/runtime</tt>, nous devrions y trouver un fichier nommé <tt>prado.log</tt>. C'est le journal d'erreur que nous venons juste de paramétrer. Le fichier pourrait contenir quelque chose comme :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="text">
+Jun 28 22:15:27 [Error] [BlogApplication] Unable to find the specified post.
+Jun 29 08:42:57 [Error] [BlogApplication] Unable to find the specified post.
+</com:TTextHighlighter>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/Performance.page b/demos/blog-tutorial/protected/pages/Day5/fr/Performance.page
new file mode 100755
index 00000000..59eee1b0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/Performance.page
@@ -0,0 +1,66 @@
+<com:TContent ID="Main">
+
+<h1>Amélioration des performances</h1>
+
+<p>
+Avant le déploiement de notre blog, nous voudrions améliorer les performances.
+</p>
+
+<h2>Changer le mode de fonctionnement de l'application</h2>
+
+<p>
+Une application PRADO peut-être configurée pour fonctionner suivant différents modes. Par défaut, elle fonctionne en mode <tt>Debug</tt>, mode qui génère beaucoup de message de journalisation et qui, en cas d'erreurs, affiche la pile des appels et l'emplacement de l'erreur. Ce comportement est préférable en cours de développement, mais pas en production. Pour changer le mode de fonctionnement de <tt>Debug</tt> à <tt>Normal</tt> ( qui signifie "en production"), nous devons modifier le fichier de configuration de l'application comme ci-dessous :
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+<?xml version="1.0" encoding="utf-8"?>
+<application id="blog" mode="Normal">
+ ......
+</application>
+</com:TTextHighlighter>
+
+<h2>Enabling Caching</h2>
+
+<p>
+There are a lot of parsing work involved in a PRADO application: configuration XMLs, templates, theme skins, etc. For every user request, PRADO needs to redo the parsing. To save this effort, we can enable caching. To do so, we modify the application configuration as follows,
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+<modules>
+ ......
+ <module id="cache" class="System.Caching.TDbCache" />
+ ......
+</modules>
+......
+</com:TTextHighlighter>
+
+<p>
+Maintenant, après avoir requis n'importe quelle page de notre blog, nous devrions trouver un fichier nommé <tt>sqlite3.cache</tt>. C'est un fichier de base de données qui mémorise les éléments analysés : gabarits, configurations, etc.
+</p>
+
+<com:InfoBox>
+Le module de <tt>cache</tt> que nous venons d'activer utilise une base de données comme support d'enregistrement. PRADO propose d'autres modules de <tt>cache</tt> plus rapide, tels que <tt>TMemCache</tt>, <tt>TAPCCache</tt>. Ces modules requièrent les extensions PHP correspondantes.
+</com:InfoBox>
+
+
+<h2>Utilisation de <tt>pradolite.php</tt></h2>
+
+<p>
+Afficher une page PRADO requiert des dizaines de fichiers PHP, ce qui est une cause de perte de temps. Ces fichiers comportent aussi beaucoup de commentaires qui permettent de générer la documentation des API. Dans le but de réduire ce coût, nous modifions notre fichier <tt>index.php</tt> et remplaçons <tt>prado.php</tt> par <tt>pradolite.php</tt>. Ce dernier est un gros fichier incluant les fichiers PHP nécessaires et dont on a retiré les commentaires.
+</p>
+
+<h2>Autres techniques</h2>
+
+<p>
+D'autres techniques sont disponibles pour améliorer les performances d'une application PRADO. D'après notre expérience, un des goulets d'étranglement dans une application Web, est l'accès aux bases de données. Les requêtes en base de données prennent souvent du temps, ce qui dégrade le temps d'affichage d'une page. Le <tt>cache</tt> est la principale solution à ce problème. Le module de <tt>cache</tt> activé dans notre fichier de configuration d'application peut aussi être utilisé dans ce but.
+</p>
+<p>
+Pour une page relativement stable et souvent consultée, le <a href="http://www.pradosoft.com/demos/quickstart/?page=Controls.OutputCache">cache de sortie</a> doit être envisagé. Le <tt>cache de sortie</tt> met en <tt>cache</tt> les parties sélectionnées d'une page. Ceci peut améliorer les performances des pages mises en <tt>cache</tt> de manière significative.
+</p>
+
+<p>
+Il a été prouvé que les solutions de <tt>cache</tt> côté serveur étaient très efficaces pour améliorer les performances d'une application PRADO. Par exemple, nous avons observé qu'en utilisant le <tt>Zend Optimizer</tt>, le RPS (requêtes par seconde) peut être multiplié par 10. Bien sûr, ceci au risque d'avoir des pages périmées, tandis que les solutions de <tt>cache</tt> de PRADO garantissent la validité des pages fournies.
+</p>
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/Summary.page b/demos/blog-tutorial/protected/pages/Day5/fr/Summary.page
new file mode 100755
index 00000000..1472c934
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/Summary.page
@@ -0,0 +1,41 @@
+<com:TContent ID="Main">
+
+<h1>Résumé</h1>
+
+<p>
+Nous pouvons finalement déployer notre blog. Pour cela, nous devons juste copier le dossier <tt>blog</tt> complet vers le dossier du serveur Web. Nous pourrions avoir besoin de modifier <tt>index.php</tt> pour qu'il puisse trouver le chemin vers l'emplacement où a été installé le framework PRADO.
+</p>
+
+<p>
+Nous avons donc fini notre blog. Le processus peut paraitre complexe vu que nous avons passé pas loin de cinq jours pour y arriver. Toutefois, comme nous l'avions dit au début, le but de ce tutoriel est d'aider les développeurs PRADO à appréhender les principales techniques de PRADO.Le tutoriel n'avais pas pour but de finir un blog en cinq minutes, sinon nous n'aurions rien appris.
+</p>
+
+<p>
+En résumé, développer une application de gestion de base de données PRADO nécessite les étapes suivantes :
+</p>
+<ol>
+ <li>Analyse et création de la base de données</li>
+ <li>Créer le squelette de l'application avec <tt>prado-cli</tt></li>
+ <li>Mise en place de la gestion d'erreur pour gérer les erreurs d'utilisations</li>
+ <li>Création et mise en place du thème</li>
+ <li>Création et mise en place des gabarits principaux</li>
+ <li>Création de la connexion et des classes d'accès aux données</li>
+ <li>Création des différentes pages</li>
+ <li>Test et amélioration des performances/li>
+ <li>Déploiement</li>
+</ol>
+
+<p>
+Contrairement à l'ordre de notre tutoriel, la gestion d'erreur et la création des thèmes sont placées au début du processus. Ceci est dû au fait que des changements d'ordre généraux sont la plupart du temps requis. Par exemple, nous avons dû remplacer <tt>THttpException</tt> par <tt>BlogException</tt> dans notre tutoriel. Si vous définissez vos feuilles de styles plus tôt, vous pourrez plus facilement les utiliser au cours de la création des gabarits de pages.
+</p>
+
+<p>
+Un dernier conseil, essayez de penser orienté objet pendant la phase d'analyse et d'implémentation. Utilisez l'héritage le plus souvent, et vous trouverez que le projet est plus facile à développer en équipe. Il vous sera aussi plus facile de réutiliser votre code et ainsi vos futurs projets seront finis plus rapidement.
+</p>
+
+
+
+
+<hr />
+<p>Traduction (laborieuse) par Eric.M, nous pouvez me contacter par message privé ou par email sur le <a href="http://www.pradosoft.com/forum/index.php">forum PRADO</a>.</p>
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/UseTheme.page b/demos/blog-tutorial/protected/pages/Day5/fr/UseTheme.page
new file mode 100755
index 00000000..840a26b0
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/UseTheme.page
@@ -0,0 +1,138 @@
+<com:TContent ID="Main">
+
+<h1>Utilisation des Thèmes et des Skins</h1>
+
+<p>
+PRADO propose un support intrinsèque des <a href="http://www.pradosoft.com/demos/quickstart/?page=Advanced.Themes">thèmes</a>. En utilisant les thèmes, nous pouvons mieux séparer la logique applicative de la présentation et nous pouvons aussi changer facilement la présentation générale de notre blog.
+</p>
+
+<h2>Création des thèmes</h2>
+
+<p>
+Nous devons auparavant créer un dossier <tt>themes</tt>. C'est le dossier parent de tous les thèmes pour une application de PRADO. Chaque sous-dossier devient ainsi un thème dont le nom est le nom du sous-dossier.
+</p>
+
+<p>
+Pour créer un thème nommé <tt>Basic</tt>, nous créons un sous-dossier <tt>theme/Basic</tt>. Dans ce dossier, nous pouvons mettre des feuilles de styles dépendantes du thème, des fichiers Javascript, des images et des fichiers skins.
+</p>
+
+<com:InfoBox>
+Le dossier <tt>themes</tt> doit être accessible de l'extérieur. Ne mettez pas de données sensibles dans ce dossier. Nous pouvons changer l'emplacement de ce dossier en configurant le module <a href="http://www.pradosoft.com/docs/classdoc/TThemeManager">TThemeManager</a> dans le fichier de configuration de l'application.
+</com:InfoBox>
+
+
+<h3>Création de la feuille de style</h2>
+
+<p>
+Dans le dossier <tt>themes/Basic</tt>, nous créons un fichier CSS nommé <tt>style.css</tt>. Quand une page utilise ce thème, PRADO importe automatiquement la feuille de style dans cette page. Le même traitement est appliqué aux fichiers Javascript.
+</p>
+
+<p>
+Le contenu du fichier CSS est le suivant :
+</p>
+
+<com:TTextHighlighter CssClass="source">
+body {
+ font-family: verdana, 'trebuchet ms', sans-serif;
+ font-size: 10pt;
+ background: white;
+}
+#page {
+ margin: 0 auto 0 auto;
+ width: 600px;
+}
+#footer {
+ text-align: center;
+ margin-top: 10px;
+ padding: 10px;
+ border-top: 1px solid silver;
+}
+.post-box {
+ margin-bottom: 10px;
+ padding: 5px;
+}
+.post-box h3 {
+ padding: 5px;
+ font-size: 13pt;
+ background: lightgray;
+}
+.post-box a {
+ color: black;
+ text-decoration: none;
+}
+.post-box a:hover {
+ color: red;
+}
+</com:TTextHighlighter>
+
+
+<h3>Création du fichier de Skin</h2>
+
+<p>
+Nous utilisons des <tt>skin</tt> pour initialiser les propriétés des contrôles PRADO. Les fichiers <tt>skin</tt> sont enregistrés avec une extension <tt>.skin</tt> dans le dossier du thème. Chaque fichier <tt>skin</tt> peut contenir plusieurs modèles pour un ou plusieurs types de contrôles.
+</p>
+
+<p>
+Pour notre test, nous allons créer un fichier <tt>skin</tt> qui changera la couleur de fond de nos liens dans le pied de page. Nous créons un fichier nommé <tt>button.skin</tt> dans le dossier du thème <tt>themes/Basic</tt>.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="prado">
+&lt;com:THyperLink SkinID="MainMenu" BackColor="lightgreen" />
+</com:TTextHighlighter>
+
+<p>
+Le fichier <tt>button.skin</tt> contient une seule définition pour les contrôles de type <tt>THyperLink</tt> dont la propriété <tt>SkinID</tt> est <tt>MainMenu</tt>. La définition applique une couleur vert-clair comme couleur de fond du contrôle.
+</p>
+
+<p>
+En accord avec cette définition, nous modifions notre fichier <tt>protected/common/MainLayout.tpl</tt> pour appliquer aux liens du pied de page la valeur <tt>MainMenu</tt> à la propriété <tt>SkinID</tt>.
+</p>
+<com:TTextHighlighter CssClass="source" Language="prado">
+......
+<div id="footer">
+......
+&lt;com:THyperLink Text="Home" SkinID="MainMenu"
+ NavigateUrl="&lt;%= $this->Service->DefaultPageUrl %>" />
+
+&lt;com:THyperLink Text="New Post" SkinID="MainMenu"
+ NavigateUrl="&lt;%= $this->Service->constructUrl('posts.NewPost') %>"
+ Visible="&lt;%= !$this->User->IsGuest %>" />
+......
+</div>
+......
+</com:TTextHighlighter>
+
+<com:InfoBox>
+La syntaxe des fichiers <tt>skin</tt> est très proche de celle des gabarits. Chaque balise <tt>&lt;com:&gt;</tt> définit la présentation d'un type de contrôle. PRADO concatène automatiquement les fichiers <tt>skin</tt> pour un thème et applique le tout lorsque la page est affichée.
+</com:InfoBox>
+
+
+<h2>Utilisation du thème</h2>
+
+<p>
+Pour utiliser le thème que nous venons juste de créer, nous modifions notre fichier de configuration de l'application comme ci-après. Comme vous pouvez le voir, nous affectons la valeur <tt>Basic</tt> (le nom du thème) à la priorité <tt>Theme</tt> pour toutes les pages.
+</p>
+
+<com:TTextHighlighter CssClass="source" Language="xml">
+......
+ <services>
+ <service id="page" class="TPageService" DefaultPage="posts.ListPost">
+ <pages MasterClass="Application.layouts.MainLayout" Theme="Basic" />
+ </service>
+ </services>
+......
+</com:TTextHighlighter>
+
+<com:InfoBox>
+Il est possible de préciser différents thèmes pour différentes pages, et ceci peut-être faits soit en modifiant le fichier de configuration de page soit par programmation (propriété <tt>Theme</tt>). En dernier recours, on peut le faire dans la méthode <tt>onPreInit()</tt> de la page, ceci parce que PRADO applique le thème au début du cycle de vie de la page.
+</com:InfoBox>
+
+
+<h2>Test</h2>
+<p>
+Pour voir la nouvelle présentation de notre site, allons à l'URL <tt>http://hostname/blog/index.php</tt>. Nous pouvons constater que la mise en page, les polices, les bordures sont modifiées. De même, la couleur de fond des liens en pied de page est vert-clair.
+</p>
+
+<img src="<%~ output.gif %>" class="output" />
+
+</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/output.gif b/demos/blog-tutorial/protected/pages/Day5/fr/output.gif
new file mode 100755
index 00000000..67bd18a3
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/output.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/output2.gif b/demos/blog-tutorial/protected/pages/Day5/fr/output2.gif
new file mode 100755
index 00000000..16c81704
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/output2.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/Day5/fr/output3.gif b/demos/blog-tutorial/protected/pages/Day5/fr/output3.gif
new file mode 100755
index 00000000..6879bbdf
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/Day5/fr/output3.gif
Binary files differ
diff --git a/demos/blog-tutorial/protected/pages/fr/Overview.page b/demos/blog-tutorial/protected/pages/fr/Overview.page
index 6920a6aa..72fc7371 100755
--- a/demos/blog-tutorial/protected/pages/fr/Overview.page
+++ b/demos/blog-tutorial/protected/pages/fr/Overview.page
@@ -1,17 +1,17 @@
-<com:TContent ID="Main">
+<com:TContent ID="Main">
<h1>Bienvenue dans ce tutoriel de développement d'un Blog</h1>
<p>
-Le but de ce tutoriel est de fournir aux nouveaux utilisateurs de PRADO, un guide pas à pas sur la façon de développer avec PRADO. Les lecteurs de ce tutoriel n'ont besoin d'aucune connaissances au sujet de PRADO. Par contre, les lecteurs doivent avoir des connaissances de base concernant la programmation orientées objets (POO) et les bases de données. Pour un guide plus exhaustif de PRADO, les lecteurs peuvent se référer au document <a href="http://www.pradosoft.com/demos/quickstart/">Quickstart Tutorial</a>.
+Le but de ce tutoriel est de fournir aux nouveaux utilisateurs de PRADO, un guide pas à pas sur la façon de développer avec PRADO. Les lecteurs de ce tutoriel n'ont besoin d'aucune connaissance au sujet de PRADO. Par contre, les lecteurs doivent avoir des connaissances de base concernant la programmation orientée objet (POO) et les bases de données. Pour un guide plus exhaustif de PRADO, les lecteurs peuvent se référer au document <a href="http://www.pradosoft.com/demos/quickstart/">Quickstart Tutorial</a>.
</p>
<p>
-Ce tutoriel est organisé sous la forme de journées. Chaque journée, des concepts et des techniques nouvelles de PRADO sont abordées, et de nouvelles fonctionalités sont mise en place. A la fin, nous obtiendrons un moteur de Blog simple mettant en place les <a href="?page=Requirements">fonctionnalités suivantes</a>.
+Ce tutoriel est organisé sous la forme de journées. Chaque journée, des concepts et des techniques nouvelles de PRADO sont abordés et de nouvelles fonctionnalités sont mises en place. A la fin, nous obtiendrons un moteur de Blog simple mettant en place les fonctionnalités suivantes <a href="?page=Requirements">buts à atteindre</a>.
</p>
<p>
-Pour pouvoir suivre ce tutoriel et créer les <a href="samples/">exemples pas à pas</a>, les lecteurs doivent avoir accès à un serveur Web qui implémente <b>PHP 5.1.0+</b> et <b>PRADO 3.1+</b>. Les instructions pour installer PRADO peuvent être trouvés ici : <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.Installation">pradosoft.com</a>.
+Pour pouvoir suivre ce tutoriel et créer les <a href="samples/">exemples pas à pas</a>, les lecteurs doivent avoir accès à un serveur Web qui implémente <b>PHP 5.1.0+</b> et <b>PRADO 3.1+</b>. Les instructions pour installer PRADO peuvent être trouvées ici : <a href="http://www.pradosoft.com/demos/quickstart/?page=GettingStarted.Installation">pradosoft.com</a>.
</p>
</com:TContent> \ No newline at end of file
diff --git a/demos/blog-tutorial/protected/pages/fr/Requirements.page b/demos/blog-tutorial/protected/pages/fr/Requirements.page
new file mode 100755
index 00000000..976a621f
--- /dev/null
+++ b/demos/blog-tutorial/protected/pages/fr/Requirements.page
@@ -0,0 +1,37 @@
+<com:TContent ID="Main">
+
+<h1>Buts à atteindre</h1>
+<p>
+Cette page décrit les buts que nous allons nous fixer et développer avec PRADO. Nous n'allons pas mettre en place toutes les fonctionnalités d'un blog (ie : commentaires, organisation des messages, calendrier, etc.), parce que nous voulons garder ce tutoriel aussi court que possible et que nous espérons que ces fonctionnalités seront faciles à mettre en place après avoir fini ce tutoriel.
+</p>
+
+<p>
+En général, les blog permettent aux utilisateurs de lire les messages et aux utilisateurs authentifiés de publier des messages. L'outil doit séparer la logique applicative de la couche présentation et il doit supporter le changement de thèmes graphiques.
+</p>
+
+<h2>Gestion des utilisateurs</h2>
+<ul>
+<li>L'outil doit permettre de gérer les utilisateurs et leurs droits.</li>
+<li>L'outil doit permettre à l'administrateur de lister tous les utilisateurs.</li>
+<li>L'outil doit permettre à l'administrateur de créer des nouveaux utilisateurs.</li>
+<li>L'outil doit permettre à l'administrateur ou au propriétaire du compte de modifier son profil.</li>
+<li>L'outil doit permettre à l'administrateur de supprimer un utilisateur.</li>
+</ul>
+
+<h2>Gestion des messages</h2>
+<ul>
+<li>L'outil doit permettre de lister les messages par ordre de date décroissante et d'en gérer la pagination.</li>
+<li>L'outil doit permettre de consulter le détail d'un message.</li>
+<li>L'outil doit permettre la création d'un nouveau message par un utilisateur authentifié.</li>
+<li>L'outil doit permettre la mise à jour d'un message par son auteur ou par l'administrateur.</li>
+<li>L'outil doit permettre la suppression d'un message par son auteur ou par l'administrateur.</li>
+</ul>
+
+<h2>Maintenance de l'outil</h2>
+<ul>
+<li>L'outil doit permettre de collecter les retours utilisateurs.</li>
+<li>L'outil doit être assez flexible pour permettre l'intégration ultérieure de nouveaux composants.</li>
+<li>L'outil doit permettre de changer le thème qui régit la présentation des composants.</li>
+</ul>
+
+</com:TContent> \ No newline at end of file