From 2fd00de2170fcb16b5214c7839c18f0dacab8cdb Mon Sep 17 00:00:00 2001 From: tof <> Date: Fri, 31 Aug 2007 09:58:35 +0000 Subject: Full translation of Blog Tutorial in French. Thanks to Eric.M ! --- .../protected/common/fr/TopicList.tpl | 62 ++++++ .../protected/layout/fr/MainLayout.tpl | 54 ++++++ .../protected/pages/Day1/fr/CreateContact.page | 203 ++++++++++++++++++++ .../protected/pages/Day1/fr/Setup.page | 161 ++++++++++++++++ .../protected/pages/Day1/fr/ShareLayout.page | 182 ++++++++++++++++++ .../protected/pages/Day1/fr/directories.gif | Bin 0 -> 3611 bytes .../protected/pages/Day1/fr/directories2.gif | Bin 0 -> 4147 bytes .../protected/pages/Day1/fr/directories3.gif | Bin 0 -> 3531 bytes .../protected/pages/Day1/fr/output.gif | Bin 0 -> 13379 bytes .../protected/pages/Day2/fr/ConnectDB.page | 47 +++++ .../protected/pages/Day2/fr/CreateAR.page | 177 +++++++++++++++++ .../protected/pages/Day2/fr/CreateDB.page | 70 +++++++ demos/blog-tutorial/protected/pages/Day2/fr/ER.gif | Bin 0 -> 4444 bytes demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd | Bin 0 -> 73216 bytes .../protected/pages/Day2/fr/directories.gif | Bin 0 -> 4580 bytes .../protected/pages/Day2/fr/directories2.gif | Bin 0 -> 6795 bytes .../protected/pages/Day3/fr/Auth.page | 106 +++++++++++ .../protected/pages/Day3/fr/CreateAdminUser.page | 159 ++++++++++++++++ .../protected/pages/Day3/fr/CreateEditUser.page | 199 +++++++++++++++++++ .../protected/pages/Day3/fr/CreateLoginUser.page | 161 ++++++++++++++++ .../protected/pages/Day3/fr/CreateNewUser.page | 212 +++++++++++++++++++++ .../protected/pages/Day3/fr/Overview.page | 42 ++++ .../protected/pages/Day3/fr/directories.gif | Bin 0 -> 10329 bytes .../protected/pages/Day3/fr/output.gif | Bin 0 -> 10006 bytes .../protected/pages/Day3/fr/output2.gif | Bin 0 -> 9222 bytes .../protected/pages/Day3/fr/output3.gif | Bin 0 -> 9464 bytes .../protected/pages/Day4/fr/CreateEditPost.page | 136 +++++++++++++ .../protected/pages/Day4/fr/CreateListPost.page | 182 ++++++++++++++++++ .../protected/pages/Day4/fr/CreateNewPost.page | 139 ++++++++++++++ .../protected/pages/Day4/fr/CreateReadPost.page | 144 ++++++++++++++ .../protected/pages/Day4/fr/Overview.page | 28 +++ .../protected/pages/Day4/fr/directories.gif | Bin 0 -> 11129 bytes .../protected/pages/Day4/fr/output.gif | Bin 0 -> 3406 bytes .../protected/pages/Day4/fr/output2.gif | Bin 0 -> 6326 bytes .../protected/pages/Day4/fr/output3.gif | Bin 0 -> 11874 bytes .../protected/pages/Day4/fr/output4.gif | Bin 0 -> 11916 bytes .../protected/pages/Day5/fr/ErrorLogging.page | 158 +++++++++++++++ .../protected/pages/Day5/fr/Performance.page | 66 +++++++ .../protected/pages/Day5/fr/Summary.page | 41 ++++ .../protected/pages/Day5/fr/UseTheme.page | 138 ++++++++++++++ .../protected/pages/Day5/fr/output.gif | Bin 0 -> 4282 bytes .../protected/pages/Day5/fr/output2.gif | Bin 0 -> 7798 bytes .../protected/pages/Day5/fr/output3.gif | Bin 0 -> 5190 bytes .../blog-tutorial/protected/pages/fr/Overview.page | 8 +- .../protected/pages/fr/Requirements.page | 37 ++++ 45 files changed, 2908 insertions(+), 4 deletions(-) create mode 100755 demos/blog-tutorial/protected/common/fr/TopicList.tpl create mode 100755 demos/blog-tutorial/protected/layout/fr/MainLayout.tpl create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/CreateContact.page create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/Setup.page create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/ShareLayout.page create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/directories.gif create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/directories2.gif create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/directories3.gif create mode 100755 demos/blog-tutorial/protected/pages/Day1/fr/output.gif create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/ConnectDB.page create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/CreateAR.page create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/CreateDB.page create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/ER.gif create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/directories.gif create mode 100755 demos/blog-tutorial/protected/pages/Day2/fr/directories2.gif create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/Auth.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/CreateAdminUser.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/CreateEditUser.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/CreateLoginUser.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/CreateNewUser.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/Overview.page create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/directories.gif create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/output.gif create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/output2.gif create mode 100755 demos/blog-tutorial/protected/pages/Day3/fr/output3.gif create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/CreateEditPost.page create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/CreateListPost.page create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/CreateNewPost.page create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/CreateReadPost.page create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/Overview.page create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/directories.gif create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/output.gif create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/output2.gif create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/output3.gif create mode 100755 demos/blog-tutorial/protected/pages/Day4/fr/output4.gif create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/ErrorLogging.page create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/Performance.page create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/Summary.page create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/UseTheme.page create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/output.gif create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/output2.gif create mode 100755 demos/blog-tutorial/protected/pages/Day5/fr/output3.gif create mode 100755 demos/blog-tutorial/protected/pages/fr/Requirements.page (limited to 'demos') diff --git a/demos/blog-tutorial/protected/common/fr/TopicList.tpl b/demos/blog-tutorial/protected/common/fr/TopicList.tpl new file mode 100755 index 00000000..2154db86 --- /dev/null +++ b/demos/blog-tutorial/protected/common/fr/TopicList.tpl @@ -0,0 +1,62 @@ +
+ +
+
Introduction
+ +
+ +
+
1er jour: Découvrir PRADO
+ +
+ +
+
2ième jour: Mise en place de la Base de Données
+ +
+ +
+
3ième jour: Gestion des utilisateurs
+ +
+ +
+
4ième jour: Gestion des Messages
+ +
+ +
+
5ième jour: Refactorisation et déploiement
+ +
+ +
\ No newline at end of file diff --git a/demos/blog-tutorial/protected/layout/fr/MainLayout.tpl b/demos/blog-tutorial/protected/layout/fr/MainLayout.tpl new file mode 100755 index 00000000..31464213 --- /dev/null +++ b/demos/blog-tutorial/protected/layout/fr/MainLayout.tpl @@ -0,0 +1,54 @@ + + + + + + + + + + + + + +Accueil | +PradoSoft.com + + + + + + + +
+ + + + +
Available Languages:
    + + +
  • DataItem %> />
  • +
    + +
+ +
+
+ +
+
+ + + +
+ + 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 @@ + + +

Création de la page Contact

+ +

+Nous avons créé une page par défaut Home.page en utilisant les outils en ligne de commande de PRADO. 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 Contact. +

+ +

+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. +

+ +

+Pour créer la page Contact, nous avons besoin de 2 fichiers dans le dossier pages : le fichier de gabarit Contact.page et le fichier de classe PHP Contact.PHP. +

+ + + + +Une page doit avoir un fichier de gabarit (extension .page) ou un fichier de classe PHP, ou les deux : +

+ + +
+ + +

Création de la page gabarit

+ +

+Nous allons premièrement créer le fichier gabarit de la page Contact. +

+ +

+Nous utilisons un fichier gabarit pour organiser la présentation de notre formulaire. Dans notre gabarit, nous utilisons des champs de saisie pour collecter le nom de l'utilisateur, son email et son commentaire. D'autre part, nous utilisons des validateurs 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, +

+ + + +Mon Blog - Contact + +

Contact

+

Veuillez remplir le formulaire suivant pour me laisser vos impressions au sujet de mon blog. Merci !

