diff --git a/doc/LdapSaisie.docbook b/doc/LdapSaisie.docbook
index b9c40656..2aa1f492 100644
--- a/doc/LdapSaisie.docbook
+++ b/doc/LdapSaisie.docbook
@@ -22,6 +22,7 @@ book SYSTEM "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
+
]>
@@ -51,4 +52,6 @@ book SYSTEM "http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd"
&upgrade;
&conf;
+
+&contrib;
diff --git a/doc/contrib/contrib.docbook b/doc/contrib/contrib.docbook
index 827c7f1e..0f0af088 100644
--- a/doc/contrib/contrib.docbook
+++ b/doc/contrib/contrib.docbook
@@ -7,22 +7,22 @@
LSaddons
- Les &LSaddons; sont utilisés pour implémenter dans &LdapSaisie; des fonctionnalités spécifiques telque le support du famille d'attribut spécifiques (POSIX, Samba, ...) ou encore des tâches communes et génériques (envoi de mails, connexion FTP, ...). Les &LSaddons; vont permettront également d'adapter &LdapSaisie; à vos besoins en écrivrant par exemple les fonctions appelées par les déclencheurs.
+ Les &LSaddons; sont utilisés pour implémenter dans &LdapSaisie; des fonctionnalités spécifiques tel que le support d'une famille d'attributs spécifiques (POSIX, Samba, SUPANN…) ou encore des tâches communes et génériques (envoi de mails, connexion FTP…). Les &LSaddons; vous permettront également d'adapter &LdapSaisie; à vos besoins spécifiques en écrivant par exemple les fonctions appelées par les déclencheurs ou encore écrire des &customSearchActions; sur des recherches ou des &customActions; sur des &LSobjects;.Structure d'écriture
- L'écriture d'un &LSaddon; doit respecter une structure suffisament souple afin de ne pas être un frein à vos contributions, tout en permettant d'assurer la bonne intégration de votre contibution au projet. Le code que vous ecrirez sera répartis dans deux fichiers :
+ L'écriture d'un &LSaddon; doit respecter une structure suffisamment souple afin de ne pas être un frein à vos contributions, tout en permettant d'assurer la bonne intégration de votre contribution au projet. Le code que vous écrirez sera réparti dans deux fichiers :conf/LSaddons/config.LSaddons.[addon name].php
- Ce fichier contiendra la configuration de votre LSadons. On y retrouvera la déclaration de constances et/ou variables de configuration permettant d'adapter votre LSaddon à une installation et à un environement.
+ Ce fichier contiendra la configuration de votre &LSaddon;. On y retrouvera la déclaration de constances et/ou variables de configuration permettant d'adapter votre &LSaddon; à une installation et à un environnement.includes/addons/LSaddons.[addon name].php
- Ce fichier contiendra le code à proprement dit de votre LSaddons.
+ Ce fichier contiendra le code à proprement dit de votre &LSaddon;.
@@ -31,90 +31,490 @@
Structure du fichier includes/addons/LSaddons.[addon name].php
- *
- * @retval boolean true if my addon is totaly supported, false in other cases
- **/
-function LSaddon_myaddon_support() {
-
- $retval=true;
-
- // Dependance de librairie
- if ( !class_exists('mylib') ) {
- if ( !LSsession::includeFile(LS_LIB_DIR . 'class.mylib.php') ) {
- LSerror :: addErrorCode('MYADDON_SUPPORT_01');
- $retval=false;
- }
- }
-
-
- $MUST_DEFINE_CONST= array(
- 'LS_MYADDON_CONF_O1',
- 'LS_MYADDON_CONF_O2',
- ...
+ // Support error messages
+ LSerror :: defineError('MYADDON_SUPPORT_01',
+ ___("MYADDON Support : Unable to load %{dep}.")
);
- foreach($MUST_DEFINE_CONST as $const) {
- if ( (!defined($const)) || (constant($const) == "")) {
- LSerror :: addErrorCode('MYADDON_SUPPORT_02',$const);
- $retval=false;
+ LSerror :: defineError('MYADDON_SUPPORT_02',
+ ___("MYADDON Support : The constant %{const} is not defined.")
+ );
+
+ // Other orror messages
+ LSerror :: defineError('MYADDON_01',
+ ___("An error : %{msg}.")
+ );
+
+ LSerror :: defineError('MYADDON_02',
+ ___("An other error about %{about} : %{msg}")
+ );
+
+ LSerror :: defineError('MYADDON_03',
+ ___("Unknown error.")
+ );
+
+ /**
+ * Verify support of my addon by LdapSaisie
+ *
+ * @author My Name
+ *
+ * @retval boolean true if my addon is totaly supported, false in other cases
+ **/
+ function LSaddon_myaddon_support() {
+
+ $retval=true;
+
+ // Check/load dependencies
+ if ( !class_exists('mylib') ) {
+ if ( !LSsession::includeFile(LS_LIB_DIR . 'class.mylib.php') ) {
+ LSerror :: addErrorCode('MYADDON_SUPPORT_01', 'mylib');
+ $retval=false;
+ }
}
+
+
+ $MUST_DEFINE_CONST= array(
+ 'LS_MYADDON_CONF_O1',
+ 'LS_MYADDON_CONF_O2',
+ ...
+ );
+
+ foreach($MUST_DEFINE_CONST as $const) {
+ if ( (!defined($const)) || (constant($const) == "")) {
+ LSerror :: addErrorCode('MYADDON_SUPPORT_02',$const);
+ $retval=false;
+ }
+ }
+
+ if ($retval) {
+ // Register LSaddon view using LSsession :: registerLSaddonView()
+
+ if (php_sapi_name() == 'cli') {
+ // Register LSaddon CLI command using LScli :: add_command()
+ }
+ }
+
+ return $retval;
}
- // Other check ...
+ /**
+ * My first function
+ *
+ * Description of this wonderfull function
+ *
+ * @author My Name
+ *
+ * @retval [type(s) of returned values (pipe separator)] Description of the return of this function
+ **/
+ function myaddon_first_function($arg1, $arg2) {
+ // Do some stuff
+ if (something) {
+ LSerror :: addErrorCode(
+ 'MYADDON_01',
+ 'something went wrong' // Error LSformat unique argument
+ );
+ return false;
+ }
- return $retval;
-}
+ if (something else) {
+ LSerror :: addErrorCode(
+ 'MYADDON_02',
+ array( // Error LSformat arguments
+ 'about' => 'second step',
+ 'msg' => 'something went wrong'
+ )
+ );
+ return false;
+ }
-/**
- * Ma première fonction.
- *
- * Ce qu'elle fait.
- *
- * @author My Name
- *
- * @retval [type valeur retournée] [Signification de la valeur retournée]
- **/
-function myaddon_first_function($arg1,$arg2) {
+ if (still something else) {
+ LSerror :: addErrorCode('MYADDON_03'); // Error without argument
+ return false;
+ }
+ return true;
+ }
- ...
+ [...]
-}
+ // Defined custom CLI commands functions only on CLI context
+ if (php_sapi_name() != 'cli')
+ return true; // Always return true to avoid some warning in log
-?>
+ // Defined functions handling custom CLI commands and optionnaly
+ // their arguments autocompleter functions.
]]>
+Par convention, la structure de ce fichier est toujours à peu près la même:
+
+
+ On déclare tout d'abord les messages d'erreurs qui seront potentiellement émis par notre &LSaddon; en commençant par
+ les messages d'erreurs liés au support de cet &LSaddon;. On utilise pour cela la méthode LSerror :: defineError() qui
+ attends en premier argument, l'identifiant du message d'erreur et en tant que second argument, le &LSformat; du message d'erreur. Par
+ convention, les identifiants des messages d'erreurs seront en majuscule et préfixés du nom du &LSaddon;.
+ On déclare ensuite une fonction LSaddon_[myaddon]_support qui sera exécuté lors du chargement de
+ l'addon et qui permettra de s'assurer du support de celui-ci. Cette fonction devra retourner True si c'est le cas ou
+ False dans le cas contraire.
+ Cette fonction s'assura notamment :
+
+ que les librairies dont l'addon dépends sont bien chargées et fonctionnelles ;
+ que les variables et constantes de configuration sont bien définies ;
+ de déclarer les vues personnalisées fournies par cet &LSaddon; ;
+ de déclarer les commandes CLI personnalisées fournies par cet &LSaddon; ;
+
+
+ On déclare ensuite les fonctions, classes et éléments fournies et manipulés par l'addon.
+ Si notre addon offre des commandes CLI
+ personnalisées, les fonctions les implémentants ne seront définies, dans un souci de performance, que dans un contexte
+ ou elles seraient potentiellement appelables, c'est à dire dans un contexte d'exécution CLI. Pour cela,
+ nous utilisons communément la fonction php_sapi_name pour déterminer le contexte d'exécution et si celui-ci
+ vaut cli, nous stoppons l'exécution du reste du code du fichier via un return true.
+ Il est important dans ce contexte de ne jamais retourner autre chose que True pour éviter tout message
+ d'erreur inutile dans les logs.
+
+ On déclare, pour finir, les fonctions implémentant les commandes
+ CLI personnalisées et leur éventuelle fonction gérant l'autocomplétion des arguments qu'elles acceptent.
+
+
+
+
+
+ Les vues personnalisées
+
+ Les &LSaddons; peuvent fournir des vues personnalisées qui seront accessibles à tout ou parties des utilisateurs de l'application.
+ Ce filtrage d'accès sera fait en utilisant les &LSprofiles; de l'utilisateur connecté sur la racine
+ courante de l'annuaire LDAP.
+
+ Pour mettre en place une telle vue personnalisée, il est nécessaire de :
+
+ Déclarer cette vue dans la fonction LSaddon_[addon]_support de l'addon à l'aide de la méthode
+ LSsession :: registerLSaddonView ;
+ Déclarer la fonction implémentant cette vue. Cette fonction n'acceptera aucun paramètre et ne retournera rien.
+ Elle devra en outre s'occuper de définir son fichier template et charger les dépendances de ce dernier (fichiers CSS
+ & JS, variables...).
+
+
+
+ Pour implémenter une telle vue personnalisée, vous pouvez vous inspirer de l'exemple fourni ci-dessous ou encore des vues fournies
+ par les autres &LSaddons; (par exemple, l'addon exportSearchResultAsCSV).
+
+
+
+
+ Structure du fichier includes/addons/LSaddons.[addon name].php
+
+ *
+ * @retval void
+ **/
+ function myaddon_view() {
+ // Do some stuff and set some template variables
+ $list = array ([...]);
+ LStemplate :: assign('list', $list);
+
+ // Load some CSS & JS files need on this view
+ LStemplate :: addCssFile('LSaddon_myadon.css');
+ LStemplate :: addJSscript('LSaddon_myadon.js');
+
+ // Set template file of the view
+ LSsession :: setTemplate('LSaddon_myadon_view.tpl');
+ }
+ ]]>
+
+
+
+
+ Les commandes CLI personnalisées
+
+ Les &LSaddons; peuvent fournir des commandes CLI personnalisées qui seront accessibles via la commande
+ ldapsaisie fournie avec l'application. Cela peut, par exemple, vous permettre de rendre accessible en ligne de commandes
+ une procédure implémentée dans le code d'LdapSaisie et vous permettre de mettre en place une tâche planifiée exécutant cette procédure
+ régulièrement.
+
+ Pour mettre en place une telle commande CLI personnalisée, il est nécessaire de :
+
+ Déclarer cette vue dans la fonction LSaddon_[addon]_support de l'addon à l'aide de la méthode
+ LScli :: add_command ;
+ Déclarer la fonction implémentant cette commande CLI personnalisée. Cette fonction acceptera,
+ en tant qu'unique paramètre, un tableau des arguments reçus lors de l'exécution de la commande et retournera True
+ ou False en cas de succès/d'erreur d'exécution de la commande. Cette valeur de retour influencera le code retourné
+ par la commande : 0 en cas de succès, 1 en cas d'erreur.
+ Bien que cela ne soit pas obligatoire, il sera également possible de déclarer une fonction permettant l'autocomplétion
+ des arguments acceptés par la commande.
+ Cette méthode recevra en paramètre:
+
+
+ $command_args
+
+ Un tableau des arguments déjà reçus par la commande.
+
+
+
+
+ $comp_word_num
+
+ Un entier indiquant le rang de l'argument que l'autocomplétion tente de compléter. Il peut s'agir du rang d'un
+ paramètre déjà fourni et présent dans le tableau $command_args ou bien d'un rang supérieur aux nombres
+ d'arguments déjà fournis à la commande et dans ce cas il s'agira d'autocompléter tous potentiels autre argument que pourrait
+ accepter cette commande.
+
+
+
+
+ $comp_word
+
+ Une chaîne de caractères correspondant à ce qu'a déjà saisi l'utilisateur de l'argument que l'on tente
+ d'autocompléter. Cette chaîne de caractères peut être vide ou non, en fonction de s'il s'agit d'un nouvel argument à
+ autocompléter ou non.
+
+
+
+
+ $opts
+
+ Un tableau des potentiels arguments globaux acceptés par LScli dans le contexte actuel (par
+ exemple, -d ou --debug pour l'activation du mode debug). La réponse de cette fonction
+ devra inclure ces potentiels arguments si le contexte d'autocomplétion si prête (nouvel argument par exemple).
+
+
+
+
+
+ Pour finir, cette fonction devra retourner un tableau des potentielles valeurs que pourrait prendre l'argument autocomplété. Si
+ une unique proposition est faite à l'utilisateur, celle-ci sera automatiquement proposée à l'utilisateur et à défaut, la liste des
+ valeurs possibles lui seront affichées.
+ Pour vous aider dans l'écrire d'une telle méthode d'autocomplétion, des méthodes statiques sont fournies par la classe
+ LScli pour les autocomplétions les plus courantes:
+
+
+
+
+ LScli :: autocomplete_class_name()
+
+ Autocomplétion du nom d'une classe PHP.
+
+
+
+
+ LScli :: autocomplete_addon_name()
+
+ Autocomplétion du nom d'un &LSaddon;.
+
+
+
+
+ LScli :: autocomplete_int()
+
+ Autocomplétion d'un nombre entier.
+
+
+
+
+ LScli :: autocomplete_LSobject_types()
+
+ Autocomplétion du nom d'un type d'&LSobject;.
+
+
+
+
+ LScli :: autocomplete_LSobject_dn()
+
+ Autocomplétion du DN d'un type précis d'&LSobject; de l'annuaire.
+
+
+
+
+
+ Par ailleurs, la méthode LScli :: autocomplete_opts() vous facilitera la construction de la liste des valeurs
+ d'autocomplétion de l'argument courant en fonction de ce qui a déjà été saisi par l'utilisateur (paramètre
+ $comp_word). Cette méthode s'occupera en l'occurrence de filtrer parmi toutes les valeurs contextuelles possibles,
+ celles qui correspondent au préfixe fourni par l'utilisateur.
+
+
+
+
+ Pour implémenter une telle commande CLI personnalisée, vous pouvez vous inspirer de l'exemple fourni ci-dessous
+ ou encore des commandes CLI fournies par les autres &LSaddons; ou classes PHP de l'application.
+
+
+ Structure du fichier includes/addons/LSaddons.[addon name].php
+
+ *
+ * @retval boolean True on succes, false otherwise
+ **/
+ function cli_my_custom_cli_cmd($command_args) {
+ $objType = null;
+ $dn = null;
+ $force_mode = false;
+ foreach ($command_args as $arg) {
+ if ($arg == '-f' || $arg == '--force')
+ $force_mode = true;
+ elseif (is_null($objType)) {
+ $objType = $arg;
+ }
+ elseif (is_null($dn)) {
+ $dn = $arg;
+ }
+ else
+ LScli :: usage("Invalid $arg parameter.");
+ }
+
+ if (is_null($objType) || is_null($dn))
+ LScli :: usage('You must provide LSobject type and DN.');
+
+ if (!LSsession :: loadLSobject($objType))
+ return false;
+
+ $obj = new $objType();
+ if (!$obj->loadData($dn)) {
+ self :: log_fatal("Fail to load object $dn data from LDAP");
+ return false;
+ }
+
+ // Do some stuff on loaded object
+ [...]
+
+ return true;
+ }
+
+ /**
+ * Args autocompleter for CLI my_custom_cli_cmd command
+ *
+ * @param[in] $command_args array List of already typed words of the command
+ * @param[in] $comp_word_num int The command word number to autocomplete
+ * @param[in] $comp_word string The command word to autocomplete
+ * @param[in] $opts array List of global available options
+ *
+ * @retval array List of available options for the word to autocomplete
+ **/
+ public static function cli_my_custom_cli_cmd_autocompleter($command_args, $comp_word_num, $comp_word, $opts) {
+ $opts = array_merge($opts, array ('-f', '--force'));
+
+ // Handle positional args
+ $objType = null;
+ $objType_arg_num = null;
+ $dn = null;
+ $dn_arg_num = null;
+ for ($i=0; $i < count($command_args); $i++) {
+ if (!in_array($command_args[$i], $opts)) {
+ // If object type not defined
+ if (is_null($objType)) {
+ // Defined it
+ $objType = $command_args[$i];
+ LScli :: unquote_word($objType);
+ $objType_arg_num = $i;
+
+ // Check object type exists
+ $objTypes = LScli :: autocomplete_LSobject_types($objType);
+
+ // Load it if exist and not trying to complete it
+ if (in_array($objType, $objTypes) && $i != $comp_word_num) {
+ LSsession :: loadLSobject($objType, false);
+ }
+ }
+ elseif (is_null($dn)) {
+ $dn = $command_args[$i];
+ LScli :: unquote_word($dn);
+ $dn_arg_num = $i;
+ }
+ }
+ }
+
+ // If objType not already choiced (or currently autocomplete), add LSobject types to available options
+ if (!$objType || $objType_arg_num == $comp_word_num)
+ $opts = array_merge($opts, LScli :: autocomplete_LSobject_types($comp_word));
+
+ // If dn not alreay choiced (or currently autocomplete), try autocomplete it
+ elseif (!$dn || $dn_arg_num == $comp_word_num)
+ $opts = array_merge($opts, LScli :: autocomplete_LSobject_dn($objType, $comp_word));
+
+ return LScli :: autocomplete_opts($opts, $comp_word);
+ }
+ ]]>
+
+
+