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 <emphasis>CLI</emphasis> 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); + } + ]]> + + +