+ +<com:TForm> + +Votre nom: +<com:TRequiredFieldValidator ControlToValidate="Name" + ErrorMessage="Veuillez indiquer votre nom." + Display="Dynamic" /> +
+<com:TTextBox ID="Name" /> + +
+Votre email: +<com:TRequiredFieldValidator ControlToValidate="Email" + ErrorMessage="Veuillez indiquer votre email." + Display="Dynamic" /> +<com:TEmailAddressValidator ControlToValidate="Email" + ErrorMessage="Vous avez saisi un email invalide." + Display="Dynamic" /> +
+<com:TTextBox ID="Email" /> + +
+Commentaires: +<com:TRequiredFieldValidator ControlToValidate="Feedback" + ErrorMessage="Veuillez saisir un commentaire." + Display="Dynamic" /> +
+<com:TTextBox ID="Feedback" + TextMode="MultiLine" + Rows="10" + Columns="40" /> + +
+<com:TButton Text="Envoyer" OnClick="submitButtonClicked" /> + +</com:TForm> + + + +
+ +

+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 <com:>. Chaque balise <com:> fait référence à un contrôle dont les propriétés sont initialisées grâce aux paires nom-valeur de la balise. Par exemple, la balise <com:TButton> fait référence au contrôle TButton qui affiche un bouton permettant à l'utilisateur de soumettre le formulaire. Pour une syntaxe complète, veuillez vous référer au Tutoriel de démarrage rapide. + + +PRADO fournit un contrôle pour chaque type de balise HTML. Par exemple, TTextBox affiche un champ de saisie, TDropDownList 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. + + + +

+Avant le contrôle TTextBox, 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, +

+ + +Your Email: +<com:TRequiredFieldValidator + ControlToValidate="Email" + ErrorMessage="Veuillez indiquer votre email." + Display="Dynamic" /> +<com:TEmailAddressValidator + ControlToValidate="Email" + ErrorMessage="Vous avez saisi un email invalide." + Display="Dynamic" /> +
+<com:TTextBox ID="Email" /> +
+
+ +

+Ci-dessous, un résumé des contrôles utilisés dans le gabarit : +

+ + + + +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.). + + +

Création du fichier de classe PHP

+ +

+Nous allons maintenant créer le fichier de classe PHP Contact.PHP. Ce fichier est nécessaire parce que nous devons agir après la soumission du formulaire. +

+ +

+Notez les lignes dans le fichier gabarit. Elles indiquent que lorsque l'utilisateur soumet le formulaire, la méthode submitButtonClicked() doit être appelé. Ici, OnClick est le nom de l'évènement et la méthode correspondante doit être défini dans le fichier de classe PHP. +

+ + + <com:TButton Text="Submit" OnClick="submitButtonClicked" /> + + +

+Nous écrirons donc le fichier de classe suivant : +

+ + +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 + } +} +?> + + +

+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 submitButtonClicked(), nous récupérons les éléments saisies par l'utilisateur. Par exemple, $this->Name->Text retourne la valeur de la propriété Text du contrôle Name qui est un contrôle permettant la saisie du nom de l'utilisateur. +

+ + +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. + + +

Test

+ +

+Notre nouvelle page Contact peut être testée en naviguant à l'URL http://hostname/blog/index.PHP?page=Contact. 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 mailFeedback() sera appelée. +

+ + + +

+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. +

+ + +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. + + +
\ 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 @@ + + +

Installation

+ +

+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 les outils en ligne de commande pour atteindre ce but. +

+ +

Nous partons du principe que le nom du dossier qui contiendra l'application est blog et que l'URL qui permet d'accéder à ce dossier est : http://hostname/blog/ (remplacer hostname par le nom de votre serveur). +

+ +

A l'intérieur du dossier blog, nous utilisons les outils en ligne de commande avec comme commande (remplacer path/to par le chemin d'installation du framework PRADO): +

+ +php path/to/prado-cli.php -c . + + +

+L'utilisation de cette commande permet de créer la structure de dossier et fichiers suivante: +

+ + + +

+Nous avons dorénavant, un squellette d'application PRADO accessible par l'URL http://hostname/blog/index.php et qui affiche une page contenant le message "Welcome to PRADO". +

+ +

+Il est de notre intérêt d'en apprendre plus à propos des dossiers et fichiers que nous venons de créer. +

+ +

Les fichiers initiaux

+ +

Le script principal de l'application

+ +

+Toutes les applications PRADO ont un point d'entrée, habituellement nommé index.php. 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. +

+ +

+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, +

+ + +run(); +?> + + + +Le nom du script ne doit pas être obligatoirement index.php. 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 index.php5, ce qui permettra au serveur Web de le traiter correctement. + + +

Le fichier de configuration de l'application

+

+Le fichier optionnel XML application.xml contient la configuration de l'application. 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 log pour notre application par le biais du fichier de configuration. +

+ +

+Le fichier application.xml 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 application.xml et vous expliquerons comment configurer l'application. +

+ + +

La page d'accueil

+ +

+La page d'accueil Home.page (aussi dénommée page par défaut) est la seule page 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 http://hostname/blog/index.php. +

+ +

+Le contenu du fichier Home.page respecte le format de template qui pour la plupart du temps est du code HTML agrémenté de quelques balises spécifiques à PRADO. Par exemple, dans Home.page nous voyons du code HTML pur : +

+ + + + + Welcome to PRADO + + +

Welcome to PRADO!

+ + +
+ + +

Les dossiers initiaux

+ +

Le dossier protected

+ +

+Le dossier protected, aussi connu sous le nom chemin de base de l'application, est le dossier racine qui contient les pages, les gabarits, les fichiers de configuration, les données, etc. Le nom protected 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. +

+ +

+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 deny from all. +

+ + +

Les dossiers protected/runtime et assets

+ +

+Les dossiers protected/runtime et assets sont deux dossiers qui doivent avoir l'autorisation "en écriture" pour le serveur Web. Le dossier runtime 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 assets contient les ressources qui doivent être publiques (ie: les images, les fichiers javascript). +

+ + +Il n'y a aucun souci à supprimer les dossiers et les fichiers contenus dans protected/runtime et assets. Il est recommandé aux développeurs de nettoyer ces dossiers lors d'une mise à jour de PRADO. + + + +

Le dossier pages

+ +

+The pages directory is the root page directory holding all pages in a PRADO application. It bears an analogy to the htdocs directory for the Apache httpd Web server. +

+ +

+Nous avons déjà vu comment accéder la page d'accueil. Pour accéder à n'importe quelle page situé dans le dossier pages, il faut utiliser l'URL suivante http://hostname/blog/index.php?page=chemin.vers.NomdelaPage. En fonction de cette URL, PRADO recherche une page dénommée NomdelaPage dans le dossier pages/chemin/vers. L'URL que nous avons utilisée précédemment pour accéder à la page d'accueil correspond à http://hostname/blog/index.php?page=Home. + + +

Personnalisation

+ +

+Il est tout à fait possible de personnaliser le nom et l'emplacement des fichiers et dossiers décrit précédemment. +

+ +

+Par exemple, pour améliorer la sécurité, certains pourraient désirer déplacer la totalité du dossier protected à 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 : +

+ + +$application = new TApplication( 'path/to/protected' ); + + +

+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 application.xml de cette manière : +

+ + + + + + + + + + + +

+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. +

+ +
\ 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 @@ + + +

Partager les modèles de gabarit

+ +

+Dans cette section, nous allons utiliser la fonctionnalité gabarit principal/contenu 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é gabarit principal/contenu nous permets de traiter les parties communes comme un contrôle qui centralise la logique applicative et la présentation de chaque page. +

+ + +Il est aussi possible de partager les parties communes grâce à l'inclusion de gabarits, 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. + + + +

Création du gabarit principal

+ +

+Nous allons maintenant créer le gabarit principal MainLayout qui représente les parties communes partagées par toutes nos pages. Le contrôle MainLayout est un contrôle de gabarit qui hérite de TTemplateControl. Il a besoin d'un fichier de gabarit MainLayout.tpl et d'un fichier de classe MainLayout.php situés dans le même dossier. Pour faciliter la maintenance, nous allons créer le nouveau dossier protected/layouts pour les accueillir. +

+ + + +

+Pour le moment, MainLayout 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. +

+ + + +<com:THead /> + +<com:TForm> +
+ + + +
+<com:TContentPlaceHolder ID="Main" /> +
+ + + +
+</com:TForm> + + +
+ +

+Ci-dessus, le contenu du fichier de gabarit MainLayout.tpl. Trois nouvelles balises sont utilisées. +

+ + + + +

+Le fichier de classe MainLayout.php est très simple : +

