Contribution Comme tout projet libre qui se respecte, les contributions à LdapSaisie sont les bienvenues. Ce chapitre explique les possibilités de contribution. LSaddons 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 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 &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 &LSaddon;. 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; // 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; } /** * 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; } if (something else) { LSerror :: addErrorCode( 'MYADDON_02', array( // Error LSformat arguments 'about' => 'second step', 'msg' => 'something went wrong' ) ); return false; } 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); } ]]>