Per aspera ad astra
Home Présentation
Example of Section Blog layout (FAQ section)
FORMIDABLE PDF Imprimer Envoyer
Écrit par Administrator   
Lundi, 08 Mars 2010 04:34

FORMidable est une API pour réaliser des formulaires front-end avec TYPO3, avec la bénédiction et le soutient de Kasper, le père de TYPO3. Il est développé et maintenu par la société française AMEOS qui fournit la doc de base sur le site http://formidable.typo3.ug/ . Je met ici quelques contributions et codes qui m'ont cruellement manquées en googlisant.

Caractéristiques FORMIDABLES
  • framework de développement de formulaires avancé TYPO3
  • comporte des validator : sytème de validation de formulaire
  • différentiation affichage / fonctionnel / données (MVC)
  • intégre un template engine qui permet d'affiner l'affichage avec des conditions sur les valeurs, des transformations de texte
  • permet d'inclure du PHP, du javascript ou du HTML dans les tratements d'évenements ou d'affichage
  • gère des événements AJAX, CLIENT ou SERVEUR
  • transforme automatiquement des traitement PHP-client en javascript


Globalement comment fonctionne FORMIDABLE ? voir le site officiel http://formidable.typo3.ug/


Un hello world en FML (le Formidable Markup Language)


Comment sont construits les fichiers FORMIDABLE ? Le contenu minimum et obligatoire est ci dessus. Ensuite voici comment est construit un fichier complet, dans l'ordre des balises :

1. xml version encoding
2. formidable version
3. metas : informations d'ordre générales. Nom du formulaire, id et éventuellement des librairies supplémentaires, un event de démarrage...
4. controlers
5. le renderer : gére la méthode d'affichage, soit par STANDARD qui affiche le mise en forme par défaut (pas de HTML associé) soit templaté et dans ce cas on spécifie le HTML et permet de définir l'agencement HTML des éléments et des styles qui composent votre formulaire, indispensable dans bien des cas
6. datasource : vos ssources de données à partir de la base ou d'un tableau PHP
7. datahandler : gére la création / édition / modification de vos données en base. Instruction à faire avant / aprés insertion ou suppression d'un enregistrement.
8. sandbox : le mal nomé bac à sable est une zone de définition de variables et de méthodes accessible de n'importe ou et n'importe quand dans votre formulaire et éventuellement une fonction d'initialisation. Très utile.
9. actionslets : de type USEROBJ ou REDIRECT permet de définir des actions aprés validation correct du formulaire (créer un rapport, envoyer des email, rediriger vers une autre page...)
10. elements : vos renderlets (les éléments du formulaire)


Possibilité de FORMIDABLE à l'affichage avec les renderlets
  • ACCORDION : un accordion scriptaculous vertical pour ouvrir des lignes quand on clique dessus. C'est bô
  • AUTOCOMPLETE : un renderlet censé compléter automatiquement votre saisis, mais ne marche pas bien avec la version
  • BOX : un renderlet générique qui contient ce que vous voulez en html, et qui sert de conteneur pour les autres renderlet
  • BUTTON : un INPUT button classique
  • CAPTCHA : formulaire de gestion de captcha comme son nom l'indique..
  • CHECKBOX : une liste de cases à cocher
  • CHECKSINGLE : une unique case à cocher
  • CHOOSER : un file browser pour aller chercher un fichier
  • DATE : affiche et formate bien une date depuis une datasource, ou permet d'en saisir une avec un petit calendrier
  • DEWPLAYER : un player de media
  • FILE : pour uploder des fichers
  • HIDDEN :
  • I18N : pour la gestion linguistique
  • IMAGE : affiche une image
  • LINK : un bête lien <a href="/"> mais permet aussi de calculer le href
  • LISTBOX : une selector qui peut être multiple (menu déroulant)
  • LISTER : un renderlet de haut niveau qui permet de lister des éléments d'une datasource et de les gérer à travers des colonnes
  • MODALBOX : une fenêtre en sur-impression sur la fenêtre existante grisant ce qu'il y a derrière. Attention à la compatibilité IE...
  • PASSTHRU : pour transmettre des valeurs un peu comme hidden
  • PWD : permet de faire un champ de saisie qui cache les caractères. Attention les caractères sont effacés quand on focus et unFocus sur input du même formulaire, peut être pénalisant.
  • RADIOBUTTON : des boutons radio
  • SEARCHFORM : un renderlet de haut niveau qui permet de définir des champs de recherche
  • SELECTOR : l'input "à la backend TYPO3" avec deux champs : la liste des éléments dispo à droite et ceux sélectionnés à gauche, avec des boutons pour trier la liste sélectionner et supprimer des éléments
  • SUBMIT : un bouton submit
  • SWFUPLOAD :
  • TABPANEL :
  • TEXT : un champs de texte court en input ou output
  • TEXTAREA : un grand champ de texte
  • TICKER :
  • TINYMCE : un RTE pour la saisir de vos formulaires
  • UPLOAD :
  • URL :