+ + + + + + +L'extension des fichiers de gabarit est .page, tandis que pour les gabarits autres que les pages c'est .tpl. Ceci permet de différencier les pages des autres contrôles. Les deux utilisent la même syntaxe de gabarit. Pour les pages, le fichier de classe est optionnel (par défaut hérite de TPage), 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. + + +

Utilisation du gabarit principal

+

+Pour utiliser notre gabarit principal nouvellement créé, nous allons modifier nos fichiers Home.page et Contact.page. 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 MainLayout. +

+ +

+Ci-dessous, le contenu de Contact.page après les modifications : +

+ + +<%@ MasterClass="Application.layouts.MainLayout" Title="Mon blog - Contact" %> + +<com:TContent ID="Main"> + +

Contact

+

Veuillez remplir le formulaire suivant pour me laisser vos impressions au sujet de mon blog. Merci !

+ +...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... + +<com:TButton Text="Envoyer" OnClick="submitButtonClicked" /> + +</com:TContent> +
+ +

+Le contenu entre les balises <com:TContent> sera inséré dans l'emplacement réservé par <com:TContentPlaceHolder> dans le gabarit principal. +

+ + +Il est possible d'avoir plusieurs TContentPlaceHolder dans un gabarit principal et plusieurs TContent dans un fichier de contenu. Ils sont associés par leurs propriétés ID. Il est aussi possible de définir un contenu comme étant le gabarit principal d'un autre contenu, ceci en plaçant une balise TContentPlaceHolder à l'endroit désiré. Ceci est appelé gabarits principaux imbriqués + + +

+A côté de la balise <com:TContent>, nous avons vu une nouvelle balise <%@ %>, qui est dénommé une balise de contrôle de gabarit. 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 Contact. +

+ +

+En définissant la propriété MasterClass comme étant de type Application.layouts.MainLayout, nous avons indiqué à la page Contact d'utiliser MainLayout comme gabarit principal. Ici, nous avons utilisé un espace de noms pour nous référer à la classe MainLayout. +

+ + +Les espaces de noms sont largement utilisés en programmation PRADO. Ils sont utilisés conjointement avec les alias de chemins. PRADO définit deux alias de chemins: System fait référence au dossier framework de l'installation PRADO, et Application fait référence au dossier protected. +L'espace de noms Application.layouts.MainLayout peut ainsi être traduit par protected/layouts/MainLayout ce qui est précisément le nom du fichier (sans l'extension .php) de la classe MainLayout. + + + +

Autres possibilités pour spécifier le gabarit principal

+ +

+Il y a plusieurs alternatives pour spécifier le gabarit principal. +

+ +

+Vous pouvez définir le gabarit principal comme ci-dessous pour pouvoir en changer dynamiquement. +

+ + +MasterClass='Path.To.NewLayout'; + } + + // ... +} +?> + + +

+Ci-dessus, nous indiquons d'utiliser le gabarit principal MasterClass dans la méthode onPreInit() qui est héritée de TPage. 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é. +

+ +

+Nous pouvons aussi spécifier quel gabarit principal utiliser dans le fichier de configuration de l'application ou encore dans le fichier de configuration de la page. Ci-dessous, le fichier de configuration de l'application modifié pour notre blog. +

+ + + + + + + + + + + + + + +

+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. +

+ + +Il y a un ordre qui permet de savoir quel fichier gabarit principal utiliser s'il est spécifié à plusieurs endroits. En particulier onPreInit() 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 MainLayout dans le fichier de configuration de l'application/page et que vous spécifiez SpecialLayout dans Contact.page, ce sera le dernier qui sera pris en compte. + + +
\ 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day1/fr/directories.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day1/fr/directories2.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day1/fr/directories3.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day1/fr/output.gif 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 @@ + + +

Connexion à la base

+ + +

+Pour pouvoir utiliser la base de données que nous venons de créer, nous devons tout d'abord établir une connexion. +

+ +

+Nous allons utiliser Data Access Objects (DAO) 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. +

+ + +Pour utiliser DAO, nous devons installer et activer la librairie PHP PDO extension ainsi que 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 php.ini les lignes suivantes: + +extension=php_pdo.dll +extension=php_pdo_sqlite.dll + + + +

+Pour une meilleure abstraction de notre base de données, nous utilisons aussi la fonctionnalité Active Record (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. +

+ +

+Nous modifions notre fichier de configuration de l'application protected/application.xml en insérant les lignes suivantes, qui indiquent à Active Record comment se connecter à notre base de données. +

+ + + + + + + + + + +

+Dans la configuration précédente, nous avons ajouté deux modules à notre application. Le module TDataSourceConfig est configuré avec la chaine de connexion sqlite:protected/data/blog.db qui pointe vers notre base de données. Cette connexion est utilisée par le module TActiveRecordConfig qui est requis pour l'utilisation d'Active Record. +

+ + +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 documentation Active Record. Il est, de même possible, d'établir une connexion à une base de données en utilisant du code PHP au travers du composant TDbConnection. + + +
\ 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 @@ + + +

Création des classes Active Record

+ +

+Nous avons besoin de définir deux classes Active Record, UserRecord et PostRecord, qui représentent respectivement les tables users et posts. Les classes Active Record doivent hériter de la classe ActiveRecord, et doivent définir les propriétés qui correspondent aux champs de la table correspondante. +

+ +

+Pour une meilleure organisation de notre arborescence, nous créons un nouveau dossier protected/database 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 protected/database à notre chemin d'inclusion de PHP (include_path). Cela nous permet d'utiliser nos classes sans avoir besoin de les inclure explicitement. +

+ + + + + + + +

+Au lieu de créer nos classes manuellement, nous allons utiliser les outils en ligne de commande de PRADO pour qu'il nous génère les classes. +

+ +

+Dans le dossier blog, lancer la commande suivante pour entrer dans le mode interactif de l'outil en ligne de commande: +

+ + +php path/to/prado-cli.php shell . + + +

+Vous devriez voir +

+ + +Command line tools for Prado 3.1.0. +** Loaded PRADO appplication in directory "protected". +PHP-Shell - Version 0.3.1 +(c) 2006, Jan Kneschke + +>> use '?' to open the inline help + +>> + + +

+A l'invite de commande, entrer les deux commandes suivantes pour créer les classes UserRecord et PostRecord. +

+ + +>> generate users Application.database.UserRecord + +>> generate posts Application.database.PostRecord + + +

+Ici, nous avons utilisés les espaces de noms pour indiquer où les classes seront créées. Le chemin Application.database.UserRecord indique que nous désirons que la classe soit insérée dans protected/database/UserRecord.php. +

+ +

+Nous devrions voir l'arborescence suivante ainsi que nos deux nouveaux fichiers dans le dossier protected/database: +

+ + + +

+Si vous consultez le fichier PostRecord, vous devriez voir le contenu suivant: +

+ + +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); + } +} + + +

+Comme vous pouvez le constater, pour chaque champs de la table posts, la classe déclare un membre correspondant. La constante TABLE indique le nom de la table que gère la classe PostRecord. La méthode statique finder() permet d'effectuer des requêtes et de lire les données sous forme d'objets PostRecord. +

+

+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 UserRecord::finder()->findAll(). +

+ + +>> 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() + ) +) + + +

Relations entre Posts et Users

+

+Rappellez-vous qu'il y a une relation entre les tables users et posts. Le diagramme des relations est indiqué ci-après. +

+ + + +

+A partir de ce diagramme, nous voyons que la table posts contient un champ author_id. Le champ author_id est une clé étrangère qui référence la table users. En particulier, les valeurs du champ author_id doivent apparaitre dans dans le champ username de la table users. 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". +

+ +

+Nous pouvons modéliser les relations entre posts and users dans nos classes PostRecord and UserRecord en les modifiant comme ci-dessous : +

+ + +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'), + ); +} + + +

+La propriété statique $RELATIONS de la classe PostRecord définit que la propriété $author fait référence à un UserRecord. Dans le tableau : array(self::BELONGS_TO, 'UserRecord'), le premier élément défini le type de relation, dans notre cas, self::BELONGS_TO. Le deuxième élément est le nom de l'objet en relation, dans notre cas UserRecord. La classe UserRecord est définie comme ci-dessous, la différence est que chaque objet UserRecord contient plusieurs PostRecord. +

+ + +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'), + ); +} + + +

+Un tableau de UserRecord ainsi que les messages correspondants peuvent être lu de la manière suivante : +

