Chapitre 49. Développement d’extension (plugin)
49.1. L’API
Toute la documentation se trouve sur http://wiki.noalyss.eu/doc/; seules les classes nous intéressent. La documentation est générée à partir du code avec Doxygen. Le but de cet article n’est pas de revisiter la documentation technique mais de la mettre en oeuvre afin d’écrire une extension ou plugin.
49.2. Installation de l’extension
Les extensions se trouvent toujours dans un sous-répertoire de
/noalyss/include/ext
, pour notre plugin que nous appelerons DUMMY, le
répertoire correspondant sera /noalyss/include/ext/dummy
. La première
étape est donc de décompresser le plugin dans le répertoire
/noalyss/include
et vérifier si on a bien /noalyss/include/ext/DUMMY
ensuite l’activer.
Le plus simple est de retrouver le fichier dummy.php, si le chemin est
/chemin/ext/dummy/dummy.php
alors /chemin/ext est l’endroit où les
plugins doivent être décompressés. Le chemin quand on ajoute le plugin
dans le menu CFGMENU est donné dans le wiki , pour chaque plugin, on
donne le paramètre "chemin" qui est en fait le chemin à partir de /ext/
49.3. Comment l’activer
Grâce à l’accès direct allez sur CFGMENU, puis ajoutez plugin, les champs sont les mêmes, il faut ajouter ce plugin dans le profil des utilisateurs qui doivent l’utiliser (CFGPRO)
Pour la version 6.8 et plus haut, il suffit d’aller dans CFGPLUGIN et cochez qui peut utiliser le plugin qui se trouvera dans le module "Extension". Avec CFGMENU, on peut placer l’extension dans un autre module.
49.3.1. Explication des champs
-
Label, est le nom de menu de votre extension
-
Code est utilisé pour inclure le fichier du plugin, il correspond au champs caché plugin_code
-
Fichier est le chemin complet vers l’extension
Pour notre extension, les valeurs suivantes sont données
-
Label : Mon dummy à moi
-
code : dum
-
Fichier : dummy/dummy.php
Vous sauvez, cliquer sur Extension et vous verrez apparaître un nouveau plugin appelé "Mon dummy à moi"; si vous cliquez dessus, la page ext/dummy/dummy.php sera exécutée; voyez le code du fichier noalyss/html/extension.php pour comprendre.
49.3.2. Tutoriel vidéo
49.3.3. Exemple de base
Dans les plugins de base de NOALYSS, il y a skel qui est le squelette d’un plugin. Vous pouvez l’utiliser comme base.
49.3.4. Connection à la base de données
Certaines valeurs doivent toujours être passées à chaque page, par exemple gDossier qui est l’identifiant du dossier. Pour se connecter c’est assez facile, il faut utiliser la classe Database et la classe Dossier.
Donc on écrit dans dummy.php
echo "L' identifiant de mon dossier est ".dossier::id()."<br>";
echo "Son nom réel est ".DOMAIN."dossier".dossier::id()."<br>";
echo "Son nom est ".dossier::name()."<br>";
// Je me connecte à présent à ce dossier
$cn_db=new Database(dossier::id());
// On peut aussi se connecter ainsi (développement plus récent)
$cn= Dossier::connect();
Je veux afficher toutes les fiches qui concernent le matériel à amortir, donc j’ai besoin de savoir ce que vaut son FICHE_DEF_REF:FRD_ID dans constant.php; la FICHE_TYPE_XX n’existe pas, je vois dans la table fiche_def_ref qu’il s’agit de "7 Matériel à amortir"
Pour avoir toutes les fiches,
$fiche= new Fiche($cn_db);
$aFicheMateriel=$fiche->getByDef(7);
Pour chaque fiche, je veux afficher son nom, son prix, le nombre d’années à amortir et la date d’achat. Je peux en trouver les valeurs dans la table attr_def ou le fichier constant.php,
Donc cela devient
for ($i=0; $i < count($aFicheMateriel);$i++) {
echo "<ul>
echo "<li> ";
echo "Nom";
echo $aFicheMateriel[$i]->strAttribut(ATTR_DEF_NAME);
echo "</li>";
echo "<li> ";
echo "Prix achat";
echo $aFicheMateriel[$i]->strAttribut(ATTR_DEF_PRIX_ACHAT);
echo "</li>";
echo "<li> ";
echo "Durée amortissement";
echo $aFicheMateriel[$i]->strAttribut(8);
echo "</li>";
echo "<li> ";
echo "Date de début";
echo $aFicheMateriel[$i]->strAttribut(10);
echo "</li>";
echo "</ul>
}
49.3.5. Soumettre des requêtes
Uniquement pour l’exercice, nous allons ajouter un FORM. Les données nécessaires dans le FORM sont toujours au minimum: l’id du dossier, le code du plugin.
Dans le FORM, on demandera juste à afficher le solde de chaque élément. On aura alors le code suivant, on utilisera la technique des templates
$year=new IText('year');
$str_year=$year->input();
$str_submit=HtmlInput::submit('year_left','Appliquer');
require_once('template1.php');
template1.php
<FORM METHOD="GET" ACTION="extension.php">
<?=dossier::hidden()?>
<?=HtmlInput::extension()?>
Solde pour l'année : <?=$str_year?>
<?=$str_submit?>
</form>
Puis dans le début du fichier noalyss/include/ext/dummy/dummy.php, on ajoutera un test pour savoir un FORM a été soumis et on affichera une boîte de dialogue.
$http= new HttpInput();
if (isset($http->get('year_left','number'))){
alert('Vous avez demandé le nombre d\'années restantes');
}
(1) permet de retrouver des variables passées par get , post ou request (voir documentation code^)
(2) remplace $_GET['year_left']
, HttpInput vérifie aussi le type de données et peut donner une valeur par défaut si cette donnée n’est pas dans $_GET
49.4. Plugin plus avancé
Ma première extension, intégrer un fichier de client dans une catégorie de fiche, ce fichier est en CSV. Le code est simple et compréhensible, normalement on devrait avoir une meilleure gestion des erreurs, vérifier les attaques SQL Inject,… Ce code n’est là QUE pour expliquer le concept. On n’a pas utilisé plusieurs pages, ni de templates
Tout d’abord, il faut se connecter à la base de données
// se connecter au dossier courant
$cn=Dossier::connect();
Dans extension.php on vérifie la sécurité, en ajoutez une dans l’extension n’est en général pas nécessaire mais vous pourriez avoir votre propre système de sécurité si votre extension est fort complexe
En premier lieu, il est nécessaire de choisir dans quelle catégorie de fiche je veux intégrer les enregistrements. Donc on utilise un petit form
echo '<form METHOD="get" action="extension.php">';
echo dossier::hidden();
// Ceci vous permet de revenir ici (voir extension.php). Cet élément caché permet d'include cette page-ci
// Donc si votre plugin contient plusieurs pages, vous allez devoir ajouter une seconde variable pour
// inclure la page que vous voulez (voir méthode de développement de PhpCompta )
echo HtmlInput::extension();
echo "Choix de la catégorie de fiche";
$select_cat=new ISelect('fd_id');
$select_cat->value=$cn->make_array('select fd_id,fd_label from fiche_def where frd_id='.
FICHE_TYPE_CLIENT);
echo $select_cat->input();
echo HtmlInput::submit('display_prop','Afficher les propriétés');
echo '</FORM>';
Il faut remarquer 2 choses dans ce FORM, primo, on utilise les objets HtmlInput et ISelect, secundo on doit avoir absolument en variables cachées, le n° de dossier sur lequel on est connecté, le code de l’extension, qui permettra à extension.php d’include le bon fichier. On utilise ici le protocole GET puisqu’on interroge, le protocole POST est réservé aux sauvegardes, c’est une convention assez répandue. La différence, est que les requêtes GET se voient dans l’URL, les requêtes POST ne sont jamais dans l’url.
L’utilisateur soumet le FORM, donc la feuille se recharge et on arrive à cette partie du code
On choisit d’afficher les propriétés avant de confirmer l’import
if ( isset($_GET['display_prop'])){
$http=new HttpInput();
$a=new Fiche($cn);
$prop=$a->toArray($http->get('fd_id'));
foreach ($prop as $key=>$value) echo "Index : $key valeur $value <br/>";
echo '<form method="POST" action="extension.php" enctype="multipart/form-data">';
echo dossier::hidden();
echo HtmlInput::extension();
echo HtmlInput::hidden('fd_id',$http->get('fd_id');
$file=new IFile('fichier_csv');
echo $file->input();
echo HtmlInput::submit('start_import','Démarrez importation');
echo '</form>';
exit;
}
Voilà, si l’utilisateur clique sur le bouton SUBMIT, l’importation va démarrer. Dans notre exemple, on imaginera que le fichier CSV n’a que 4 champs "nom client","prenom client", "numero client","adresse client"
Le code qui suit est très simplifié.
if ( isset($_POST['start_import'])){
$http=new HttpInput();
$fd_id=$http->post('fd_id');
$tmp_file=$_FILE['fichier_csv']['tmp_name'];
if ( ! is_uploaded_file($tmp_file))
die 'Je ne peux charger ce fichier';
// on ouvre le fichier
$f=fopen($tmp_file,'r');
// On récupère les propriétés de cette catégorie de fiche
$client=new Fiche($cn);
// $array contient toutes les valeurs nécessaires à Fiche::insert,
$array=$client->toArray($http->post('fd_id'));
while ( $data=fgetcsv($f)) {
// remarque : on a éliminé les traitements d'erreur
// On remet tous les attributs (propriétés) à vide
foreach(array_keys($array) as $key) $array[$key]="";
// Nom et prénom
$array['av_text1']=$data[0].' '.$data[1];
// Numéro de client
$array['av_text30']=$data[2];
// Adresse
$array['av_text14']=$data[3];
// Quickcode
$array['av_text23']="CLI".$data[2];
$client->insert($fd_id,$array);
}
exit;
}
Voici le fichier client.txt
"Nom client1","Prénom","C1","Rue de la boite,55" "Nom client2","Prénom","C2","Rue du couvercle,55" "Nom client3","Prénom","C3","Rue de la chaussure,55" "Nom client4","Prénom","C4","Rue de la couleur,55"
Si vous vérifiez dans VW_CLIENT, vous verrez que toutes vos fiches ont été ajoutées. Dans l’exemple, il fatraitement d’erreur plus élaboré; le fait que si une fiche echoue , l’opération est annulée (Database::rollback) ou alors création d’un fichier avec les enregistrements "ratés"…
49.5. Ajax
Afin d’utiliser des fonctions avec ajax, prototype.js devrait être utilisé.Vous devez appeler le fichier ajax.php, ce fichier dans html va simplement vérfier la sécurité et appeler le fichier ajax.php du répertoire où se trouve le plugin avec tous les arguments donnés.
Exemple
dans
dummy/javascript.js, vous avez
function show_detail(pop_id){
$('detail_invoice_content').innerHTML=loading();
showIPopup('detail_invoice');
try {
var gDossier=$('gDossier').value;
var phpsessid=$('phpsessid').value;
var code=$('code').value;
var obj={"op_id":pop_id,"gDossier":gDossier,"phpsessid":phpsessid,"code":code,'act':'detail_invoice'};
var queryString=encodeJSON(obj);
var action=new Ajax.Request ( 'ajax.php',
{
method:'get',
parameters:queryString,
onFailure:show_detail_error,
onSuccess:show_detail_success
}
);
} catch (e){alert('show_detail'+e.message);}
}
et dans dummy/ajax.php
<?php
// Met correctement la langue
set_language();
//retrouve le dossier courant et s y connecte
$gDossier=dossier::id();
$cn=new Database($gDossier);
// action
$action=(isset($_REQUEST['act']))?$_REQUEST['act']:'sh';
// Véridfie la sécurité
require_once ('class_user.php');
$User=new User(new Database());
$User->Check();
/* Suivant l action demandé, on executera tel ou tel partie de code
/* Show the document */
if ( $action == 'sh') {
/*
* Votre code
*/}
/* remove the document */
if ( $action == 'rm' ) {
/*votre code et la réponse
*/
header('Content-type: text/xml; charset=UTF-8');
header ('<?xml version="1.0" encoding="UTF-8"?>');
echo '<answer>';
echo '<ctl>detail_invoice</ctl>';
echo '<html>'.escape_xml($html).'</html>';
echo '</answer>';
}
?>
49.6. Les données
Si votre extension nécessite de sauver ses propres données, il faut impérativement les mettres dans un schéma séparé. Prévoyez une table version, afin d’appliquer des mises à jour si nécessaire et un fichier install.php afin de créer les schémas nécessaire
exemple
create schema plugin_tva;
create table version (val integer);