Validations, exécutions, évenements


* les validators : qui permettent de définir au sein d'un renderlet des critère de validité (validation de formulaire)
* les temps d'exécution (ou checkpoint) qui définissent pour vos évenement le moment ou ils sont exécutés dans le process formidable :

1. start,
2. before-compilation,
3. after-compilation,
4. before-init,
5. before-init-renderer,
6. after-init-renderer,
7. before-init-renderlets,
8. after-init-renderlets,
9. before-init-datahandler,
10. after-init-datahandler,
11. after-init,
12. before-render,
13. after-validation,
14. after-render,
15. end


* les évenements peuvent être de 3 types : CLIENT / SERVER / AJAX . Les event client sont convertis en js (mais on peut les créer en php) les server reload la page et les AJAX ne reload pas mais permettent un dialogue serveur (l'avantage d'AJAX quoi...)


Voici un gros exemple de formulaire de saisie :


<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<formidable version="2.0.421">

 <meta>
 <name>fiche-patho</name>
 <form formid="fiche-patho"/>
 <displaylabels>true</displaylabels>
 <debug>false</debug>
 <exportStyles>false</exportStyles>
 </meta>

 <control>
 <renderer:TEMPLATE>
 <template>
 <path>fileadmin/templates/formidable/FICHES/fiche-pathologie.html</path>
 <subpart>###fiche###</subpart>
 </template>
 </renderer:TEMPLATE>
 
 <datahandler:DB>
 <tablename>tx_bdd_pathologies</tablename>
 <keyname>uid</keyname>
 <process>
 <beforeCreation>
 <userobj>
 <php><![CDATA[
 
 $aData = $this->getParams();
 $aData["hidden"] = "1";
 $aData["crdate"] = time();
 $aData["tstamp"] = time();
 $aData["pid"] = 63;
 $aData["cruser_id"] = $GLOBALS["TSFE"]->fe_user->user['uid'];

 return $aData;
 
 ]]></php>
 </userobj>
 </beforeCreation>

 <afterCreation>
 <userobj>
 <php><![CDATA[/*<?php*/
 
 $this->oSandBox->message = "La pathologie a bien été créée. Elle sera soumise à l\'administrateur pour validation.";
 
 $GLOBALS["TSFE"]->fe_user->setKey("ses", "confirmation_message", $this->oSandBox->message);
 $GLOBALS["TSFE"]->fe_user->storeSessionData();

 &;nbsp;           return $this->getParams();

 /*?>*/]]></php>
 </userobj>
 </afterCreation>
 <beforeEdition>
 <userobj>
 <php><![CDATA[/*<?php*/
 $params = t3lib_div::_GET();
 $postId = $params['fiche'];
 $currentEntryId = $this->oDataHandler->currentEntryId();
 $uid = ($currentEntryId) ? $currentEntryId : $postId;
 
 $admin_condition = "0";   
 $groups = explode(",", $GLOBALS["TSFE"]->fe_user->user['usergroup']);                       
 if (in_array("2", $groups) || in_array("3", $groups)) {
 $admin_condition = "1";
 }

 $temp= $GLOBALS["TYPO3_DB"]->exec_SELECTgetRows(
 "uid",
 "tx_bdd_pathologies",
 "deleted = 0 AND pid = 63 AND uid=".$uid ." AND ( ". $admin_condition ." OR cruser_id = " . $GLOBALS["TSFE"]->fe_user->user['uid'] .")",
 "",
 "");

 if (sizeof($temp) == 0) { //pas habilité
 header('Location: /index.php?id=30');
 }

 $aData = $this->getParams();
 $aData["tstamp"] = time();
 return $aData;

 /*?>*/]]></php>
 </userobj>
 </beforeEdition>
 <afterEdition>
 <userobj>
 <php><![CDATA[/*<?php*/

 $this->oSandBox->message = "Les modifications ont bien été enregistrées dans la pathologie. Elles viennent d’être envoyées à l\'administrateur pour validation.";

 $GLOBALS["TSFE"]->fe_user->setKey("ses", "confirmation_message", $this->oSandBox->message);
 $GLOBALS["TSFE"]->fe_user->storeSessionData();

 return $this->getParams();
 
 /*?>*/]]></php>
 </userobj>
 </afterEdition>
 </process>
 </datahandler:DB>
 
 <actionlets>           
 <actionlet:REDIRECT>
 <pageid>53</pageid>
 </actionlet:REDIRECT>
 </actionlets>   
 
 <sandbox>
 <userobj>
 <php><![CDATA[
 
 var $message = "";

 function init() {
 $params = t3lib_div::_GET();
 $postId = $params['fiche'];
 $currentEntryId = $this->oForm->oDataHandler->currentEntryId();
 $fiche = ($currentEntryId) ? $currentEntryId : $postId;
 if (isset($fiche) && $fiche != "" && !$currentEntryId) {
 $this->oForm->forceEntryId($fiche);
 }
 }

 ]]></php>
 </userobj>
 </sandbox>
 
 
 </control>

 
 <!-- FICHE begin -->
 <elements>   

 <renderlet:BOX name="confirm" >
 <html>
 <userobj>
 <php>
 <![CDATA[
 
 $html = "";
 
 if ($this->oSandBox->message != "") {
 //$html = '<script type="text/javascript"> alert(\'' . $this->oSandBox->message . "');</script>";
 //$this->oSandBox->message = "";
 }

 return $html;
 
 ]]></php>
 </userobj>
 </html>
 </renderlet:BOX>

 <renderlet:BOX name="pathologie" >
 <childs>
 <template path="fileadmin/templates/formidable/FICHES/fiche-pathologie.html" subpart="###pathologie###" />
 
 <!--column type="renderlet:TEXT" name="uid" label="N°" readOnly="true" /-->

 <renderlet:BOX defaultWrap="false" name="cruser_id" label="Créateur" readOnly="true" >
 <html>
 <userobj>
 <php>
 <![CDATA[
 $uid = $this->oDataHandler->getStoredData();
 $uid = $uid['cruser_id'];
 $res = $GLOBALS["TYPO3_DB"]->exec_SELECTgetRows(
 "username",
 "fe_users",
 "uid = " . $uid,
 "",
 "");
 return  $res[0]['username'];
 ]]>
 </php>
 </userobj>
 </html>
 </renderlet:BOX>

 <renderlet:DATE name="crdate" label="Créé le " readOnly="true" >
 <data>
 <datetime format="%d/%m/%y"/>
 </data>
 </renderlet:DATE>

 <renderlet:DATE name="tstamp" label="Modifié le " readOnly="true" >
 <data>
 <datetime format="%d/%m/%y"/>
 </data>
 </renderlet:DATE>

 <renderlet:TEXT name="pathologie" label="Nom *"  activeListable="true">
 <validators>
 <validator:STANDARD>
 <required message="Champ obligatoire" />
 </validator:STANDARD>
 <validator:DB>
 <unique>
 <tablename>tx_bdd_pathologies</tablename>
 <field>pathologie</field>
 <message>Cette pathologie existe déjà</message>
 <deleted>true</deleted>
 </unique>
 </validator:DB>
 </validators>
 </renderlet:TEXT>

 <!--renderlet:LISTBOX name="id_cat" label="Domaine de santé" activeListable="true" >
 <data defaultvalue ="" >
 <userobj>
 <php>
 <![CDATA[
 $temp= $GLOBALS["TYPO3_DB"]->exec_SELECTgetRows(
 "sys_dmail_category.uid as value, sys_dmail_category.category as caption",
 "sys_dmail_category",
 "(sys_dmail_category.deleted=0) AND (sys_dmail_category.hidden=0) ",
 "caption",
 "caption ASC");
 return  $temp;

 ]]>
 </php>
 </userobj>
 </data>
 <validators>
 <validator:STANDARD>
 <required message="Veuillez choisir un domaine de santé associé" />
 </validator:STANDARD>
 </validators>
 </renderlet:LISTBOX-->
 
 <renderlet:TEXTAREA name="motcles" label="Mot-clés *" activeListable="true">
 <validators>
 <validator:STANDARD>
 <required message="Champ obligatoire" />
 </validator:STANDARD>
 </validators>
 </renderlet:TEXTAREA>
 
 </childs>
 </renderlet:BOX>
 
 <renderlet:SUBMIT name="button_submit" label="Enregistrer pour validation" class="submit"/>

 <renderlet:BUTTON name="button_del" label="Supprimer" class="submit">
 <onclick params="uid" runat="server" confirm="Voulez-vous supprimer cette pathologie ?" when="after-init-renderlets">
 <userobj>
 <php><![CDATA[
 
 $paramurl = t3lib_div::_GET();   
 $uidcourant = $this->oDataHandler->currentEntryId();// récupère l'uid courant
 $GLOBALS["TYPO3_DB"]->exec_UPDATEquery( "tx_bdd_pathologies", "uid='" . $uidcourant . "'", array('deleted' => 1));
 return header('Location: /index.php?id=' . $paramurl['pageretour'] );

 ]]></php>
 </userobj>
 </onclick>
 <process>
 <userobj>
 <php><![CDATA[
 $groups = explode(",", $GLOBALS["TSFE"]->fe_user->user['usergroup']);                       
 if ((in_array("2", $groups) || in_array("3", $groups)) && $this->oDataHandler->currentEntryId()) {
 return true;
 }
 return false;
 ]]></php>
 </userobj>
 </process> 
 </renderlet:BUTTON>       

 <renderlet:LINK name ="annuler" label="Retour à la liste"  class="btn">
 <url>
 <userobj>
 <php><![CDATA[ 
 $paramurl = t3lib_div::_GET(); // récupère les paramètres de l'URL dans un tableau
 $idpageretour = $paramurl[pageretour];    
 return "index.php?id=".$idpageretour;
 ]]></php>
 </userobj>
 </url>
 </renderlet:LINK>
 </elements>