+ + +$users = UserRecord::finder()->withPosts()->findAll(); + + + +La méthode withXXX() (avec XXX qui est le nom de la propriété de la relation, dans notre cas Posts) lit les données correspondantes de PostRecords en utilisant une deuxième requête (mais pas en utilisant une jointure). La méthode withXXX() accepte les mêmes arguments que les autres méthodes finder de l'objet Active record, ie : withPosts('status = ?', 0). + + +

+Plus d'informations sont disponibles dans le manuel de démarrage rapide Active Record. +

+ +
\ 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 @@ + + +

Création de la base

+< +

+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 database access object (DAO) et Active Record. +

+ +

+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 users et posts, comme décrit dans le diagramme ci-après. +

+ + + +

+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 protected/schema.sql. +

+ + +/* 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); + + + +La contrainte fk_author est ignorée par SQlite vu que SQLite ne supporte pas les clés étrangères. +Néanmoins, nous gardons cette contrainte dans le but de pouvoir porter notre blog sur d'autres SGBD. Par ailleurs, nous utilisons la capacité auto incrémenté du champ posts.post_id si celui-ci est renseigné à NULL lors de l'insertion. + + +

+Nous utilisons après ceci, les outils en ligne de commande SQLite pour créer la base de données. Nous avons créé un dossier protected/data qui contiendra le fichier de base de données. Nous lançons maintenant la ligne de commande suivante dans le dossier protected/data. +

+ + +sqlite3 blog.db < ../schema.sql + + +

+La base de données est ainsi créée dans le fichier protected/data/blog.db et nous pouvons maintenant voir la l'arborescence suivante : +

+ + + + +Il est nécessaire pour SQLite que le dossier protected/data et le fichier protected/data/blog.db soient accessibles en écriture par le serveur Web. + + +
\ 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day2/fr/ER.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day2/fr/ER.vsd 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day2/fr/directories.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day2/fr/directories2.gif 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 @@ + + +

Authentification et Autorisation

+ + +

+Avant que nous n'implémentions la gestion des utilisateurs, nous devons activer les modules authentification et autorisation. +

+ + +

+Nous ajoutons deux nouveaux modules à notre fichier de configuration de l'application. +

+ + + + ...modules TDataSourceConfig et TActiveRecordConfig ... + + + + + + + +

+Le module TAuthManager gère le processus d'authentification et d'autorisation. Il utilise le module users comme gestionnaire d'utilisateur (voir ci-après). En spécifiant la propriété LoginPage, nous indiquons au module d'authentification de rediriger vers la page LoginUser quand il est nécessaire de s'authentifier. Nous décrirons comment créer la page LoginUser ci-après. +

+ +

+Le module user est une classe de type TDbUserManager 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é UserClass est initialisée comme étant de type Application.BlogUser, ceci indique au module user de chercher une classe BlogUser dans le dossier protected (rappellez-vous que l'alias Application fait référence au dossier protected) et d'utiliser cette classe pour conserver les données utilisateurs dans une session. +Le module user est une classe de type TDbUserManager 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é UserClass est initialisée comme étant de type Application.BlogUser, ceci indique au module user de chercher une classe BlogUser dans le dossier protected (rappellez-vous que l'alias Application fait référence au dossier protected) et d'utilisez cette classe pour conserver les données utilisateurs dans une session. +

+ +

+Comme vous pourrez le constater dans les sections suivantes, dans les différents contrôles et pages, nous pourrons utiliser $this->User pour accéder à l'objet qui contient les informations de l'utilisateur actuellement connecté. +

+ + +

+Ci-dessous les détails de l'implémentation de la classe BlogUser. Remarquez que les Active Record sont utilisés pour exécuter une requête. Par exemple, nous utilisons UserRecord::finder()->findByPk($username) pour chercher la valeur de $username dans la table users et ceci par la clé primaire. +

+ + +// 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'); + } +} + + +
\ 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 @@ + + +

Création de la page d'administration des utilisateurs AdminUser

+ +

+La page AdminUser 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. +

+ + +

+Nous allons lister les utilisateurs dans une table HTML. Chaque ligne correspondra à un compte utilisateur, les colonnes suivantes seront affichées: +

+ + + +

+Nous créons deux fichiers protected/pages/users/AdminUser.page et protected/pages/users/AdminUser.php qui contiendront respectivement le gabarit et la classe. +

+ +

Création du gabarit

+

+Nous allons utiliser un contrôle TDataGrid pour afficher les données. Suivant l'analyse précédente, nous allons configurer quatre colonnes: +

+ + + + +

Le gabarit complet est affiché ci-après:

+ + + +<%@ Title="Mon Blog - Administration des comptes utilisateurs" %> + +<com:TContent ID="Main"> + +

Administration des comptes utilisateurs

+ +Créer un nouvel utilisateur +
+ +<com:TDataGrid ID="UserGrid" + DataKeyField="username" + AutoGenerateColumns="false" + OnDeleteCommand="deleteButtonClicked"> + + <com:THyperLinkColumn + HeaderText="Pseudo" + DataTextField="username" + DataNavigateUrlField="username"> + <prop:DataNavigateUrlFormatString># + $this->Service->constructUrl('users.EditUser',array('username'=>{0})) + </prop:DataNavigateUrlFormatString> + </com:THyperLinkColumn> + + <com:TBoundColumn + HeaderText="Email" + DataField="email" /> + + <com:TCheckBoxColumn + HeaderText="Administrateur" + DataField="role" /> + + <com:TButtonColumn + HeaderText="Commande" + Text="Supprimer" + ButtonType="PushButton" + CommandName="delete" /> + +</com:TDataGrid> + +</com:TContent> +
+ + +

Création du fichier de classe

+ + +

+Dans le gabarit précédent, le bouton OnDeleteCommand déclenche l'évènement deleteButtonClicked() 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 : +

+ + +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); + } +} + + +

+Dans le code précédent, la méthode deleteButtonClicked() est appelée quand on clique sur le bouton "Supprimer". Pour savoir à quelle ligne appartenait le bouton, nous utilisons la propriété Item.ItemIndex 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é DataKeys. +Dans le code précédent, la méthode deleteButtonClicked() est appelée quand on clique sur le bouton "Supprimer". Pour savoir à quelle ligne appartenait le bouton, nous utilisons la propriété Item.ItemIndex 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é DataKeys. +

+ + +Tous les contrôles liés sont basé sur le même modèle. C'est à dire, définition de la propriété DataSource pour savoir d'où proviennent les données et appel à la méthode dataBind() pour effectivement lier les données au contrôle. + + +

Ajout de la vérification des droits d'accès

+

+Vu que seuls les administrateurs doivent pouvoir accéder à la page AdminUser, nous devons modifier notre fichier de configuration de page protected/pages/users/config.xml. +

+ + + + + + + + + + + +

Test

+ +

+Pour tester notre page AdminUser, nous naviguons à l'adresse http://hostname/blog/index.php?page=users.AdminUser. 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. +

+ + + +
\ 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 @@ + + +

Création de la page de mise à jour des utilisateurs EditUser

+ +

+La page EditUser ressemble beaucoup à la page NewUser. La différence principale est le fait que lorsque la page EditUser est requise, les champs sont renseignés avec les données de l'utilisateur en cours. Une autre différence est que la page EditUser est accessible à un utilisateur standard. +

+ +

+Pour définir quels sont les comptes qui peuvent être modifiés, nous allons utiliser les règles suivantes : +

+ + +

+Nous créons deux fichiers protected/pages/users/EditUser.page et protected/pages/users/EditUser.php qui contiendront respectivement le gabarit et la classe. +

+ +

Création du gabarit

+p> +Comme vous avez pu le deviner, la page EditUser est fortement ressemblante à la page NewUser. En dehors du titre de la page et du bouton "envoyer", il y a trois différences principales. +

+ + + + + +<%@ Title="Mon blog - Mise à jour utilisateur" %> + +<com:TContent ID="Main"> + +

Mise à jour utilisateur

+ +Pseudo: +<com:TLabel ID="Username" /> + +
+Mot de passe: +
+<com:TTextBox ID="Password" TextMode="Password" /> + +
+Confirmation mot de passe: +<com:TCompareValidator + ControlToValidate="Password" + ControlToCompare="Password2" + ErrorMessage="Différence entre le mot de passe et la confirmation." + Display="Dynamic" /> +
+<com:TTextBox ID="Password2" TextMode="Password" /> + +
+Email: +<com:TRequiredFieldValidator + ControlToValidate="Email" + ErrorMessage="Veuillez indiquer votre email." + Display="Dynamic" /> +<com:TEmailAddressValidator + ControlToValidate="Email" + ErrorMessage="Vous avez indiqué un mot de passe invalide." + Display="Dynamic" /> +
+<com:TTextBox ID="Email" /> + +<com:TControl Visible="<%= $this->User->IsAdmin %>"> +
+Role: +
+<com:TDropDownList ID="Role"> + <com:TListItem Text="Utilisateur standard" Value="0" /> + <com:TListItem Text="Administrateur" Value="1" /> +</com:TDropDownList> +</com:TControl> + +
+Prénom: +
+<com:TTextBox ID="FirstName" /> + +
+Nom: +
+<com:TTextBox ID="LastName" /> + +
+<com:TButton Text="Enregistrer" OnClick="saveButtonClicked" /> + +</com:TContent> +
+ + +

Création du fichier de classe

+ + +

+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 saveButtonClicked() appelée par l'évènement OnClick du bouton "Enregistrer". +

+ + +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; + } +} + + + +La méthode onInit() est appelée par PRADO lors du cycle de vie de la page. Les autres méthodes couramment surchargées sont onPreInit(), onLoad() et onPreRender(). + + +

Test

+ +

+Pour tester la page EditUser, rendons-nous à l'URL http://hostname/blog/index.php?page=users.EditUser&username=demo. 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. +

+ + +
\ 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 @@ + + +

Création de la page LoginUser

+ +

+La page LoginUser affiche un formulaire et gère l'authentification de l'utilisateur. Comme décrit dans authentification et autorisation, le navigateur est automatiquement redirigé vers la page LoginUser quand un utilisateur essaye d'accéder à une page protégée, telle que la page d'administration des utilisateurs. +

+ +

+Le processus de la page LoginUser est similaire à celui de la page Contact: +

+
    +
  1. Quand un utilisateur accède à la page LoginUser, un formulaire est affiché;
  2. +
  3. L'utilisateur remplit les champs, nom de l'utilisateur et mot de passe et clique sur le bouton "envoyer";
  4. +
  5. La classe LoginUser reçoit l'évènement "login" et lance la séquence d'authentification;
  6. +
  7. 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é. +
+ +

+Nous créons les deux fichiers protected/pages/users/LoginUser.page et protected/pages/users/LoginUser.php qui enregistre le gabarit et la classe respectivement. +

+ +

Création du gabarit

+ +

+Ci-après est affiché le gabarit pour LoginUser. 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 TRequiredFieldValidator contrôle. La validité du mot de passe est assurée par le validateur TCustomValidator qui fait un appel à la méthode validateUser() de la classe. La page contient aussi un bouton "envoyer" qui fait un appel à loginButtonClicked() quand il est activé. +

+ + +<%@ Title="My Blog - Login" %> + +<com:TContent ID="Main"> + +

Connexion

+ +Votre nom: +<com:TRequiredFieldValidator + ControlToValidate="Username" + ErrorMessage="Veuillez indiquer votre nom." + Display="Dynamic" /> +
+<com:TTextBox ID="Username" /> + +
+Mot de passe: +<com:TCustomValidator + ControlToValidate="Password" + ErrorMessage="vous avez saisi un mot de passe invalide." + Display="Dynamic" + OnServerValidate="validateUser" /> +
+<com:TTextBox ID="Password" TextMode="Password" /> + +
+<com:TButton Text="Envoyer" OnClick="loginButtonClicked" /> + +</com:TContent> +
+ +

Création de la classe

+ +

+Tout comme la page Contact, la page LoginUser 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 : validateUser() et loginButtonClicked(). Dans validateUser(), nous utilisons le gestionnaire d'authentification 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. +

+ + +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 OnServerValidate du validateur TCustomValidator. + * @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 OnClick 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); + } + } +} + + + +

Test

+ +

+Nous avons donc créé la page LoginUser. Nous pouvons la tester en naviguant à l'URL http://hostname/blog/index.php?page=users.LoginUser. Rappellez-vous que la dans la section Création de la base, nous avons déjà créé deux comptes utilisateurs (nom d'utilisateur/mot de passe) admin/demo et demo/demo. Nous pouvons donc les utiliser pour tester notre page de connexion. +

+ + + +

Ajout des liens de connexion/déconnexion à notre gabarit principal

+

+Pour permettre à l'utilisateur d'accéder directement aux pages de connexion/déconnexion, nous modifions le gabarit principal MainLayout. En particulier, nous ajoutons un lien vers la page LoginUser. Nous ajoutons aussi un lien "se déconnecter" qui permet à l'utilisateur de se déconnecter. +

+ +

+Nous modifions le pied de page de notre gabarit principal MainLayout. 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: $this->User->IsGuest 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é. +

+ + + + + +

+Vu que le lien "se déconnecter" génère l'évènement OnClick avec comme nom d'évènement logoutButtonClicked(), nous devons modifier le fichier de classe de MainLayout comme ci-dessous : +

+ + +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); + } +} + + +

+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. +

+ +
\ 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 @@ + + +

Création de la page nouvel utilisateur NewUser

+ +

+La page NewUser 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 base de données, nous devons prévoir la saisie des informations suivantes : +

+ + + +

+Nous créons deux fichiers, protected/pages/users/NewUser.page et protected/pages/users/NewUser.php qui contiendront respectivement le gabarit et la classe. +

+ +

Création du gabarit

+

+En fonction de l'analyse précédente, nous créons le gabarit comme ci-dessous : +

+ + +<%@ Title="Mon Blog - Nouvel utilisateur" %> + +<com:TContent ID="Main"> + +

Création nouvel utilisateur

+ +Pseudo: +<com:TRequiredFieldValidator + ControlToValidate="Username" + ErrorMessage="Veuillez indiquer un pseudo." + Display="Dynamic" /> +<com:TCustomValidator + ControlToValidate="Username" + ErrorMessage="Désolé, le pseudo choisi est déjà utilisé. Veuillez en saisir un autre." + OnServerValidate="checkUsername" + Display="Dynamic" /> +
+<com:TTextBox ID="Username" /> + +
+Mot de passe: +<com:TRequiredFieldValidator + ControlToValidate="Password" + ErrorMessage="Veuillez indiquer un mot de passe." + Display="Dynamic" /> +
+<com:TTextBox ID="Password" TextMode="Password" /> + +
+Confirmation mot de passe: +<com:TCompareValidator + ControlToValidate="Password" + ControlToCompare="Password2" + ErrorMessage="Différence entre le mot de passe et la confirmation." + Display="Dynamic" /> +
+<com:TTextBox ID="Password2" TextMode="Password" /> + +
+Email: +<com:TRequiredFieldValidator + ControlToValidate="Email" + ErrorMessage="Veuillez indiquer votre email." + Display="Dynamic" /> +<com:TEmailAddressValidator + ControlToValidate="Email" + ErrorMessage="Vous avez indiqué un mot de passe invalide." + Display="Dynamic" /> +
+<com:TTextBox ID="Email" /> + +
+Rôle: +
+<com:TDropDownList ID="Role"> + <com:TListItem Text="Utilisateur standard" Value="0" /> + <com:TListItem Text="Administrateur" Value="1" /> +</com:TDropDownList> + +
+Prénom: +
+<com:TTextBox ID="FirstName" /> + +
+Nom: +
+<com:TTextBox ID="LastName" /> + +
+<com:TButton Text="Ajouter" OnClick="createButtonClicked" /> + +</com:TContent> +
+ +

+Le gabarit est très proche du gabarit de la page Contact et de la page LoginUser. 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. +

+ + +

Création du fichier de classe

+ +

+En fonction du gabarit précédent, nous constatons que nous avons besoin d'une classe qui implémente deux gestionnaires d'évènements : checkUsername() (appellé par le premier validateur dans l'évènement OnServerValidate) et createButtonClicked() (appellé par l'évènement OnClick du bouton "create"). ainsi, nous écrirons la classe comme ci-dessous : +

+ + +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); + } + } +} + + +

+Dans le code précédent, l'appel à la méthode save() insère un enregistrement dans la table users. Cette fonctionnalité est fournie par l'objet Active Record. +

+ + +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 authentification. 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 authentification. 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 authentification. De même les espaces en début et fin de pseudo devrait être supprimés. + + + +

Test

+

+Pour tester la page NewUser, il suffit de naviguer à l'URL http://hostname/blog/index.php?page=users.NewUser. 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. +

+ + + + +

Ajout de la vérification des droits d'accès

+

+Durant le test, vous vous êtes peut-être demandé : Est-ce que la page NewUser ne devrait être accessible qu'aux administrateurs ? Oui, ceci est dénommé autorisation. Nous allons maintenant décrire comment ajouter cette vérification d'accès à la page NewUser. +

+ +

+Une façon simple serait de vérifier dans le code de la classe si $this->User->IsAdmin est vrai, dans le cas contraire, une redirection vers la page de connexion LoginUser serait faite. +

+ +

+PRADO propose une approche complémentaire de vérification des droits. Pour ce faire, nous devons utiliser un fichier de configuration de page. Créer un fichier protected/pages/users/config.xml avec le contenu suivant : +

+ + + + + + + + + + + +

+Le fichier de configuration de page contient les règles d'accès aux pages contenues dans le dossier protected/pages/users. Ce fichier indique que la page NewUser peut être vue par les utilisateurs dont le rôle est "admin" (concernant le mot "admin" voir BlogUser.createUser()), et toutes les autres pages de ce dossier interdites aux utilisateurs anonymes (users="?" signifie utilisateur anonyme). +

+ +

+Dorénavant, si nous naviguons à la page NewUser en tant qu'anonyme, nous serons redirigés vers la page LoginUser. Si notre connexion est acceptée, nous serons redirigés en retour vers la page NewUser +

+ + +Le fichier de configuration de pages peut contenir d'autres éléments que les règles d'autorisations. Par exemple, il pourrait inclure un module tout comme nous l'avons fait pour le fichier de configuration de l'application. 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. + + +
\ 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 @@ + + +

Vue d'ensemble de la gestion des utilisateurs

+ + +

+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. +

+ + +

+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 protected/pages/users. +

+ + + + + +

+Après avoir fini cette section, nous devrions obtenir l'arborescence suivante : +

+ + + + +
\ 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day3/fr/directories.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day3/fr/output.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day3/fr/output2.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day3/fr/output3.gif 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 @@ + + +

Création de la page modification d'un message EditPost

+ + +

+La page EditPost est fournie aux auteurs et administrateurs pour modifier les messages. Comme la page NewPost, elle affiche un formulaire permettant de modifier les données d'un message. +

+ + +

+Nous créons deux fichiers protected/pages/posts/EditPost.page et protected/pages/posts/EditPost.php contenant respectivement le gabarit et la classe de notre page. +

+ +

Création du gabarit

+

+Le gabarit de la page EditPost est très proche de celui de la page NewPost template. Seul le titre et le texte du bouton sont différents. +

+ + +<%@ Title="Mon Blog - Modification Message" %> + +<com:TContent ID="Main"> + +

Modification message

+ +Titre: +<com:TRequiredFieldValidator + ControlToValidate="TitleEdit" + ErrorMessage="Veuillez indiquer un titre." + Display="Dynamic" /> +
+<com:TTextBox ID="TitleEdit" Columns="50" /> + +
+Message: +<com:TRequiredFieldValidator + ControlToValidate="ContentEdit" + ErrorMessage="Veuillez indiquer le contenu du message." + Display="Dynamic" /> +
+<com:THtmlArea ID="ContentEdit" /> + +
+<com:TButton Text="Enregistrer" OnClick="saveButtonClicked" /> + +</com:TContent> +
+ + +

Création du fichier de classe

+ + +

+La classe de EditPage est un peu plus complexe que celle de la page NewPage 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. +

+ + +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; + } +} + + +

Test

+

+Pour tester notre page EditPost, authentifiez-vous auparavant et allez à l'URL http://hostname/blog/index.php?page=EditPost&id=1. Cette URL peut aussi être atteinte par le bouton Modifier de notre page de détail. +

+ + + +
\ 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 @@ + + +

Création de la page d'affichage des messages ListPost

+ +

+La page ListPost affiche les derniers messages sous forme de liste. S'il y a trop de messages, ils seront affichés dans différentes pages. +

+ +

+Avant que nous ne passions à l'implémentation, nous voudrions que notre page d'accueil pointe vers la page à venir ListPost, 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 protected/application.xml de cette manière. +

+ + +...... + + + + + + + +

+Nous alons maintenant créer le gabarit et le fichier de classe pour notre page ListPost : protected/pages/posts/ListPost.page et protected/pages/posts/ListPost.php. +

+ +

Création du gabarit

+

+Pour satisfaire les fonctionnalités de notre page ListPost, nous allons utiliser deux contrôles dans notre gabarit. +

+ +

+Ci-dessous le contenu du gabarit : +

+ + +<%@ Title="Mon Blog" %> + +<com:TContent ID="Main"> + +<com:TRepeater ID="Repeater" + ItemRenderer="Application.pages.posts.PostRenderer" + AllowPaging="true" + AllowCustomPaging="true" + PageSize="5" + /> + +<com:TPager ControlToPaginate="Repeater" OnPageIndexChanged="pageChanged" /> + +</com:TContent> + + +

+Dans la partie répétée TRepeater, nous indiquons que l'affichage du contenu est délégué à l'élément PostRenderer que nous allons créer après. Pour permettre à PRADO de trouver cette classe, nous fournissons l'espace de noms complet Application.pages.posts.PostRenderer, qui correspond au fichier protected/pages/posts/PostRenderer.php. +

+ +

+Nous définissons aussi quelques propriétés complémentaires du TRepeater pour activer la pagination. Et nous définissons la propriété ControlToPaginate du TPager afin qu'il sache quelle est la zone répetée à paginer. +

+ + +

Création du fichier de classe

+

+En fonction du gabarit précédent, nous pouvons voir que notre fichier de classe doit implémenter un gestionnaire d'évènement pour pageChanged() (déclenché par OnPageIndexChanged du TPager). Nous devons aussi remplir les données qui apparaitront dans le TRepeater. Ci-dessous le source complet du fichier de classe : +

+ + +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); + } +} + + +

Création du PostRenderer

+ +

+Nous devons toujours créer la classe PostRenderer. Elle définit la manière dont sera affichée chaque ligne de notre TRepeater. 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 PostRenderer.tpl et PostRenderer.php dans le dossier protected/pages/posts. +

+

Création du gabarit pour PostRenderer

+

+Le gabarit définit la présentation des différentes informations d'un message : titre, nom, heure, contenu. Nous lions le titre à la page ReadPost qui affiche le détail du message. +

+

+L'expression $this->Data fait référence aux données provenant du TRepeater. Dans notre cas, c'est un objet de type PostRecord. Remarquez comment nous accédons au nom de l'auteur du message par $this->Data->author->username. +

+ + +
+

+<com:THyperLink Text="<%# $this->Data->title %>" + NavigateUrl="<%# $this->Service->constructUrl('posts.ReadPost',array('id'=>$this->Data->post_id)) %>" /> +

+ +

+Auteur: +<com:TLiteral Text="<%# $this->Data->author->username %>" />
+Heure: +<com:TLiteral Text="<%# date('m/d/Y h:m:sa', $this->Data->create_time) %>" /> +

+ +

+<com:TLiteral Text="<%# $this->Data->content %>" /> +

+
+
+ +

Création du fichier de classe pour PostRenderer

+ +

+Notre classe est très simple, elle hérite de TRepeaterItemRenderer et ne contient aucun autre code. +

+ +class PostRenderer extends TRepeaterItemRenderer +{ +} + + +

Test

+

+Pour tester la page ListPost, naviguons à l'URL http://hostname/blog/index.php (rappellez-vous, nous avons défini ListPost 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 NewPost, nous pourrons ajouter des messages et revenir ici pour tester notre contrôle de pagination. +

+ + + +
\ 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 @@ + + +

Création de la page nouveau message NewPost

+ +

+La page NewPost permet aux utilisateurs authentifiés de créer des nouveaux messages. Elle doit afficher un formulaire permettant la saisie des informations du message. +

+ + +

+Parce que la page NewPost ne peut être vu que par les utilisateurs authentifiés, nous ajoutons un fichier de configuration de page config.xml dans le dossier protected/pages/posts. Cette configuration indique que les invités ne peuvent voir les pages NewPost et EditPost qui sera implémentée dans la section suivante. +

+ + + + + + + + + + + +

+Vu le nombre grandissant de pages, nous allons modifier le pied de page de notre gabarit principal pour qu'il inclus des liens vers : la page d'accueil, la page nouvel utilisateur NewUser (visible seulement par les administrateurs), et la page à venir : nouveau message NewPost (visible seulement par les utilisateurs authentifiés). +

+ + + + + +

+Nous allons maintenant créer deux fichiers protected/pages/posts/NewPost.page et protected/pages/posts/NewPost.php contenant respectivement le gabarit et la classe de notre page. +

+ + +

Création du gabarit

+

+Le gabarit de NewPost contient une référence à un TTextBox pour saisir le titre de notre message et à un THtmlArea 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. +

+ + + +<%@ Title="Mon Blog - Nouveau Message" %> + +<com:TContent ID="Main"> + +

Création nouveau message

+ +Titre: +<com:TRequiredFieldValidator + ControlToValidate="TitleEdit" + ErrorMessage="Veuillez indiquer un titre." + Display="Dynamic" /> +
+<com:TTextBox ID="TitleEdit" Columns="50" /> + +
+Message: +<com:TRequiredFieldValidator + ControlToValidate="ContentEdit" + ErrorMessage="Veuillez indiquer le contenu du message." + Display="Dynamic" /> +
+<com:THtmlArea ID="ContentEdit" /> + +
+<com:TButton Text="Ajouter" OnClick="createButtonClicked" /> + +</com:TContent> +
+ + +

Création du fichier de classe

+

+Dans le gabarit précédent, nous voyons que la fonction principale de notre page est l'appel de la méthode createButtonClicked() implémenté par un évènement OnClick attaché au bouton Ajouter. +

+ + +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); + } + } +} + + +

Test

+

+Pour tester notre page NewPost, identifiez-vous auparavant et cliquez sur le lien Nouveau message dans le pied de page. Le navigateur affiche le résultat suivant avec comme URL http://hostname/blog/index.php?page=NewPost. +

+ + +Quand vous visitez la page NewPost 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 THtmlArea. Ceci est fait une fois pour toutes. + + + +Pour tester la fonctionnalité de pagination que nous avons mise en place dans la page ListPost, nous pouvons créer cinq messages ou plus et regardez ce qu'il se passe sur la page d'accueil. Le contrôle TPager de la page ListPost affiche cinq éléments par page. + + + + +
\ 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 @@ + + +

Création de la page détail d'un message ReadPost

+ +

+La page ReadPost affiche le détail d'un message. Pour les utilisateurs autorisés, un lien sera disponible permettant de modifier ou de supprimer le message. +

+ +

+Nous créons deux fichiers protected/pages/posts/ReadPost.page et protected/pages/posts/ReadPost.php qui contiendront respectivement notre gabarit et notre classe. +

+ +

Création du gabarit

+ +

+Le gabarit de ReadPost est très proche du gabarit de PostRenderer, chacun d'eux affiche le détail d'un message. La différence est que la page ReadPost doit afficher deux boutons, permettant aux utilisateurs autorisés de modifier ou supprimer le message. +

+ + + +<com:TContent ID="Main"> + +

+<com:TLiteral Text="<%= $this->Post->title %>" /> +

+ +<com:TControl Visible="<%= $this->canEdit() %>"> + Modifier | + <com:TLinkButton Text="Supprimer" + OnClick="deletePost" + Attributes.onclick="javascript:if(!confirm('Etes vous sûr ?')) return false;" /> +</com:TControl> + +

+Auteur: +<com:TLiteral Text="<%= $this->Post->author->username %>" />
+Heure: +<com:TLiteral Text="<%= date('m/d/Y h:m:sa', $this->Post->create_time) %>" /> +

+ +

+<com:TLiteral Text="<%= $this->Post->content %>" /> +

+ +</com:TContent> +
+ +

+Plusieurs expressions PHP sont utilisées dans le gabarit. L'expression $this->Post fait référence à la propriété définie dans la classe de ReadPost. Elle représente l'objet PostRecord correspondant au message actuel. +

+ + + +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 l'expression doit être une propriété ou une simple mise en forme d'une propriété. 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é. + + + +

+Nous pouvons aussi remarquer dans le gabarit précédent, que, nos deux boutons sont entourés d'un TControl dont la propriété 'visible' est déterminée par l'expression $this->canEdit(). Pour le bouton Supprimer, 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 TControl dont la propriété 'visible' est déterminée par l'expression $this->canEdit(). Pour le bouton Supprimer, nous utilisons une boite de dialogue javascript pour confirmer la suppression du message. +

+ + +Tous les contrôles PRADO, ont une propriété très utile Attributes 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 Supprimer nous définissons onclick qui est directement reporté dans la balise <a> sous forme d'un attribut onclick. + + + + +

Création du fichier de classe

+ +

+Dans le gabarit précédent, nous voyons que notre classe doit implémenter le gestionnaire d'évènement deletePost() (attaché à l'évènement Onclick de notre bouton Supprimer). Nous devons aussi lire les données du message dont l'ID est passé par un paramètre GET. +

+ + + +Nous implémentons la fonctionnalité suppression dans le classe ReadPost parce qu'il est classique de faire ainsi. Quand l'utilisateur clique sur le bouton Supprimer, une boite de dialogue demande confirmation de la suppression. Si l'utilisateur confirme, l'évènement OnClick du bouton Supprimer est déclenché. + + + + +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; + } +} + + +

Test

+

+Pour tester notre page ReadPost, allons à l'URL http://hostname/blog/index.php et cliquons sur le titre du seul message affiché. Notre navigateur devrait afficher le résultat suivant avec l'URL http://hostname/blog/index.php?page=ReadPost&id=1. Notez que si vous n'êtes pas connecté, les deux boutons ne sont pas visibles. +

+ + + +
\ 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 @@ + + +

Vue d'ensemble de la gestion des messages

+ +

+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). +

+ + +

+Nous allons créer les nouvelles pages dans le dossier protected/pages/posts créé à cet effet. +

+ + + + +

+Après avoir fini cette section, nous devrions obtenir l'arborescence suivante : +

+ + + +
\ 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day4/fr/directories.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day4/fr/output.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day4/fr/output2.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day4/fr/output3.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day4/fr/output4.gif 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 @@ + + +

Gestion et journalisation d'erreur

+ +

+Si vous tentez de naviguez à l'URL http://hostname/blog/index.php?page=EditPost&id=100, 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. +

+ + + + +Une des tâches importantes dans les applications Web est la gestion des erreurs ainsi que leurs journalisation. 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. + + + +

Personnalisation de la gestion d'erreur

+ +

+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 : +

+ + +...... + + ...... + + ...... + +...... + + +

+La classe BlogErrorHandler 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 TErrorHandler. +

+ +

+Nous créons un fichier nommé protected/BlogErrorHandler.php avec le contenu suivant. La classe BlogErrorHandler surcharge deux méthodes de TErrorHandler : +

+ + +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); + } +} + + +

+Dans le code précédent, nous spécifions que lorsqu'une exception de type BlogException est générée, nous utilisons le gabarit protected/error.html pour afficher l'erreur. Par ailleurs, nous devons créer la classe BlogException et remplacer toutes les occurrences de THttpException dans notre code (par exemple dans les pages EditUser et ReadPost). Nous devons aussi créer le gabarit protected/error.html. La classe BlogException hérite de THttpException et est vide. Le fichier de classe est enregistré sous protected/BlogException.php. +

+ + +class BlogException extends THttpException +{ +} + + +

+Ci-dessous le contenu du gabarit protected/error.html. 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 %%ErrorMessage%%, %%ServerAdmin%%. +

+ + + + +%%ErrorMessage%% + + +
+ +
+

%%ErrorMessage%%

+

+Une erreur est apparue lors du traitement de votre demande. +

+

+Si vous pensez que c'est une erreur de notre serveur, veuillez contacter webmaster. +

+
+ + + + + +

Journalisation des erreurs

+ +

+Dans la méthode handleExternalError() de BlogErrorHandler, nous appelons Prado::log() pour journaliser l'erreur si elle est de type BlogException. 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 : +

+ + +...... + + ...... + + + + ...... + +...... + + +

+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 BlogApplication, de manière à ce que seules les erreurs de type BlogApplication soient sauvegardées. Cette possibilité permet de réduire la taille du journal et d'en améliorer la lisibilité. +

+ +

Test

+

+Pour voir comme notre blog se comporte suite à une demande invalide, nous naviguons à l'URL http://hostname/blog/index.php?page=posts.ReadPost&id=100. Nous devrions voir la page suivante qui est différente de celle vue précédemment. +

+ + + +

+Si nous regardons dans le dossier protected/runtime, nous devrions y trouver un fichier nommé prado.log. C'est le journal d'erreur que nous venons juste de paramétrer. Le fichier pourrait contenir quelque chose comme : +

+ + +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. + + + \ 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 @@ + + +

Amélioration des performances

+ +

+Avant le déploiement de notre blog, nous voudrions améliorer les performances. +

+ +

Changer le mode de fonctionnement de l'application

+ +

+Une application PRADO peut-être configurée pour fonctionner suivant différents modes. Par défaut, elle fonctionne en mode Debug, 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 Debug à Normal ( qui signifie "en production"), nous devons modifier le fichier de configuration de l'application comme ci-dessous : +

+ + + + + ...... + + + +

Enabling Caching

+ +

+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, +

+ + +...... + + ...... + + ...... + +...... + + +

+Maintenant, après avoir requis n'importe quelle page de notre blog, nous devrions trouver un fichier nommé sqlite3.cache. C'est un fichier de base de données qui mémorise les éléments analysés : gabarits, configurations, etc. +

+ + +Le module de cache que nous venons d'activer utilise une base de données comme support d'enregistrement. PRADO propose d'autres modules de cache plus rapide, tels que TMemCache, TAPCCache. Ces modules requièrent les extensions PHP correspondantes. + + + +

Utilisation de pradolite.php

+ +

+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 index.php et remplaçons prado.php par pradolite.php. Ce dernier est un gros fichier incluant les fichiers PHP nécessaires et dont on a retiré les commentaires. +

+ +

Autres techniques

+ +

+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 cache est la principale solution à ce problème. Le module de cache activé dans notre fichier de configuration d'application peut aussi être utilisé dans ce but. +

+

+Pour une page relativement stable et souvent consultée, le cache de sortie doit être envisagé. Le cache de sortie met en cache les parties sélectionnées d'une page. Ceci peut améliorer les performances des pages mises en cache de manière significative. +

+ +

+Il a été prouvé que les solutions de cache 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 Zend Optimizer, 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 cache de PRADO garantissent la validité des pages fournies. +

+ +
\ 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 @@ + + +

Résumé

+ +

+Nous pouvons finalement déployer notre blog. Pour cela, nous devons juste copier le dossier blog complet vers le dossier du serveur Web. Nous pourrions avoir besoin de modifier index.php pour qu'il puisse trouver le chemin vers l'emplacement où a été installé le framework PRADO. +

+ +

+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. +

+ +

+En résumé, développer une application de gestion de base de données PRADO nécessite les étapes suivantes : +

+
    +
  1. Analyse et création de la base de données
  2. +
  3. Créer le squelette de l'application avec prado-cli
  4. +
  5. Mise en place de la gestion d'erreur pour gérer les erreurs d'utilisations
  6. +
  7. Création et mise en place du thème
  8. +
  9. Création et mise en place des gabarits principaux
  10. +
  11. Création de la connexion et des classes d'accès aux données
  12. +
  13. Création des différentes pages
  14. +
  15. Test et amélioration des performances/li> +
  16. Déploiement
  17. +
+ +

+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 THttpException par BlogException 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. +

+ +

+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. +

+ + + + +
+

Traduction (laborieuse) par Eric.M, nous pouvez me contacter par message privé ou par email sur le forum PRADO.

+
\ 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 @@ + + +

Utilisation des Thèmes et des Skins

+ +

+PRADO propose un support intrinsèque des thèmes. 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. +

+ +

Création des thèmes

+ +

+Nous devons auparavant créer un dossier themes. 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. +

+ +

+Pour créer un thème nommé Basic, nous créons un sous-dossier theme/Basic. Dans ce dossier, nous pouvons mettre des feuilles de styles dépendantes du thème, des fichiers Javascript, des images et des fichiers skins. +

+ + +Le dossier themes 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 TThemeManager dans le fichier de configuration de l'application. + + + +

Création de la feuille de style

+ +

+Dans le dossier themes/Basic, nous créons un fichier CSS nommé style.css. 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. +

+ +

+Le contenu du fichier CSS est le suivant : +

+ + +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; +} + + + +

Création du fichier de Skin

+ +

+Nous utilisons des skin pour initialiser les propriétés des contrôles PRADO. Les fichiers skin sont enregistrés avec une extension .skin dans le dossier du thème. Chaque fichier skin peut contenir plusieurs modèles pour un ou plusieurs types de contrôles. +

+ +

+Pour notre test, nous allons créer un fichier skin qui changera la couleur de fond de nos liens dans le pied de page. Nous créons un fichier nommé button.skin dans le dossier du thème themes/Basic. +

+ + +<com:THyperLink SkinID="MainMenu" BackColor="lightgreen" /> + + +

+Le fichier button.skin contient une seule définition pour les contrôles de type THyperLink dont la propriété SkinID est MainMenu. La définition applique une couleur vert-clair comme couleur de fond du contrôle. +

+ +

+En accord avec cette définition, nous modifions notre fichier protected/common/MainLayout.tpl pour appliquer aux liens du pied de page la valeur MainMenu à la propriété SkinID. +

+ +...... + +...... + + + +La syntaxe des fichiers skin est très proche de celle des gabarits. Chaque balise <com:> définit la présentation d'un type de contrôle. PRADO concatène automatiquement les fichiers skin pour un thème et applique le tout lorsque la page est affichée. + + + +

Utilisation du thème

+ +

+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 Basic (le nom du thème) à la priorité Theme pour toutes les pages. +

+ + +...... + + + + + +...... + + + +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é Theme). En dernier recours, on peut le faire dans la méthode onPreInit() de la page, ceci parce que PRADO applique le thème au début du cycle de vie de la page. + + + +

Test

+

+Pour voir la nouvelle présentation de notre site, allons à l'URL http://hostname/blog/index.php. 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. +

+ + + +
\ 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day5/fr/output.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day5/fr/output2.gif 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 Binary files /dev/null and b/demos/blog-tutorial/protected/pages/Day5/fr/output3.gif 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 @@ - +

Bienvenue dans ce tutoriel de développement d'un Blog

-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 Quickstart Tutorial. +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 Quickstart Tutorial.

-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 fonctionnalités suivantes. +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 buts à atteindre.

-Pour pouvoir suivre ce tutoriel et créer les exemples pas à pas, les lecteurs doivent avoir accès à un serveur Web qui implémente PHP 5.1.0+ et PRADO 3.1+. Les instructions pour installer PRADO peuvent être trouvés ici : pradosoft.com. +Pour pouvoir suivre ce tutoriel et créer les exemples pas à pas, les lecteurs doivent avoir accès à un serveur Web qui implémente PHP 5.1.0+ et PRADO 3.1+. Les instructions pour installer PRADO peuvent être trouvées ici : pradosoft.com.

\ 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 @@ + + +

Buts à atteindre

+

+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. +

+ +

+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. +

+ +

Gestion des utilisateurs

+
    +
  • L'outil doit permettre de gérer les utilisateurs et leurs droits.
  • +
  • L'outil doit permettre à l'administrateur de lister tous les utilisateurs.
  • +
  • L'outil doit permettre à l'administrateur de créer des nouveaux utilisateurs.
  • +
  • L'outil doit permettre à l'administrateur ou au propriétaire du compte de modifier son profil.
  • +
  • L'outil doit permettre à l'administrateur de supprimer un utilisateur.
  • +
+ +

Gestion des messages

+
    +
  • L'outil doit permettre de lister les messages par ordre de date décroissante et d'en gérer la pagination.
  • +
  • L'outil doit permettre de consulter le détail d'un message.
  • +
  • L'outil doit permettre la création d'un nouveau message par un utilisateur authentifié.
  • +
  • L'outil doit permettre la mise à jour d'un message par son auteur ou par l'administrateur.
  • +
  • L'outil doit permettre la suppression d'un message par son auteur ou par l'administrateur.
  • +
+ +

Maintenance de l'outil

+
    +
  • L'outil doit permettre de collecter les retours utilisateurs.
  • +
  • L'outil doit être assez flexible pour permettre l'intégration ultérieure de nouveaux composants.
  • +
  • L'outil doit permettre de changer le thème qui régit la présentation des composants.
  • +
+ +
\ No newline at end of file -- cgit v1.2.3