Développer un plugin GLPI

Comment créer un plugin pour GLPI

Tristan Fleury

Ce tutoriel aborde la création d'un plugin pour le logiciel de gestion de parc et d'incident : GLPI. Aucune connaissance sur le développement de GLPI n'est nécessaire, par contre les bases de PHP sont indispensables ainsi qu'une compétence en langage objet.

Dans ce tutoriel j'utiliserai une bibliothèque externe nommée Smarty (moteur de templates) pour gérer toute la partie « Vue » du plugin.

N'hésitez pas à commenter ce tutoriel Commentez

- Titre : Développer un plugin GLPI

- Auteur : Tristan Fleury

- Parution : 12 janvier 2014

- Mise à jour : 4 novembre 2014

- Public visé :

- Licence : Copyright ® 2013 Viduc. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Voir également :

- FAQ NetBeans

- FAQ Ajax

- Tutoriel PHP Débutant

- Tutoriel PHP cours

- Tutoriel Smarty Initiation

- Tutoriel Apache

Sommaire

- I. Introduction

- II. Prérequis

- III. Structure du plugin

- III-A. Création du dossier de base

- III-B. Création du projet dans l'EDI

- IV. Développement du plugin

- IV-A. Fichiers d'installation

- IV-A-1. Fichier setup.php

- IV-A-2. Fichier hook.php

- IV-A-2-a. Création des tables

- IV-A-2-b. Suppression des tables

- IV-A-2-c. Fichier complet

- IV-A-3. Premier test du plugin

- IV-B. Création des class

- IV-B-1. Dossier des class

- IV-B-2. La class config.class.php

- IV-B-2-a. Fonction getConfiguration()

- IV-B-2-b. Fonction setConfiguration($id=null,$valeur)

- IV-B-2-c. Fichier final

- IV-B-3. La class profile.class.php

- IV-B-4. La class monplugin.class.php

- IV-C. Création des formulaires

- IV-C-1. Le formulaire config.form.php

- IV-C-2. Le formulaire profile.form.php

- IV-C-3. Le formulaire monplugin.form.php

- IV-D. Création des templates

- IV-D-1. Le template config.tpl

- IV-D-2. Le template profile.tpl

- IV-D-3. Le template monplugin.tpl

- IV-E. Création des fichiers AJAX

- IV-E-1. Le fichier Ajax config.ajax.php

- IV-E-2. Le fichier Ajax monplugin.ajax.php

- V. Installation et configuration du plugin

- V-A. Modifier les droits

- V-B. Administration

- V-C. Profil

- VI. Utilisation du plugin

- VII. Téléchargement du plugin

- VIII. Conseils et informations

- VIII-A. Base de données

- VIII-B. Fonctions et méthodes internes

- VIII-C. Utiliser d'autres bibliothèques externes

- VIII-D. Compatibilité

- VIII-E. Sources d'informations

- IX. Conclusion et remerciements

I - Introduction

« Solution open source de gestion de parc informatique et de servicedesk, GLPI est une application Full Web pour gérer l'ensemble de vos problématiques de gestion de parc informatique : de la gestion de l'inventaire des composantes matérielles ou logicielles d'un parc informatique à la gestion de l'assistance aux utilisateurs. »

GLPI est un logiciel libre distribué sous licence GNU/GPL version 2.

Depuis son premier lancement, GLPI a beaucoup évolué entre autres, grâce à ses nombreux plugins qui permettent d'adapter l'application aux besoins spécifiques de l'environnement de travail. À force d'utilisation, certains besoins apparaissent et ne sont pas forcément couverts par les plugins existants. Pour répondre à ce problème, il est possible de créer son propre plugin. Ce tutoriel va vous aider à réaliser ce projet.

Attention !

Ce tutoriel n'est pas à ce jour validé par le groupe de développeurs de GLPI. J'utilise certaines bibliothèques et fonctionnalités externes à GLPI pour répondre à certains manques ou besoins spécifiques.

II - Prérequis

Vous devez avoir installé votre version de GLPI. J'utilise pour ce tutoriel une version 0,84. Attention, ne pas utiliser la version 0,85 (RC), car de nombreuses modifications majeures ont été réalisées. Pour installer votre version de GLPI, reportez-vous ici.

Voici un tutoriel pour l'installation d'un serveur web

III - Structure du plugin

Le plugin que je vous propose de créer est minimaliste. Nous allons réaliser un petit formulaire avec une liste déroulante pour renseigner le statut d'une machine (le plugin ne servira à rien en soi, c'est juste pour l'exemple:)).

III-A - Création du dossier de base

Nous partons du principe que le dossier de base de GLPI peut être enregistré n'importe où. Dans ce tutoriel, l'installation de GLPI est positionnée dans var/www/glpi. Le dossier de base doit ressembler à cette installation :

