summaryrefslogtreecommitdiff
path: root/demos/blog-tutorial/protected/pages/Day4
diff options
context:
space:
mode:
Diffstat (limited to 'demos/blog-tutorial/protected/pages/Day4')
-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
10 files changed, 629 insertions, 0 deletions
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