</formidable>

 



Ce fichier XML est associé au fichier HTML suivant :

fiche-pathologie.html


<!-- ###fiche### begin-->
{confirm.html}
<h3>Les champs avec une " * " sont obligatoires.</h3>
{pathologie}

<br />                       
<p><span class="label">&nbsp;</span>{button_submit}&nbsp;&nbsp;&nbsp;&nbsp;{button_del}</p><br />   

{annuler}
<!-- ###fiche### end-->


<!-- ###pathologie### begin-->

<p><span class="label">{pathologie.label} </span>{pathologie.input}<span class="error">{pathologie.error}</span></p>
<!-- ###crdate.value.equals("").isFalse() perimeter### begin-->
<p><span class="label">{statut.label} </span>{statut.html}</p>
<p><span class="label">{cruser_id.label} </span>{cruser_id.html}</p>
<p><span class="label">{crdate.label} </span>{crdate.value.readable}</p>
<p><span class="label">{tstamp.label} </span>{tstamp.value.readable}</p>
<!-- ###crdate.value.equals("").isFalse() perimeter### end-->
<p><span class="label">{motcles.label} </span>{motcles.input}<span class="error">{motcles.error}</span></p>
<!--p><span class="label">{id_cat.label} </span>{id_cat.input}<span class="error">{id_cat.error}</span></p-->