Le dossier qui va plus précisément nous intéresser est « plugins ». Ouvrez-le, il devrait être vide (sauf si d'autres plugins sont déjà installés dans votre environnement).

Pour commencer, il nous faut créer un dossier de base pour notre nouveau plugin. Le nom de ce dossier doit être en minuscules, avec des caractères alphanumériques, sans espace… Comme exemple dans ce tutoriel, nous allons réaliser un plugin nommé « monplugin ». Créez donc ce dossier dans plugins.

III-B - Création du projet dans l'EDI

Nous allons maintenant créer un projet avec Netbeans. Cette étape n'est pas obligatoire si vous n'utilisez pas cet EDI. Vous devrez adapter la création du projet si vous utilisez un autre EDI (Eclipse…). Sachez que si vous n'utilisez pas d'EDI, un simple éditeur de texte suffit…

Ouvrez Netbeans puis cliquez sur l'icône « Nouveau projet ».

Choisissez la catégorie « PHP », « PHP Application ». Faites « suivant ».

Dans « Project Name », entrez le nom du projet (ici monplugin). Choisissez le dossier précédemment créé dans « Sources Folder », puis cliquez sur « Terminer ».

Pour plus d'informations sur l'utilisation de NetBeans, je vous conseille la FAQ :https://netbeans.developpez.com/faq/

IV - Développement du plugin

IV-A - Fichiers d'installation

Un plugin GLPI doit contenir deux fichiers de base (setup.php et hook.php) à la racine même du dossier.

Créer ces deux fichiers à la racine :

IV-A-1 - Fichier setup.php

Ce fichier permet d'enregistrer la configuration minimum du plugin et de déclarer les classes primaires pour son fonctionnement. De nombreuses options sont disponibles, mais ne possédant pas de documentation officielle dessus, je vais rester très simpliste dans mon approche.

La première fonction à créer est celle qui définit la version du plugin :

plugin_version

/**
* Fonction de définition de la version du plugin
* @return type
*/
function plugin_version_monplugin()
{
return array('name'           => "monplugin",
'version'        => '1.0.0',
'author'         => 'Viduc',
'license'        => 'GPLv2+',
'homepage'       => 'http://viduc.sugarbox.fr',
'minGlpiVersion' => '0.83');
}

Dans la variable 'minGlpiVersion', mettez la version avec laquelle vous développez votre plugin. Vous pourrez modifier cette information plus tard (si vous souhaitez tester votre plugin sur des versions inférieures).

Vous remarquerez que le titre de la fonction contient le nom du plugin. Ce sera le cas pour les fonctions suivantes de ce fichier. Il faudra bien sûr adapter le nom des fonctions à votre plugin.

La fonction suivante vérifie les prérequis de version de GLPI. Dans mon exemple, j'ai simplifié la vérification au maximum, ne gardant qu'une version minimum (normalement le fonctionnement que je vais adopter ici devrait permettre une compatibilité ascendante…).

Prérequis

/**
* Fonction de vérification des prérequis
* @return boolean
*/
function plugin_monplugin_check_prerequisites()
{
if (GLPI_VERSION >= 0.80)
return true;
echo "A besoin de la version 0.80 au minimum";
return false;
}

La troisième fonction obligatoire permet de vérifier la configuration initiale de votre installation de GLPI (ou autre). Si par exemple il est nécessaire d'utiliser certaines options de PHP, vous pouvez faire un test ici. La fonction de base est très simple, je n'y toucherai pas.

Vérification configuration

/**
* Fonction de vérification de la configuration initiale
* @param type $verbose
* @return boolean
*/
function plugin_monplugin_check_config($verbose=false)
{
if (true)
{ // Your configuration check
return true;
}
if ($verbose)
{
echo 'Installed / not configured';
}
return false;
}

Enfin, la dernière fonction est celle qui va initialiser les class du plugin.

initialisation

/**
* Fonction d'initialisation du plugin
* @global array $PLUGIN_HOOKS
*/
function plugin_init_monplugin()
{
global $PLUGIN_HOOKS;

$PLUGIN_HOOKS['csrf_compliant']['monplugin'] = true;
$PLUGIN_HOOKS['config_page']['monplugin'] = 'front/config.form.php';
Plugin::registerClass('PluginMonpluginMonplugin', array('addtabon' => array('Computer')));
Plugin::registerClass('PluginMonpluginProfile', array('addtabon' => array('Profile')));
Plugin::registerClass('PluginMonpluginConfig');
}

La première ligne est obligatoire, elle permet de renseigner si le plugin est conforme aux normes de sécurité crsf_compliant. Cette vérification consiste en l'utilisation de token pour les formulaires. Nous verrons comment intégrer ce système.

La deuxième ligne permet de créer une page de configuration du plugin, accessible par le menu d'administration de GLPI.

Vous remarquerez que dans ces deux lignes l'index ['monplugin'] est ajouté à chaque variable. Modifiez cet index en fonction du nom de votre plugin.

Les trois lignes suivantes instancient trois class différentes. Il est possible de créer d'autres class et de les instancier de la sorte ici. La première instancie la class principale (le cœur) du plugin et lui passe comme argument un tableau ('addtabon' pour l'ajout d'un onglet, “Computer” pour ajouter cet onglet à la partie « ordinateur »). La ligne suivante va permettre de rajouter une entrée dans la gestion des profils. Enfin, la dernière ligne instancie la class servant à la configuration du plugin (dont le formulaire est renseigné plus haut…)

Vous remarquerez le nommage des class, elles commencent toutes par Plugin (avec un P majuscule), puis le nom du plugin (avec une majuscule également) et enfin le nom propre à la class avec lui aussi une majuscule. Ce nom doit refléter au mieux l'objet de la class.

Le fichier aura donc cette forme-là :

setup.php

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Fonction de définition de la version du plugin
* @return type
*/
function plugin_version_monplugin()
{
return array('name'           => "monplugin",
'version'        => '1.0.0',
'author'         => 'Viduc',
'license'        => 'GPLv2+',
'homepage'       => 'http://viduc.sugarbox.fr',
'minGlpiVersion' => '0.83');// For compatibility
}

/**
* Fonction de vérification des prérequis
* @return boolean
*/
function plugin_monplugin_check_prerequisites()
{
if (GLPI_VERSION >= 0.80)
return true;
echo "A besoin de la version 0.80 au minimum";
return false;
}

/**
* Fonction de vérification de la configuration initiale
* @param type $verbose
* @return boolean
*/
function plugin_monplugin_check_config($verbose=false)
{
if (true)
{ // Your configuration check
return true;
}
if ($verbose)
{
echo 'Installed / not configured';
}
return false;
}

/**
* Fonction d'initialisation du plugin
* @global array $PLUGIN_HOOKS
*/
function plugin_init_monplugin()
{
global $PLUGIN_HOOKS;

$PLUGIN_HOOKS['csrf_compliant']['monplugin'] = true;
$PLUGIN_HOOKS['config_page']['monplugin'] = 'front/config.form.php';
Plugin::registerClass('PluginMonpluginMonplugin', array('addtabon' => array('Computer')));
Plugin::registerClass('PluginMonpluginProfile', array('addtabon' => array('Profile')));
Plugin::registerClass('PluginMonpluginConfig');
}
?>

IV-A-2 - Fichier hook.php

Ce fichier sert à la création et à la suppression des tables dans la base de données. Il sera automatiquement lancé lors de l'installation du plugin dans la partie administration.

Dans notre exemple, nous allons créer trois tables, une pour enregistrer la configuration de notre plugin, une autre qui servira au fonctionnement du plugin (enregistrement de l'information machine virtuelle ou machine physique) et enfin la troisième qui enregistrera les accès pour les différents profils.

Ce fichier comportera deux fonctions, une pour la création des tables, la seconde pour la suppression de ces tables.

Il n'est pas obligatoire de créer des tables pour un plugin, par contre le fichier hook avec ces deux fonctions (même vides) sont indispensables. Les deux fonctions devront renvoyer « true ».

IV-A-2-a - Création des tables

La fonction de création des tables est la suivante :

installation table

/**
* Fonction d'installation du plugin
* @return boolean
*/
function plugin_monplugin_install()
{
global $DB;

return true ;
}

La variable globale $DB doit être instanciée pour permettre l'appel au gestionnaire de base de données interne.

Créons la table profiles :

profiles

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_profiles"))
{

// requête de création de la table
$query = "CREATE TABLE `glpi_plugin_monplugin_profiles` (
`id` int(11) NOT NULL default '0' COMMENT 'RELATION to glpi_profiles (id)',
`right` char(1) collate utf8_unicode_ci default NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";

$DB->query($query) or die($DB->error());

//création du premier accès nécessaire lors de l'installation du plugin
$id = $_SESSION['glpiactiveprofile']['id'];
$query = "INSERT INTO glpi_plugin_monplugin_profiles VALUES ('$id','w')";

$DB->query($query) or die($DB->error());
}

La table commence par glpi_plugin. Cette norme permet de garder une structure équivalente pour tous les plugins. Il faut garder ce principe pour toutes les tables que vous souhaitez créer. Nous complétons par le nom du plugin en minuscules et enfin un identifiant compréhensible (comme pour les class).

Les requêtes sont simplement écrites puis envoyées au noyau GLPI via la variable globale $DB.

Ici nous insérons un premier enregistrement, il s'agit de l'id du profil qui va installer le plugin (généralement un administrateur de GLPI) et nous lui attribuons un droit complet (w).

La deuxième table concerne la configuration. Dans cet exemple, nous n'en avons pas réellement besoin, mais pour l'exemple, nous allons enregistrer quelques infos… Il s'agira simplement des différents statuts possibles pour la machine (physique, VM, et pourquoi pas… cloud ?).

config

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_config"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_config` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`statut` char(32) NOT NULL default '',
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

Enfin nous allons créer la table qui servira à enregistrer le statut de la machine à proprement parler (dans cette table sera enregistré l'id de la machine et l'id du statut disponible de la table créée précédemment).

statut

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_statut"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_statut` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`id_machine` int(11) NOT NULL,
`id_config` int(11) NOT NULL,
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

Finalement, la fonction de création des tables ressemblera à ça :

installation tables

/**
* Fonction d'installation du plugin
* @return boolean
*/
function plugin_monplugin_install()
{
global $DB;

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_profiles"))
{

// requête de création de la table
$query = "CREATE TABLE `glpi_plugin_monplugin_profiles` (
`id` int(11) NOT NULL default '0' COMMENT 'RELATION to glpi_profiles (id)',
`right` char(1) collate utf8_unicode_ci default NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";

$DB->query($query) or die($DB->error());

//création du premier accès nécessaire lors de l'installation du plugin
$id = $_SESSION['glpiactiveprofile']['id'];
$query = "INSERT INTO glpi_plugin_monplugin_profiles VALUES ('$id','w')";

$DB->query($query) or die($DB->error());
}

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_config"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_config` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`statut` char(32) NOT NULL default '',
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_statut"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_statut` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`id_machine` int(11) NOT NULL,
`id_config` int(11) NOT NULL,
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

return true;
}

IV-A-2-b - Suppression des tables

La fonction suivante permet de supprimer les tables précédemment créées lors de la désinstallation du plugin. La fonction est la suivante :

désinstallation table

/**
* Fonction de désinstallation du plugin
* @return boolean
*/
function plugin_monplugin_uninstall()
{
global $DB;

$tables = array("glpi_plugin_monplugin_profiles", "glpi_plugin_monplugin_config", "glpi_plugin_monplugin_statut");

foreach($tables as $table)
{$DB->query("DROP TABLE IF EXISTS `$table`;");}
return true;
}

Comme nous pouvons le voir, il suffit de reporter dans le tableau $tables le nom des tables précédemment créées.

IV-A-2-c - Fichier complet

Le fichier final ressemblera donc à ceci :

hook.php

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Fonction d'installation du plugin
* @return boolean
*/
function plugin_monplugin_install()
{
global $DB;

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_profiles"))
{

// requête de création de la table
$query = "CREATE TABLE `glpi_plugin_monplugin_profiles` (
`id` int(11) NOT NULL default '0' COMMENT 'RELATION to glpi_profiles (id)',
`right` char(1) collate utf8_unicode_ci default NULL,
PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";

$DB->query($query) or die($DB->error());

//création du premier accès nécessaire lors de l'installation du plugin
$id = $_SESSION['glpiactiveprofile']['id'];
$query = "INSERT INTO glpi_plugin_monplugin_profiles VALUES ('$id','w')";

$DB->query($query) or die($DB->error());
}

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_config"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_config` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`statut` char(32) NOT NULL default '',
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

// Création de la table uniquement lors de la première installation
if (!TableExists("glpi_plugin_monplugin_statut"))
{
// Création de la table config
$query = "CREATE TABLE `glpi_plugin_monplugin_statut` (
`id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
`id_machine` int(11) NOT NULL,
`id_config` int(11) NOT NULL,
`vie` char(1) NOT NULL default '1'
)ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci";
$DB->query($query) or die($DB->error());
}

return true;
}

/**
* Fonction de désinstallation du plugin
* @return boolean
*/
function plugin_monplugin_uninstall()
{
global $DB;

$tables = array("glpi_plugin_monplugin_profiles", "glpi_plugin_monplugin_config", "glpi_plugin_monplugin_statut");

foreach($tables as $table)
{$DB->query("DROP TABLE IF EXISTS `$table`;");}
return true;
}
?>

Attention ! Comme vous l'aurez sûrement remarqué, les deux fonctions renvoient « true ». Même si vous ne créez pas de table, il est impératif de créer ces deux fonctions et de les faire renvoyer « true » (return true).

IV-A-3 - Premier test du plugin

Normalement votre plugin devrait être disponible dans la liste des plugins installables depuis votre GLPI. Ouvrez la page web de votre GLPI (avec un compte administrateur GLPI), et rendez-vous sur « Configuration-->plugins ». Le plugin doit apparaître dans la liste comme ici :

Si ce n'est pas le cas, reprenez depuis le début et vérifiez bien tous vos paramètres et entre autres le nom du plugin dans les différentes fonctions des fichiers setup et hook.

IV-B - Création des class

Dans cet exemple, nous allons créer trois class, une pour la partie configuration, la deuxième pour la gestion des profils et la dernière pour le fonctionnement général du plugin. Les class constituent le moteur du plugin. Elles sont regroupées dans un dossier unique (inc) et elles sont nommées de la façon suivante : <nomduplugin>.class.php (le tout en minuscules). Ces class sont instanciées dans le fichier setup.php. Attention, les class fonctionnent avec d'autres fichiers (template, formulaire, Ajax, etc.). Vous ne comprendrez pas tout de suite le fonctionnement de ces class, mais sachez qu'elles répondent à un ensemble. J'ai préféré étudier la création par type de fichier et non par fonctionnalité pour plus de lisibilité.

IV-B-1 - Dossier des class

Il faut créer le dossier pour enregistrer nos class. Créez le dossier inc dans votre dossier source du plugin. Toutes les class devront étendre la class CommonDBTM (pour l'accès à la base de données).

IV-B-2 - La class config.class.php

Nous allons commencer par créer la class qui permettra de configurer notre plugin. Dans le dossier inc créez le fichier config.class.php

Le nom de la class a déjà été défini dans le fichier setup.php. Il suit la normalisation « PluginNomdupluginIdentifiant », donc pour nous PluginMonpluginConfig.

Cette class étend la class CommonDBTM (qui permet l'accès à la base de données). Ce sera le cas pour toutes nos class.

Ce fichier sera couplé à un formulaire que nous verrons plus tard. Pour cette class nous aurons besoin d'une fonction qui récupère les informations de configuration déjà enregistrées et une fonction pour enregistrer ou mettre à jour les nouvelles informations. Cette class sera appelée par le formulaire (souvenez-vous de la ligne « $PLUGIN_HOOKS['config_page']['monplugin'] = 'front/config.form.php'; » dans le setup.php, c'est elle qui lancera le formulaire).

IV-B-2-a - Fonction getConfiguration()

Voici la fonction qui récupère la configuration dans la base de données :

getConfiguration

/**
* Récupère les informations de configuration enregistrées
* Retourne un tableau avec ID + valeur
* @global type $DB
* @return string
*/
function getConfiguration()
{
global $DB;

$query = "SELECT * FROM glpi_plugin_monplugin_config WHERE vie='1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$i = 0;
while ($row = $DB->fetch_assoc($result))
{
if (!empty($row['id'])){$config['id'] = $row['id'];}
else{$config['id'] = "";}
if (!empty($row['statut'])){$config['statut'] = $row['statut'];}
else{$config['statut'] = "";}
$retour[$i] = $config;
$i++;
}
}
}
return $retour;
}

Comme nous pouvons le voir, la fonction est assez simple. Nous faisons une requête vers la base de données, on filtre les résultats et on renvoie un tableau de valeurs.

IV-B-2-b - Fonction setConfiguration($id=null,$valeur)

Cette fonction enregistre ou modifie un enregistrement de la configuration :

setConfiguration

/**
* Enregistre ou modifie une information de configuration
* @global type $DB
* @param type $id
* @param type $valeur
*/
function setConfiguration($id=null,$valeur)
{
global $DB;

if($id != null)
{
if($valeur != "delStatut")
{$query = "UPDATE glpi_plugin_monplugin_config SET statut='$valeur' WHERE id='$id'";}
else //suppression du statut (on passe la vie à 0)
{$query = "UPDATE glpi_plugin_monplugin_config SET vie='0' WHERE id='$id'";}
$DB->query($query) or die($DB->error());
}
else
{
$query = "INSERT INTO glpi_plugin_monplugin_config (statut,vie) VALUES ('$valeur','1')";
$DB->query($query) or die($DB->error());
}
}

Cette fonction prend deux paramètres, l'id de l'enregistrement à modifier (null si nouvel enregistrement) et la valeur à enregistrer (statut).

IV-B-2-c - Fichier final

Le fichier final sera le suivant :

config

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Class pour la partie gestion de la configuration
*/
class PluginMonpluginConfig extends CommonDBTM
{
/**
* Récupère les informations de configuration enregistrées
* Retourne un tableau avec ID + valeur
* @global type $DB
* @return string
*/
function getConfiguration()
{
global $DB;

$query = "SELECT * FROM glpi_plugin_monplugin_config WHERE vie='1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$i = 0;
while ($row = $DB->fetch_assoc($result))
{
if (!empty($row['id'])){$config['id'] = $row['id'];}
else{$config['id'] = "";}
if (!empty($row['statut'])){$config['statut'] = $row['statut'];}
else{$config['statut'] = "";}
$retour[$i] = $config;
$i++;
}
}
}
return $retour;
}

/**
* Enregistre ou modifie une information de configuration
* @global type $DB
* @param type $id
* @param type $valeur
*/
function setConfiguration($id=null,$valeur)
{
global $DB;

if($id != null)
{
if($valeur != "delStatut")
{$query = "UPDATE glpi_plugin_monplugin_config SET statut='$valeur' WHERE id='$id'";}
else //suppression du statut (on passe la vie à 0)
{$query = "UPDATE glpi_plugin_monplugin_config SET vie='0' WHERE id='$id'";}
$DB->query($query) or die($DB->error());
}
else
{
$query = "INSERT INTO glpi_plugin_monplugin_config (statut,vie) VALUES ('$valeur','1')";
$DB->query($query) or die($DB->error());
}
}
}
?>

IV-B-3 - La class profile.class.php

Comme pour la class précédente, créez une class qui étend la class CommonDBTM :

Ce fichier sera couplé à un formulaire ainsi qu'à un fichier Ajax que nous verrons plus tard. Cette class doit contenir deux fonctions obligatoires, la première (getTabNameForItem(CommonGLPI $item, $withtemplate=0)) permet au système de récupérer le nom à afficher dans l'onglet, la seconde lui permet de savoir quoi faire si on sélectionne cet onglet (displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0)).

Suit une fonction permettant l'affichage du formulaire (showForm($id, $options=array())) , une fonction qui vérifie les droits (estAutorise()), une fonction qui met à jour les droits (majDroit($arrayItem)) et deux fonctions qui permettent de récupérer les chemins absolus de l'application (getAbsolutePath() et getHttpPath()).

Le fichier complet ressemblera à ça :

profile.class.php

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Class de gestion pour la partie profil
*/
class PluginMonpluginProfile extends CommonDBTM
{
/**
* Récupère le nom de l'onglet si l'utilisateur est autorisé
* @param CommonGLPI $item
* @param type $withtemplate
* @return boolean|string
*/
function getTabNameForItem(CommonGLPI $item, $withtemplate=0)
{
if (!Session::haveRight("profile","r"))
{return false;}
elseif (Session::haveRight("profile", "w"))
{
//Ici on vérifie qu'il s'agit bien de la partie Profil de GLPI qui est concernée
if ($item->getType() == 'Profile')
{
//Ici on a vérifié que l'utilisateur actuel a le droit d'accéder
//à cet onglet.
return "Monplugin";
}
}
return '';
}

/**
* Gère ce qui doit être affiché en accédant à l'onglet
* @param CommonGLPI $item
* @param type $tabnum
* @param type $withtemplate
* @return boolean
*/
static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0)
{
//Ici on vérifie qu'il s'agit bien de la partie Profil de GLPI qui est concernée
if ($item->getType() == 'Profile')
{
$prof = new self();
//On récupère l'id du profil actuel
$ID = $item->getField('id');
// on affiche le formulaire
$prof->showForm($ID);
}
return true;
}

/**
* Fonction qui affiche le formulaire du plugin
* @param type $id
* @param type $options
* @return boolean
*/
function showForm($id, $options=array())
{
global $DB;
//On récupère le chemin qui servira au formulaire
$target = $this->getFormURL();
if (isset($options['target']))
{$target = $options['target'];}
//On vérifie que l'utilisateur actuel a bien le droit d'accéder à la gestion des profils
if (!Session::haveRight("profile","w"))
{return false;}

//On définit le chemin d'accès à la bibliothèque Smarty
$cheminSmarty = $this->getAbsolutePath()."plugins/monplugin/Smarty";

// définition de l'emplacement de la bibliothèque
define('SMARTY_DIR', $cheminSmarty."/libs/");

// instanciation de la class Smarty
require_once(SMARTY_DIR . 'Smarty.class.php');
$smarty = new Smarty();

// définition des dossiers Smarty
$smarty->template_dir = $cheminSmarty."/templates/";
$smarty->compile_dir = $cheminSmarty."/templates_c/";
$smarty->config_dir = $cheminSmarty."/configs/";
$smarty->cache_dir = $cheminSmarty."/cache/";

// vérification des droits pour le groupe actuel sur le plugin
$query = "SELECT * FROM glpi_plugin_monplugin_profiles WHERE id = '$id'";
if ($result = $DB->query($query))
{
// Si le groupe est enregistré dans la base, on récupère le droit
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['right'])){$droit = $row['right'];}
$smarty->assign('droit',$droit);
}
// Sinon, on insère le groupe dans la base avec un droit à null
else
{
$query = "INSERT INTO `glpi_plugin_monplugin_profiles`(`id`, `right`) VALUES ('$id','0')";
$DB->query($query) or die($DB->error());
$smarty->assign('droit','0');
}
}
$smarty->assign('endform', HTML::closeForm(false));
$smarty->assign('id',$id);
$smarty->assign('target',$target);
$smarty->display('profile.tpl');
}

/**
* Fonction qui modifie les droits dans la base
* @param type $arrayItem (id, right)
*/
function majDroit($arrayItem)
{
global $DB;
//Mise à jour des droits
$query = "SELECT * FROM glpi_plugin_monplugin_profiles WHERE id='$arrayItem[0]'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$query = "UPDATE `glpi_plugin_monplugin_profiles` SET `right`='$arrayItem[1]' WHERE `id`=$arrayItem[0]";
$DB->query($query);
}
}
}

/**
* Vérifie si l'utilisateur courant est autorisé à utiliser le plugin
* @global type $DB
* @return boolean
*/
function estAutorise()
{
global $DB;
if (isset($_SESSION["glpiactiveprofile"]["monplugin"]))
{
if($_SESSION["glpiactiveprofile"]["monplugin"] == "w" || $_SESSION["glpiactiveprofile"]["monplugin"] == "r")
{return true;}
}
else
{
$ID = $_SESSION["glpiactiveprofile"]["id"];
$query = "SELECT * FROM glpi_plugin_monplugin_profiles WHERE id='$ID'";
if ($result = $DB->query($query))
{
$row = $DB->fetch_assoc($result);
if (!empty($row['right']))
{
$_SESSION["glpiactiveprofile"]["monplugin"] = $row['right'];
if($_SESSION["glpiactiveprofile"]["monplugin"] == "w" || $_SESSION["glpiactiveprofile"]["monplugin"] == "r")
{return true;}
}

else{$_SESSION["glpiactiveprofile"]["monplugin"] = "NULL";}
}
}
return false;
}

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("ajax/common.tabs.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin http absolu de l'application GLPI
* @return string : le chemin http absolu de l'application
*/
function getHttpPath()
{
$temp = explode("/",$_SERVER['HTTP_REFERER']);
$Ref = "";
foreach ($temp as $value)
{
if($value != "front"){$Ref.= $value."/";}
else{break;}
}
return $Ref;
}
}
?>

Dans cette class nous voyons apparaître une bibliothèque (Smarty). Il s'agit d'un moteur de template qui permet de dissocier l'affichage du code de gestion. Pour plus d'informations sur les templates Smarty vous pouvez vous référez à ce tuto : https://eric-pommereau.developpez.com/tutoriels/initiation-smarty/

Concernant la protection CRSF : La création du tocken crsf se fait via une commande interne à GLPI : HTML::closeForm(). Ici nous voyons que le résultat de cette commande est passé à Smarty avec l'argument « false ». Le fonctionnement normal de glpi aurait été de faire un echo HTML::closeForm(), mais comme nous utilisons le moteur de template, il nous faut lui envoyer puis « l'afficher » dans le template (sous forme de champ caché). L'argument false permet de ne pas afficher le résultat de la commande (1 ou 0). L'affichage sera expliqué dans la partie consacrée aux templates.

IV-B-4 - La class monplugin.class.php

Voici donc la class qui constitue le cœur de notre plugin. Dans cet exemple nous n'utiliserons qu'une seule class pour le fonctionnement du plugin, mais il est tout à fait possible de créer et utiliser plusieurs class. Je rappelle l'objectif du plugin : il s'agit de créer un « select » pour enregistrer le statut d'une machine.

Créez la class comme nous l'avons vu précédemment :

Comme pour la class profiles, les deux premières méthodes sont obligatoires :

autorisation et affichage

/**
* Class générale du plugin
*/
class PluginMonpluginMonplugin extends CommonDBTM
{
/**
* Récupère le nom de l'onglet si l'utilisateur est autorisé
* @param CommonGLPI $item
* @param type $withtemplate
* @return boolean|string
*/
function getTabNameForItem(CommonGLPI $item, $withtemplate=0)
{
$profile = new PluginMonpluginProfile();
if ($profile->estAutorise())
{if ($item->getType() == 'Computer'){return "Monplugin";}}
return '';
}

/**
* Gère ce qui doit être affiché en accédant à l'onglet
* @param CommonGLPI $item
* @param type $tabnum
* @param type $withtemplate
* @return boolean
*/
static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0)
{
if ($item->getType() == 'Computer')
{
$profile = new PluginMonpluginProfile();
if ($profile->estAutorise())
{
$monplugin = new self();
$ID = $item->getField('id');
$Name = $item->getField('name');
// j'affiche le formulaire
$monplugin->showForm($ID, $Name);
}
}
return true;
}

Comme pour la class profiles nous créons une méthode pour afficher le formulaire :

show form

/**
* Fonction qui affiche le formulaire du plugin
* @param type $id
* @param type $options
* @return boolean
*/
function showForm($id)
{
global $DB;
$cheminSmarty = $this->getAbsolutePath()."plugins/monplugin/Smarty";

// définition de l'emplacement de la bibliothèque
define('SMARTY_DIR', $cheminSmarty."/libs/");

// instanciation de la class Smarty
require_once(SMARTY_DIR . 'Smarty.class.php');
$smarty = new Smarty();

// définition des dossiers Smarty
$smarty->template_dir = $cheminSmarty."/templates/";
$smarty->compile_dir = $cheminSmarty."/templates_c/";
$smarty->config_dir = $cheminSmarty."/configs/";
$smarty->cache_dir = $cheminSmarty."/cache/";

// vérification si machine déjà enregistrée
$query = "SELECT t2.statut FROM glpi_plugin_monplugin_statut as t1,
glpi_plugin_monplugin_config as t2 WHERE t1.id_machine = '$id' AND t1.vie = '1'
AND t1.id_config = t2.id";

if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0) // Si la machine est enregistrée
{
$row = $DB->fetch_assoc($result);
if (!empty($row['statut'])) {$statut = $row['statut'];}
}
else // sinon on lui attribue le statut 1
{
$DB->query("INSERT INTO glpi_plugin_monplugin_statut (id_machine,id_config) VALUES ('$id','1')") or die($DB->error());
$query = "SELECT statut FROM glpi_plugin_monplugin_config WHERE id = '1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['statut'])) {$statut = $row['statut'];}
}
}
}
}
$smarty->assign('statut',$statut);
// Récupération de la liste des statuts
$statutListe = null;
$query = "SELECT statut FROM glpi_plugin_monplugin_config WHERE vie='1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0) // S'il y a des résultats
{
while ($row = $DB->fetch_assoc($result))
{if (!empty($row['statut'])) {$statutListe[] = $row['statut'];}}
}
}
$smarty->assign('statutListe',$statutListe);
$smarty->assign('id',$id);
$smarty->assign('httpPath',$this->getHttpPath());
$smarty->display('monplugin.tpl');
}

Cette class vérifie si le statut de la machine est déjà enregistré et envoie l'information au moteur de template. La liste des statuts disponibles est récupérée et est envoyée à Smarty. Enfin le template associé est affiché (nous passons également l'id de la machine et le httpPath).

Nous rajoutons nos méthodes de récupération des chemins absolus :

chemin absolu

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("ajax/common.tabs.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin absolu de l'instance GLPI pour le formulaire
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePathForm()
{return str_replace("front/monplugin.form.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin http absolu de l'application GLPI
* @return string : le chemin http absolu de l'application
*/
function getHttpPath()
{
$temp = explode("/",$_SERVER['HTTP_REFERER']);
$Ref = "";
foreach ($temp as $value)
{
if($value != "front"){$Ref.= $value."/";}
else{break;}
}
return $Ref;
}

Nous allons ajouter une méthode pour enregistrer les modifications de statut de la machine dans la base de données (qui sera appelée depuis un fichier Ajax) :

modifier machine

/**
* Modifie le statut d'une machine
* @param type $arrayItem
*/
function modifierMachine($statut,$id)
{
global $DB;

//Récupération de l'id statut
$query = "SELECT id FROM glpi_plugin_monplugin_config WHERE statut = '$statut'";
$idStatut = null;
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['id'])){$idStatut = $row['id'];}
}
}

// Vérification enregistrement machine
$query = "SELECT id FROM glpi_plugin_monplugin_statut WHERE id_machine = '$id'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['id']))
{
// Mise à jour
$query = "UPDATE glpi_plugin_monplugin_statut SET id_config='$idStatut' WHERE id='".$id."'";
$DB->query($query);
}
}
else //insertion
{$DB->query("INSERT INTO glpi_plugin_monplugin_statut (id_machine,id_config) VALUES ('$id','$idStatut')") or die($DB->error());}
}
}

Le fichier complet ressemblera donc à ça :

monplugin.class

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Class générale du plugin
*/
class PluginMonpluginMonplugin extends CommonDBTM
{
/**
* Récupère le nom de l'onglet si l'utilisateur est autorisé
* @param CommonGLPI $item
* @param type $withtemplate
* @return boolean|string
*/
function getTabNameForItem(CommonGLPI $item, $withtemplate=0)
{
$profile = new PluginMonpluginProfile();
if ($profile->estAutorise())
{if ($item->getType() == 'Computer'){return "Monplugin";}}
return '';
}

/**
* Gère ce qui doit être affiché en accédant à l'onglet
* @param CommonGLPI $item
* @param type $tabnum
* @param type $withtemplate
* @return boolean
*/
static function displayTabContentForItem(CommonGLPI $item, $tabnum=1, $withtemplate=0)
{
if ($item->getType() == 'Computer')
{
$profile = new PluginMonpluginProfile();
if ($profile->estAutorise())
{
$monplugin = new self();
$ID = $item->getField('id');
$Name = $item->getField('name');
// j'affiche le formulaire
$monplugin->showForm($ID, $Name);
}
}
return true;
}

/**
* Fonction qui affiche le formulaire du plugin
* @param type $id
* @param type $options
* @return boolean
*/
function showForm($id)
{
global $DB;
$cheminSmarty = $this->getAbsolutePath()."plugins/monplugin/Smarty";

// définition de l'emplacement de la bibliothèque
define('SMARTY_DIR', $cheminSmarty."/libs/");

// instanciation de la class Smarty
require_once(SMARTY_DIR . 'Smarty.class.php');
$smarty = new Smarty();

// définition des dossiers Smarty
$smarty->template_dir = $cheminSmarty."/templates/";
$smarty->compile_dir = $cheminSmarty."/templates_c/";
$smarty->config_dir = $cheminSmarty."/configs/";
$smarty->cache_dir = $cheminSmarty."/cache/";

// vérification si machine déjà enregistrée
$query = "SELECT t2.statut FROM glpi_plugin_monplugin_statut as t1,
glpi_plugin_monplugin_config as t2 WHERE t1.id_machine = '$id' AND t1.vie = '1'
AND t1.id_config = t2.id";

if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0) // Si la machine est enregistrée
{
$row = $DB->fetch_assoc($result);
if (!empty($row['statut'])) {$statut = $row['statut'];}
}
else // sinon on lui attribue le statut 1
{
$DB->query("INSERT INTO glpi_plugin_monplugin_statut (id_machine,id_config) VALUES ('$id','1')") or die($DB->error());
$query = "SELECT statut FROM glpi_plugin_monplugin_config WHERE id = '1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['statut'])) {$statut = $row['statut'];}
}
}
}
}
$smarty->assign('statut',$statut);
// Récupération de la liste des statuts
$statutListe = null;
$query = "SELECT statut FROM glpi_plugin_monplugin_config WHERE vie='1'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0) // Si il y a des résultats
{
while ($row = $DB->fetch_assoc($result))
{if (!empty($row['statut'])) {$statutListe[] = $row['statut'];}}
}
}
$smarty->assign('statutListe',$statutListe);
$smarty->assign('id',$id);
$smarty->assign('httpPath',$this->getHttpPath());
$smarty->display('monplugin.tpl');
}

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("ajax/common.tabs.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin absolu de l'instance GLPI pour le formulaire
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePathForm()
{return str_replace("front/monplugin.form.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin http absolu de l'application GLPI
* @return string : le chemin http absolu de l'application
*/
function getHttpPath()
{
$temp = explode("/",$_SERVER['HTTP_REFERER']);
$Ref = "";
foreach ($temp as $value)
{
if($value != "front"){$Ref.= $value."/";}
else{break;}
}
return $Ref;
}

/**
* Modifie le statut d'une machine
* @param type $arrayItem
*/
function modifierMachine($statut,$id)
{
global $DB;

//Récupération de l'id statut
$query = "SELECT id FROM glpi_plugin_monplugin_config WHERE statut = '$statut'";
$idStatut = null;
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['id'])){$idStatut = $row['id'];}
}
}

// Vérification enregistrement machine
$query = "SELECT id FROM glpi_plugin_monplugin_statut WHERE id_machine = '$id'";
if ($result = $DB->query($query))
{
if ($DB->numrows($result) > 0)
{
$row = $DB->fetch_assoc($result);
if (!empty($row['id']))
{
// Mise à jour
$query = "UPDATE glpi_plugin_monplugin_statut SET id_config='$idStatut' WHERE id='".$id."'";
$DB->query($query);
}
}
else //insertion
{$DB->query("INSERT INTO glpi_plugin_monplugin_statut (id_machine,id_config) VALUES ('$id','$idStatut')") or die($DB->error());}
}
}
}
?>

IV-C - Création des formulaires

Les formulaires sont des fichiers PHP vers lesquels seront envoyées les données venant de formulaires (HTML en POST). Leur fonction consiste à faire le relais avec les class et les informations reçues. Normalement les formulaires seront donc appelés depuis le plugin via du code HTML (depuis Smarty…). Il n'y a que le formulaire de configuration qui sera appelé depuis le cœur de GLPI… souvenez-vous, dans le fichier setup.php nous avions cette ligne :

setup config

$PLUGIN_HOOKS['config_page']['monplugin'] = 'front/config.form.php';

Nous allons donc commencer par ce formulaire, puis il faudra en créer un autre pour la partie profil (la class principale n'utilisera pas de formulaire). Il est bien sûr possible de créer d'autres formulaires en fonction des besoins comme pour les class.

Les formulaires sont regroupés dans un dossier intitulé « front », créez ce dossier à la racine du plugin. Le nommage de ces fichiers suit la même logique que tous les autres fichiers, la base commune étant « form ». Le nom d'un fichier aura donc cette forme : « identifiant.form.php ».

IV-C-1 - Le formulaire config.form.php

Créez le fichier config.form.php dans le dossier « front » :

Voici à quoi va ressembler le fichier, il est suffisamment commenté pour le comprendre :

config form

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Gestion du formulaire de configuration plugin monplugin
* Reçoit les informations depuis un formulaire de configuration
* Renvoie sur la page de l'item traité
*/

// Définition de la variable GLPI_ROOT obligatoire pour l'instanciation des class
define('GLPI_ROOT', getAbsolutePath());
// Récupération du fichier includes de GLPI, permet l'accès au cœur
include (GLPI_ROOT."inc/includes.php");

// Définition du chemin d'accès à Smarty
$cheminSmarty = getAbsolutePath()."plugins/monplugin/Smarty";

// définition de l'emplacement de la bibliothèque
define('SMARTY_DIR', $cheminSmarty."/libs/");

// instanciation de la class Smarty
require_once(SMARTY_DIR . 'Smarty.class.php');
$smarty = new Smarty();

// définition des dossiers Smarty
$smarty->template_dir = $cheminSmarty."/templates/";
$smarty->compile_dir = $cheminSmarty."/templates_c/";
$smarty->config_dir = $cheminSmarty."/configs/";
$smarty->cache_dir = $cheminSmarty."/cache/";

//Instanciation de la class config
$config = new PluginMonpluginConfig();

//Envoie des variables à Smarty
$smarty->assign('configuration', $config->getConfiguration());
$smarty->assign('httpPath', getHttpPath());
$smarty->assign('absolutePath',  getAbsolutePath());

//Affichage de l'entête GLPI (fonction native GLPI)
HTML::header('Configuration Plugin Monplugin');
//Affichage du plugin
$smarty->display('config.tpl');
//Affichage du pied de page GLPI (fonction native GLPI)
HTML::footer();

//========================================================================//
/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("plugins/monplugin/front/config.form.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin http absolu de l'application GLPI
* @return string : le chemin http absolu de l'application
*/
function getHttpPath()
{
$temp = explode("/",$_SERVER['HTTP_REFERER']);
$Ref = "";
foreach ($temp as $value)
{
if($value != "front"){$Ref.= $value."/";}
else{break;}
}
return $Ref;
}
?>

Ici aucune valeur de formulaire (POST) n'est attendue, nous utiliserons de l'Ajax pour gérer nos formulaires. Ce fichier permet d'afficher le formulaire de configuration appelé via le fichier setup.php (il sera appelé lorsque vous irez dans Configuration-->Plugins et que vous cliquerez sur le plugin).

IV-C-2 - Le formulaire profile.form.php

Ce formulaire est très simple, il reçoit les données pour modifier les droits d'un profil pour l'accès au plugin. Il envoie la demande à la class profile.class.php, voici le fichier commenté :

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Gestion des droits du plugin monplugin
* Reçoit les informations depuis un formulaire de configuration des droits (profile)
* Renvoie sur la page de l'item traité
*/

// récupération des chemins absolus
define('GLPI_ROOT', getAbsolutePath());
include (GLPI_ROOT."inc/includes.php");
include "../inc/profile.class.php";

if (isset($_POST["Modifier"]))
{
$arrayItem[0] = $_POST["id"];
if($_POST["droit"] == "Lecture")
{$arrayItem[1] = "r";}
elseif ($_POST["droit"] == "Modification")
{$arrayItem[1] = "w";}
else {$arrayItem[1] = "0";}
// Modification des droits dans la base
$profile = new PluginMonpluginProfile();

$profile->majDroit($arrayItem);
// Retour à la page d'appel (fonction native GLPI)
Html::back();

}

//========================================================================//
/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("plugins/monplugin/front/profile.form.php", "", $_SERVER['SCRIPT_FILENAME']);}
?>

IV-C-3 - Le formulaire monplugin.form.php

Dans notre exemple nous n'utiliserons pas de formulaire. Les actions seront réalisées via de l'Ajax. Sachez cependant qu'il est tout à fait possible d'utiliser un formulaire pour les actions du plugin.

IV-D - Création des templates

Comme dit précédemment je n'expliquerai pas le fonctionnement des templates, vous pouvez vous référer à cet article : https://eric-pommereau.developpez.com/tutoriels/initiation-smarty/ ou bien à la documentation officielle de Smarty.

Il faut au préalable récupérer le dossier d'installation de Smarty et le copier dans le dossier de votre plugin. Il faut ensuite créer les dossiers utiles à Smarty (template, templates_c, configs et cache).

Les templates, contrairement à l'organisation générale du plugin, ne seront pas stockés dans un dossier situé à la racine du plugin. Smarty suit sa propre logique. Dans les class précédentes nous avons vu cette ligne :

templates dir

$smarty->template_dir = $cheminSmarty."/templates/";

Cette méthode permet de dire à Smarty où sont stockés tous nos templates. Il vous est donc possible de choisir l'emplacement (le dossier) de vos fichiers. Personnellement, je choisis la méthode standard de Smarty, à savoir dans un sous-dossier nommé template créé dans Smarty.

IV-D-1 - Le template config.tpl

Dans le dossier template de Smarty, créez un fichier config.tpl. Ce fichier aura l'aspect suivant :

config.tpl

{*
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//
*}

<body>{literal}
<link rel="stylesheet" type="text/css" href="{/literal}{$httpPath|cat:"plugins/monplugin/css/config.css"}{literal}" media="all"/>
</head>{/literal}

<div id="info">
<table class='tab_cadre_fixe'>
<tr>
<th>Configuration du plugin Monplugin</th>
</tr>
<tr>
<td align="center" class="blue">
La configuration du module doit être renseignée avant son utilisation. <br>
</td>
</tr>
<tr>
<td align="center" >
<button id="btn-ajoutStatut" onclick="ajout()">
<img alt='' title="Ajouter" src='{$httpPath}pics/menu_add.png'>
</button>
</td>
</tr>
</table>
</div>

<div id="configMonplugin">
<table class='tab_cadre'>
<tr>
<td align="center" class="green">
<label>Statut existant</label>
</td>
<td align="center" class="green">
<label>Modifier</label>
</td>
<td align="center" class="green">
<label>Supprimer</label>
</td>
</tr>
{foreach from=$configuration item=statut}
<tr>
<td><input type="text" size='20' id='{$statut.id}' name='statut' value="{$statut.statut}" /></td>
<td width="30px">
<button id="btn-modifStatut" onclick="change('modifier',{$statut.id},document.getElementById({$statut.id}).value)">
<img alt='' title="Modifier" src='{$httpPath}pics/actualiser.png'>
</button>
</td>
<td width="30px">
<button id="btn-suppStatut" onclick="change('supprimer',{$statut.id})">
<img alt='' title="Modifier" src='{$httpPath}pics/reset.png'>
</button>
</td>
</tr>
{/foreach}
</table>
</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

<script type="text/javascript">
{literal}
/*
* Fonction qui envoie au serveur via Ajax le champ à modifier.
*/
function change(action,id_statut,value)
{
var valeur = null;
if(value){valeur = value;}
//else {var valeur = document.getElementById(id_statut).value;}

var DATA = "action="+action+"&id="+id_statut+"&valeur="+valeur;

if (window.XMLHttpRequest){xmlhttp=new XMLHttpRequest();}
else{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}

xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{
alert("L'élément a été modifié avec succès.");
document.getElementById("configMonplugin").innerHTML=xmlhttp.responseText;
}
}
xmlhttp.open("POST","{/literal}{$httpPath|cat:"plugins/monplugin/ajax/config.ajax.php"}{literal}",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(DATA);
}

/*
* Fonction qui envoie au serveur via Ajax le champ à modifier.
*/
function ajout()
{
var form = "<table class='tab_cadre'> \n\
<tr> <td> Statut: <input type=\"text\" id=\"formStatut\"> </td> </tr> \n\
<tr>\n\
<td align=\"center\">\n\
<button onclick=\"change('ajouter','formStatut',document.getElementById('formStatut').value)\"> \n\
ajouter </button>\n\
</td>\n\
</tr> \n\
</table>"
document.getElementById("configMonplugin").innerHTML=form ;
}

/*
* Gestion des info-bulles
*/
$(document).ready(function()
{
// Sélectionner tous les liens ayant l'attribut rel valant tooltip
$('label[rel=tooltip]').mouseover(
function(e)
{
// Récupérer la valeur de l'attribut title et l'assigner à une variable
var tip = $(this).attr('title');
// Supprimer la valeur de l'attribut title pour éviter l'info-bulle native
$(this).attr('title','');
// Insérer notre info-bulle avec son texte dans la page
$(this).append('<div id="tooltip"><div class="tipHeader"></div><div class="tipBody">' + tip + '</div><div class="tipFooter"></div></div>');

// Ajuster les coordonnées de l'info-bulle
$('#tooltip').css('top', e.pageY + 10 );
$('#tooltip').css('left', e.pageX + 20 );

// Faire apparaître l'info-bulle avec un effet fadeIn
$('#tooltip').fadeIn('500');
$('#tooltip').fadeTo('10',0.8);

}
).mousemove(
function(e)
{
// Ajuster la position de l'info-bulle au déplacement de la souris
$('#tooltip').css('top', e.pageY + 10 );
$('#tooltip').css('left', e.pageX + 20 );
}
).mouseout(
function()
{
// Réaffecter la valeur de l'attribut title
$(this).attr('title',$('.tipBody').html());

// Supprimer notre info-bulle
$(this).children('div#tooltip').remove();

}
);
});
{/literal}

</script>

Ce fichier est appelé par config.form.php lorsque nous cliquons sur le plugin dans le menu « Configuration-->Plugins ». Il contient du JavaScript et de l'Ajax qui permettent d'envoyer les informations à modifier au serveur sans avoir à recharger la page. Il n'est bien sûr pas obligatoire d'utiliser ces fonctionnalités (il suffit d'utiliser la balise <Form> avec comme cible le fichier config.form.php, voir le fichier profile.tpl comme exemple).

Le fichier Ajax sera abordé plus loin dans le tutoriel.

IV-D-2 - Le template profile.tpl

Créez le fichier profile.tpl (toujours dans le dossier des templates de Smarty). Ce fichier est appelé par la class profile.class.php lorsque nous accédons à la gestion des profils (souvenez-vous nous avons créé un onglet dans cette class… cet onglet doit apparaître dans la gestion du profil d'un utilisateur ou groupe « Administration-->Profils-->un profil…) :

Le fichier se composera de cette façon :

profile.tpl

{*
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//
*}
<FORM METHOD=POST ACTION={$target}>
<table class='tab_cadre_fixe'>
<tr>
<th colspan="2">Gestion des droits pour le plugin monplugin:</th>
</tr>
<tr>
<td align="center" colspan="2">
<SELECT name="droit" size="1">
{if $droit eq 'r'}
<OPTION selected>Lecture
<OPTION>Modification
<OPTION>Aucun
{elseif $droit eq 'w'}
<OPTION>Lecture
<OPTION selected>Modification
<OPTION>Aucun
{else}
<OPTION>Lecture
<OPTION>Modification
<OPTION selected>Aucun
{/if}
</SELECT>
</td>
</tr>
<tr>
<td align="center">
<input type='hidden' name='id' value={$id}>
<input type='submit' name='Modifier' class='submit' value="Modifier">
</td>
</tr>
</table>
{$endform}

Ici j'utilise le fonctionnement standard des formulaires. Le template envoie les informations au fichier profile.form.php qui instancie la class profile.class.php et permet l'enregistrement ou la modification des données dans la base. Le fichier profile.form.php utilise une fonction native de GLPI « Html::back() ; » qui permet de retourner directement sur la page en cours.

Concernant la protection CRSF : Nous avons vu dans la partie concernant la class du profil qu'une commande GLPI était envoyée au moteur : $smarty->assign('endform', HTML::closeForm(false));

Vous remarquerez également que le formulaire ne dispose pas de balise </FORM> mais à la place nous avons l'affichage de la commande via : {$endform}. Voilà, il n'y a rien d'autre à faire pour gérer cette fonctionnalité. Souvenez-vous juste qu'elle est nécessaire pour la vérification du plugin (setup.php).

IV-D-3 - Le template monplugin.tpl

Le template principal (monplugin.tpl) du projet aura cette forme :

monplugin.tpl

{*
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//
*}
<table class='tab_cadre_fixe'>
<tr>
<th colspan="2">Monplugin</th>
</tr>
<tr>
<td align="center" class="blue" colspan="2">
Ce module vous permet de renseigner le statut de l'ordinateur <br>
</td>
</tr>
<tr>
<td align="right" ><label>Statut:</label></td>
<td align="left" width="50%">
<SELECT id="statut" size="1" onchange="change({$id})">
{foreach from=$statutListe item="item"}
{if $statut eq $item}
<OPTION selected>{$item}
{else}
<OPTION>{$item}
{/if}
{/foreach}
</SELECT>
</td>
</tr>
</table>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

<script type="text/javascript">
{literal}
/*
* Fonction qui envoie au serveur via Ajax le champ à modifier.
*/
function change(id)
{
var liste, valeur;
liste = document.getElementById("statut");
valeur = liste.options[liste.selectedIndex].value;
var DATA = "statut="+valeur+"&id="+id;

if (window.XMLHttpRequest){xmlhttp=new XMLHttpRequest();}
else{xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");}

xmlhttp.onreadystatechange=function()
{
if (xmlhttp.readyState==4 && xmlhttp.status==200)
{alert("L'élément a été modifié avec succès.");}
}
xmlhttp.open("POST","{/literal}{$httpPath|cat:"plugins/monplugin/ajax/monplugin.ajax.php"}{literal}",true);
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xmlhttp.send(DATA);
}
{/literal}
</script>

Comme nous pouvons le constater, nous utilisons encore de l'Ajax pour la modification du champ. Nous aurions tout aussi bien pu utiliser le fonctionnement standard du formulaire.

Lorsque nous sélectionnons une valeur du « select », l'information est envoyée au fichier Ajax associé qui instancie la class principale et appelle la méthode qui permet la modification dans la base de données.

IV-E - Création des fichiers AJAX

Nous avons vu deux méthodes distinctes pour traiter les informations envoyées depuis les formulaires. La première est d'utiliser la méthode standard HTML (POST) depuis une balise <FORM> vers un fichier PHP. La deuxième (AJAX) envoie également les informations vers un fichier PHP, la seule différence est que la page n'est pas rechargée… c'est le fonctionnement typique d'Ajax. Pour plus d'informations, je vous conseille la lecture de la FAQ concernant cette technologie : https://javascript.developpez.com/faq/javascript/?page=Ajax.

Comme pour les class et les formulaires, nous allons créer un dossier à la racine de notre plugin que nous nommerons Ajax et nous y enregistrerons nos fichiers.

IV-E-1 - Le fichier Ajax config.ajax.php

Voici l'ensemble du fichier :

config.ajax.php

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Traite toutes les demandes Ajax du plugin
*/
define('GLPI_ROOT', getAbsolutePath());
include (GLPI_ROOT."inc/includes.php");

//Instanciation de la class config
$config = new PluginMonpluginConfig();

// appelle la méthode correspondant à l'action demandée
if(isset($_POST['action']))
{
if($_POST['action'] == "ajouter")
{$config->setConfiguration(null,$_POST['valeur']);}
if($_POST['action'] == "modifier")
{$config->setConfiguration($_POST['id'],$_POST['valeur']);}
if($_POST['action'] == "supprimer")
{$config->setConfiguration($_POST['id'],"delStatut");}
echo getRetour($config);
}

//renvoie la partie qui doit être modifiée sur la page
function getRetour($config)
{
$retour = "<table class='tab_cadre'>";
$retour .= "<tr>";
$retour .= "<td align=\"center\" class=\"green\">";
$retour .= "<label>Statut existant</label>";
$retour .= "</td>";
$retour .= "<td align=\"center\" class=\"green\">";
$retour .= "<label>Modifier</label> ";
$retour .= "</td>";
$retour .= "<td align=\"center\" class=\"green\">";
$retour .= "<label>Supprimer</label>";
$retour .= "</td>";
$retour .= "</tr>";
foreach ($config->getConfiguration() as $statut)
{
$retour .= "<tr>";
$retour .= "<td><input type=\"text\" size='20' id='".$statut["id"]."' name='statut' value=\"".$statut["statut"]."\" /></td>";
$retour .= "<td width=\"30px\">";
$retour .= "<button id=\"btn-modifStatut\" onclick=\"change('modifier',".$statut["id"].",document.getElementById(".$statut["id"].").value)\">";
$retour .= "<img alt='' title=\"Modifier\" src='".getHttpPath()."pics/actualiser.png'>";
$retour .= "</button>";
$retour .= "</td>";
$retour .= "<td width=\"30px\">";
$retour .= "<button id=\"btn-suppStatut\" onclick=\"change('supprimer',".$statut["id"].")\">";
$retour .= "<img alt='' title=\"Modifier\" src='".getHttpPath()."pics/reset.png'>";
$retour .= "</button>";
$retour .= "</td>";
$retour .= "</tr>";
}
$retour .= "</table>";
return $retour;
}

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("plugins/monplugin/ajax/config.ajax.php", "", $_SERVER['SCRIPT_FILENAME']);}

/**
* Récupère le chemin http absolu de l'application GLPI
* @return string : le chemin http absolu de l'application
*/
function getHttpPath()
{
$temp = explode("/",$_SERVER['HTTP_REFERER']);
$Ref = "";
foreach ($temp as $value)
{
if($value != "plugins"){$Ref.= $value."/";}
else{break;}
}
return $Ref;
}

?>

Ce fichier instancie la class config.class et fait appel à certaines de ses méthodes. Il renvoie un format texte qui viendra remplacer la partie à modifier du template.

IV-E-2 - Le fichier Ajax monplugin.ajax.php

Le dernier fichier du plugin aura donc cette forme :

monplugin.ajax.php

<?php
//============================================================================//
//==    Plugin pour GLPI - Dévelloppeur: Viduc (Fleury Tristan) - ©2013     ==//
//==            http://viduc.sugarbox.fr - viduc@sugarbox.fr                ==//
//============================================================================//

/**
* Traite toutes les demandes Ajax du plugin
*/
define('GLPI_ROOT', getAbsolutePath());
include (GLPI_ROOT."inc/includes.php");

//Instanciation de la class config
$config = new PluginMonpluginMonplugin();

$config->modifierMachine($_POST['statut'],$_POST['id']);

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("plugins/monplugin/ajax/monplugin.ajax.php", "", $_SERVER['SCRIPT_FILENAME']);}

?>

Contrairement à l'autre fichier Ajax, celui-ci ne renvoie rien, il n'y a pas d'information à modifier sur la page web.

V - Installation et configuration du plugin

V-A - Modifier les droits

Avant d'aller plus loin, il est probable qu'il soit nécessaire de modifier les droits d'accès au dossier de votre plugin. En fonction de la manière dont vous travaillez, il est possible que certains dossiers que vous avez créés n'aient pas les bons droits d'écriture (entre autres pour Smarty). Je vous conseille donc de faire un :

chown

chown www-data:www-data -R monplugin
chmod 775 -R monplugin

V-B - Administration

Notre plugin est enfin prêt à fonctionner, mais avant de l'utiliser, il faut l'installer et le configurer.

Nous avons vu au début du tutoriel que le plugin devrait apparaître dans la liste des plugins du menu administration :

Cliquez sur « Installer », le nom du plugin devrait devenir vert et « installer » doit être remplacé par « activer ». Cliquez sur « activer ».

À ce stade trois nouvelles tables doivent être présentes dans la base de données de GLPI (glpi_plugin_monplugin_config, profiles et statut). Vérifiez que c'est bien le cas via PhPMyadmin :

Cliquez ensuite sur le nom du plugin pour accéder au formulaire de configuration (que nous avons créé ;)).

Pour le moment aucun statut n'est enregistré. Créez-en quelques-uns pour tester en cliquant sur le +.

Vous pouvez tester les fonctions de modification et suppression. Ici tout le fonctionnement des formulaires est géré avec de l'Ajax.

Le plugin est désormais installé et configuré. Vous pouvez tester de le désinstaller complètement et vérifier que les trois tables ont bien été supprimées de la base de données.

V-C - Profil

Pour gérer les accès au plugin, allez dans Administration-->Profils. Cliquez sur le nom du profil dont vous souhaitez ajouter ou modifier les droits. Pour rappel le compte utilisé pour installer le plugin a déjà hérité des droits maximums (normalement c'est le compte administrateur…).

Vous devriez avoir un nouvel onglet nommé « Monplugin », cliquez dessus :

Choisissez le droit que vous souhaitez (lecture ou modification), sachant que pour l'utilisation même du plugin, le droit de lecture suffit…

VI - Utilisation du plugin

Il nous faut maintenant tester le plugin, allez dans Inventaire-->Ordinateurs puis cliquez sur la fiche d'un ordinateur. Comme pour la gestion du profil, un nouvel onglet nommé « Monplugin » est apparu. Cliquez dessus. En bas de la fiche, vous devez avoir ceci :

Un statut est déjà enregistré, en effet j'ai choisi de donner le premier statut de la base disponible si la machine n'en avait pas d'enregistré. Testez le changement de statut.

Le plugin est désormais opérationnel.

VII - Téléchargement du plugin

Voici le plugin complet : monplugin

VIII - Conseils et informations

VIII-A - Base de données

Il me semble important de revenir rapidement sur la gestion de la base de données. Nous avons vu que nous avions fait plusieurs requêtes sur la base GLPI sans jamais avoir ouvert de connexion PHP vers la base. Nous utilisons en fait la connexion réalisée par le cœur de GLPI (le moteur) via la variable globale $DB (il faut bien sûr étendre la class CommonDBTM). C'est une fonction très importante et très pratique, car nous pouvons développer un plugin sans jamais avoir besoin de se soucier du nom de la base, des identifiants, etc. N'oubliez pas que seule une class peut utiliser cette méthode, c'est pourquoi depuis les fichiers PHP de formulaires ou d'Ajax nous instancions nos class (du dossier inc) et nous leur envoyons le traitement à faire (dans l'idée nous aurions pu le faire directement depuis le fichier du formulaire ou Ajax si nous n'utilisions pas cette méthode…).

VIII-B - Fonctions et méthodes internes

Comme pour la gestion de la base de données, il existe d'autres méthodes de class accessibles (fonction mail, PDF, etc.). Cependant il semblerait qu'aucune information officielle n'existe sur le sujet. Il n'est donc pas aisé de les utiliser proprement. Vous pouvez regarder dans le dossier inc à la racine de votre installation de GLPI, toutes les méthodes internes s'y trouvent…

Vos class ont normalement accès à ces autres class (nous avons étendu CommonDBTM plusieurs fois). Pour vos fichiers formulaire ou Ajax, il ne faut pas oublier ceci :

define

// Définition de la variable GLPI_ROOT obligatoire pour l'instanciation des class
define('GLPI_ROOT', getAbsolutePath());
// Récupération du fichier includes de GLPI, permet l'accès au cœur
include (GLPI_ROOT."inc/includes.php");

VIII-C - Utiliser d'autres bibliothèques externes

Il est possible d'utiliser d'autres bibliothèques externes comme nous le faisons pour Smarty. Personnellement j'utilise fpdf pour la création de fichiers PDF et adldap pour la gestion des ldap.

L'important est de bien gérer les chemins d'accès. Référez-vous à mon exemple de récupération de chemin pour Smarty :

chemin

/**
* Récupère le chemin absolu de l'instance GLPI
* @return String : le chemin absolu (racine principale)
*/
function getAbsolutePath()
{return str_replace("plugins/monplugin/front/config.form.php", "", $_SERVER['SCRIPT_FILENAME']);}

VIII-D - Compatibilité

Un problème récurrent des plugins GLPI est la compatibilité ascendante. En effet, lors de mises à jour importantes de GLPI, notre plugin ne pourrait plus être utilisable. Une fonction a été créée dans le fichier setup.php pour valider les versions compatibles pour votre plugin. Cependant, le fonctionnement que je propose devrait limiter grandement les problèmes de compatibilité, car en utilisant des bibliothèques externes vous serez moins touché par ce souci. Il est cependant important de tester lors de mises à jour de GLPI la compatibilité de vos plugins et d'apporter des correctifs si nécessaires.

VIII-E - Sources d'informations

Il existe un forum officiel pour GLPI : http://www.glpi-project.org/forum/

Voici d'autres liens vers des tutoriels que j'ai pu trouver :

http://www.maje.biz/downloads/glpi-plugin.pdf

http://liebesbier.com/glpi-plugins/

https://forge.indepnet.net/projects/plugins/wiki/Fr_CreatePlugin084

http://www.fortisfio.com/?p=581

Si vous voulez un autre exemple de plugin créé selon ma méthode vous pouvez tester un de mes plugins : https://forge.indepnet.net/projects/reforme

IX - Conclusion et remerciements

J'espère avoir pu vous apporter un nouvel éclairage sur la création de plugins pour GLPI. Mon tutoriel n'est sûrement pas parfait et ne correspond pas forcément aux critères établis par la communauté GLPI (entre autres par l'utilisation de Smarty). J'ai voulu faire ce document suite à mes nombreuses heures passées à essayer de comprendre comment réaliser un projet de la sorte, il faut dire que les informations existantes à ce sujet sont très peu nombreuses et pas suffisamment explicites…

N'hésitez pas à me faire des remarques, des suggestions et autres propositions…

Je tiens à remercier l'équipe complète de DVP pour son travail en général et plus précisément les correcteurs zoom61 et ClaudeLELOUP qui auront eu la gentillesse de m'accorder un peu de temps.