<!-- ###pathologie### end-->

 



Ce qui donnera le formulaire de saisie suivant, une fois stylé :





Multilangue

 

Le multilingue se gère comme dans n'importe quelle extension. Vous créer des fichiers de langue T3 XML dans lesquels vousn définissez vos labelselon la langue, et vous les appelez dans formidable avec la syntaxe suivante :

 

<renderlet:TEXT name="title"label="LLL:EXT:lang/locallang_general.php:LGL.title">

 

...ou en PHP dans le XML :

$this->getLLLabel("LLL:EXT:myext/locallang.xml:email.bodytext")

 

Pour éviter de mettre le nom du fichier (EXT:lang/locallang_general.php par exemple)  à chaque début de label pour pouvez préciser un fichier de langue par défaut dans vos meta :

  
<meta />
[...] 	<defaultlll>EXT:my_extension/pi1/locallang.xml</defaultlll> 


Puis noter vos label plus simplement : LLL:myfield.label



Débugger formidable ?


Pour gérer vos bug formidable (vous avez du rencontrer les magnifiques erreurs Mayday qu'il vous faudra éradiquer) plusieurs options.

D'abord si vous êtes dans le traitement côté serveur ou client non-AJAX vous pouvez utiliser le debugster, c'est un plugin TYPO3 qui ajoute une libraire PHP. La fonction


 debugster($variable_a_afficher) 



vous sera d'une grande aide. Dans un event javascript, vous pouvez passer par la célébre fonction alert(variable_a_afficher); ou de manière plus professionnelle, utiliser firebug. Ce dernier vous sera également indispensable pour débugger les CSS et éventuellement le HTML généré.

Si vous êtes dans un évenements AJAX, le debugster ne marchera pas, il vous faudra utiliser  $this->majixDebug($variable_a_afficher);   et mettre le mode debug a true dans le meta comme ci dessous :


 <meta>
 <name>validations</name>
 <form formid="validations"/>
 <displaylabels>true</displaylabels>
 <exportStyles>false</exportStyles>
 <debug>true</debug>
 <libs>scriptaculous</libs>
 <debugSendMail>on_va@ chez-toi.com</debugSendMail>
 </meta>



Pour les mail, vous pouvez ajouter un debugSendMail comme ci dessus, tous les mail envoyés avec $this->SendMail() seront envoyé à cette adresse dans votre formulaire. J'en profite pour montrer comment l'utiliser :


 $this->oForm->sendMail(
 "adresse@ destinaire.com",
 "Bonjour, <BR/>Ceci est le contenu HTML du message.<BR/>L'équipe",
 "TItre du message",
 "from@ chez-moi.com",
 "Truc Webmaster",
 "reply_to@ chez_moi.com",
 "Truc Reply service"
 );



Debuger MySQL
Si une requête est incorrect, la plupart du temps le formulaire MayDay vous la signalera. Mais il est possible qu'elle passe inapercu. Vous pouvez alors utiliser un outil d'affichage des requetes :

$GLOBALS['TYPO3_DB']->debugOutput = TRUE;
$this->debugDB = TRUE;

[...]

if( $this->debugDB ) {
 $GLOBALS['TSFE']->set_no_cache();
 t3lib_div::debug( $GLOBALS['TYPO3_DB']->debug_lastBuiltQuery );
 }

ou plus simplement :


$GLOBALS['TYPO3_DB']->debugOutput = TRUE;
debugster($GLOBALS['TYPO3_DB']->debug_lastBuiltQuery);

évidemmment cela fonctionnera avec les requêtes du genre $GLOBALS["TYPO3_DB"]->exec_SELECTgetRows(..) dans votre code PHP, pas dans les <sql> de votre datahandler !

Mise à jour le Jeudi, 18 Novembre 2010 16:58