From f0bb4f585cef72da3815e65b438140039acff96a Mon Sep 17 00:00:00 2001 From: Benjamin Renard Date: Tue, 7 Jul 2020 12:43:55 +0200 Subject: [PATCH] LScli modify command: add autocompletion Autocompletion feature also add on LSform and LSformElement for attribute values --- src/includes/class/class.LSform.php | 41 ++++++++- src/includes/class/class.LSformElement.php | 65 ++++++++++++++ .../class/class.LSformElement_boolean.php | 22 +++++ .../class/class.LSformElement_password.php | 19 ++++ .../class/class.LSformElement_select.php | 28 ++++++ .../class.LSformElement_select_object.php | 36 ++++++++ src/includes/class/class.LSldapObject.php | 86 ++++++++++++++++++- 7 files changed, 294 insertions(+), 3 deletions(-) diff --git a/src/includes/class/class.LSform.php b/src/includes/class/class.LSform.php index 88d0520a..393483b5 100644 --- a/src/includes/class/class.LSform.php +++ b/src/includes/class/class.LSform.php @@ -20,6 +20,7 @@ ******************************************************************************/ +LSsession :: loadLSclass('LSlog_staticLoggerClass'); /** * Formulaire pour LdapSaisie @@ -29,7 +30,7 @@ * @author Benjamin Renard */ -class LSform { +class LSform extends LSlog_staticLoggerClass { var $ldapObject; var $idForm; var $can_validate = true; @@ -779,6 +780,44 @@ class LSform { } } + /** + * CLI autocompleter for form attributes values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The command word to autocomplete + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * + * @retval void + */ + public function autocomplete_attrs_values(&$opts, $comp_word, $multiple_value_delimiter='|') { + if ($comp_word && strpos($comp_word, '=') !== false) { + // Check if $comp_word is quoted + $quote_char = LScli :: unquote_word($comp_word); + + // Attribute name already entered: check it and autocomplete using LSformElement -> autocomplete_opts() + $comp_word_parts = explode('=', $comp_word); + $attr_name = trim($comp_word_parts[0]); + $attr_value = (count($comp_word_parts) > 1?implode('=', array_slice($comp_word_parts, 1)):''); + if (!$this -> hasElement($attr_name)) { + self :: log_error("Attribute '$attr_name' does not exist or not present in modify form."); + return; + } + $this -> elements[$attr_name] -> autocomplete_attr_values($opts, $comp_word, $attr_value, $multiple_value_delimiter, $quote_char); + } + else { + // Attribute name not already entered: add attribute name options + // Check if $comp_word is quoted and retreived quote char + if ($comp_word) { + $quote_char = LScli :: unquote_word($comp_word); + } + else + $quote_char = ''; + foreach (array_keys($this -> elements) as $attr_name) { + $opts[] = LScli :: quote_word("$attr_name=", $quote_char); + } + } + } + } /** diff --git a/src/includes/class/class.LSformElement.php b/src/includes/class/class.LSformElement.php index 87073fcf..b74b40f3 100644 --- a/src/includes/class/class.LSformElement.php +++ b/src/includes/class/class.LSformElement.php @@ -334,4 +334,69 @@ class LSformElement extends LSlog_staticLoggerClass { return LSconfig :: get($param, $default, $cast, $this -> params); } + /** + * CLI autocompleter for form element attribute values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The (unquoted) command word to autocomplete + * @param[in] $attr_value string The current attribute value in command word to autocomplete + * (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character detected (optional, default: empty string) + * + * @retval void + */ + public function autocomplete_attr_values(&$opts, $comp_word, $attr_value="", $multiple_value_delimiter="|", $quote_char='') { + return; + } + + /** + * CLI autocompleter helper to split form element attribute values + * + * @param[in] $attr_value string The current attribute value in command word to autocomplete + * (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] &$attr_values Reference of array Reference of array that will contain splited attribute + * values without last-one + * @param[in] &$last_attr_value Reference of string Reference of array that will contain the last splited attribute + * value + * + * @retval boolean True on success, False otherwise + */ + protected function split_autocomplete_attr_values($attr_value="", $multiple_value_delimiter="|", &$attr_values, &$last_attr_value) { + $attr_values = explode($multiple_value_delimiter, $attr_value); + if (count($attr_values) > 1 && !$this -> getParam('multiple', false, 'bool')) { + self :: log_error("The attribute ".$this -> name." is not multivalued."); + return; + } + self :: log_debug("split_autocomplete_attr_values('$attr_value', '$multiple_value_delimiter'): values = '".implode("', '", $attr_values)."'"); + $last_attr_value = array_pop($attr_values); + self :: log_debug("split_autocomplete_attr_values('$attr_value', '$multiple_value_delimiter'): last value = '$last_attr_value'"); + return true; + } + + /** + * CLI autocompleter helper to format and add form element attribute value option + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] &$attr_values Reference of array Reference of array of splited attribute values without last-one + * @param[in] $value string The attribute value to add as option + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character (optional, default: empty string) + * + * @retval boolean True on success, False otherwise + */ + protected function add_autocomplete_attr_value_opts(&$opts, &$attr_values, $value, $multiple_value_delimiter='|', $quote_char='') { + if (in_array($value, $attr_values)) { + self :: log_debug("LSformElement :: autocomplete_opts(): '$value' already one of selected value, ignore it"); + return; + } + $opt = $this -> name . "=" .implode($multiple_value_delimiter, array_merge($attr_values, array($value))); + self :: log_debug("LSformElement :: add_autocomplete_attr_value_opts(): option=$opt"); + if ($quote_char) + $opt = LScli :: quote_word($opt, $quote_char); + if (!in_array($opt, $opts)) + $opts[] = $opt; + } + } diff --git a/src/includes/class/class.LSformElement_boolean.php b/src/includes/class/class.LSformElement_boolean.php index 41b22e88..49106c30 100644 --- a/src/includes/class/class.LSformElement_boolean.php +++ b/src/includes/class/class.LSformElement_boolean.php @@ -64,4 +64,26 @@ class LSformElement_boolean extends LSformElement { return $return; } + /** + * CLI autocompleter for form element attribute values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The (unquoted) command word to autocomplete + * @param[in] $attr_value string The current attribute value in command word to autocomplete (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character detected (optional, default: empty string) + * + * @retval void + */ + public function autocomplete_attr_values(&$opts, $comp_word, $attr_value="", $multiple_value_delimiter="|", $quote_char='') { + // Split attribute values and retreived splited value in $attr_values and $last_attr_value + if (!$this -> split_autocomplete_attr_values($attr_value, $multiple_value_delimiter, $attr_values, $last_attr_value)) + return; + + // Add yes/no values + foreach(array('yes', 'no') as $value) { + $this -> add_autocomplete_attr_value_opts($opts, $attr_values, $value, $multiple_value_delimiter, $quote_char); + } + } + } diff --git a/src/includes/class/class.LSformElement_password.php b/src/includes/class/class.LSformElement_password.php index 0bfeec1b..4f2efacb 100644 --- a/src/includes/class/class.LSformElement_password.php +++ b/src/includes/class/class.LSformElement_password.php @@ -375,6 +375,25 @@ class LSformElement_password extends LSformElement { return $this -> getParam('html_options.isLoginPassword', false, 'bool'); } + /** + * CLI autocompleter for form element attribute values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The (unquoted) command word to autocomplete + * @param[in] $attr_value string The current attribute value in command word to autocomplete (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character detected (optional, default: empty string) + * + * @retval void + */ + public function autocomplete_attr_values(&$opts, $comp_word, $attr_value="", $multiple_value_delimiter="|", $quote_char='') { + // Split attribute values and retreived splited value in $attr_values and $last_attr_value + if (!$this -> split_autocomplete_attr_values($attr_value, $multiple_value_delimiter, $attr_values, $last_attr_value)) + return; + $pwd = $this->generatePassword($this -> params); + $this -> add_autocomplete_attr_value_opts($opts, $attr_values, $pwd, $multiple_value_delimiter, $quote_char); + } + } /* diff --git a/src/includes/class/class.LSformElement_select.php b/src/includes/class/class.LSformElement_select.php index 178e90ec..e252ffdb 100644 --- a/src/includes/class/class.LSformElement_select.php +++ b/src/includes/class/class.LSformElement_select.php @@ -113,6 +113,34 @@ class LSformElement_select extends LSformElement { return $ret; } + /** + * CLI autocompleter for form element attribute values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The (unquoted) command word to autocomplete + * @param[in] $attr_value string The current attribute value in command word to autocomplete (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character detected (optional, default: empty string) + * + * @retval void + */ + public function autocomplete_attr_values(&$opts, $comp_word, $attr_value="", $multiple_value_delimiter="|", $quote_char='') { + // Split attribute values and retreived splited value in $attr_values and $last_attr_value + if (!$this -> split_autocomplete_attr_values($attr_value, $multiple_value_delimiter, $attr_values, $last_attr_value)) + return; + + foreach ($this -> params['text_possible_values'] as $value => $label) { + if (is_array($label)) { + foreach ($label['possible_values'] as $v => $l) { + $this -> add_autocomplete_attr_value_opts($opts, $attr_values, $v, $multiple_value_delimiter, $quote_char); + } + } + else { + $this -> add_autocomplete_attr_value_opts($opts, $attr_values, $value, $multiple_value_delimiter, $quote_char); + } + } + } + } /** diff --git a/src/includes/class/class.LSformElement_select_object.php b/src/includes/class/class.LSformElement_select_object.php index 646f1232..eee521e6 100644 --- a/src/includes/class/class.LSformElement_select_object.php +++ b/src/includes/class/class.LSformElement_select_object.php @@ -286,4 +286,40 @@ class LSformElement_select_object extends LSformElement { } } + /** + * CLI autocompleter for form element attribute values + * + * @param[in] &$opts array Reference of array of avalaible autocomplete options + * @param[in] $comp_word string The (unquoted) command word to autocomplete + * @param[in] $attr_value string The current attribute value in command word to autocomplete (optional, default: empty string) + * @param[in] $multiple_value_delimiter string The multiple value delimiter (optional, default: "|") + * @param[in] $quote_char string The quote character detected (optional, default: empty string) + * + * @retval void + */ + public function autocomplete_attr_values(&$opts, $comp_word, $attr_value="", $multiple_value_delimiter="|", $quote_char='') { + self :: log_debug("LSformElement :: autocomplete_opts([...], '$comp_word', '$attr_value', '$multiple_value_delimiter', '$quote_char')"); + + // Split attribute values and retreived splited value in $attr_values and $last_attr_value + if (!$this -> split_autocomplete_attr_values($attr_value, $multiple_value_delimiter, $attr_values, $last_attr_value)) + return; + + // Retreive selectable objects configuration + $objs = null; + $confs = $this -> attr_html -> getSelectableObjectsConfig($objs); + if (!is_array($confs)) + return; + + // Iter on selectable object types to retreived available autocomplete options + foreach($confs as $object_type => $conf) { + $dns = LScli :: autocomplete_LSobject_dn($object_type, $last_attr_value); + self :: log_debug("LScli :: autocomplete_LSobject_dn($object_type, $last_attr_value) : ".varDump($dns)); + if (is_array($dns)) { + foreach ($dns as $dn) { + $this -> add_autocomplete_attr_value_opts($opts, $attr_values, $dn, $multiple_value_delimiter, $quote_char); + } + } + } + } + } diff --git a/src/includes/class/class.LSldapObject.php b/src/includes/class/class.LSldapObject.php index b73b04fb..db01d801 100644 --- a/src/includes/class/class.LSldapObject.php +++ b/src/includes/class/class.LSldapObject.php @@ -2376,7 +2376,7 @@ class LSldapObject extends LSlog_staticLoggerClass { $changes = array(); for ($i=0; $i < count($command_args); $i++) { switch ($command_args[$i]) { - case '-d': + case '-D': case '--delimiter': $delimiter = $command_args[++$i]; if ($delimiter == '=') @@ -2453,6 +2453,86 @@ class LSldapObject extends LSlog_staticLoggerClass { return True; } + /** + * Args autocompleter for CLI modify 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_modify_args_autocompleter($command_args, $comp_word_num, $comp_word, $opts) { + $opts = array_merge($opts, array ('-j', '--just-try', '-D', '--delimiter', '-N', '--no-confirm')); + + // 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; + } + } + else { + // All args accept option, increase $i + $i++; + } + } + LSlog :: debug("obj type :'$objType' (#$objType_arg_num) / dn :'$dn' (#$dn_arg_num)"); + + // Handle completion of args value + LSlog :: debug("Last complete word = '".$command_args[$comp_word_num-1]."'"); + switch ($command_args[$comp_word_num-1]) { + case '-D': + case '--delimiter': + return array('|', ';'); + } + + // 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 already 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)); + + // Otherwise, autocomplete on attribute=value + elseif ($objType && class_exists($objType) && $dn) { + LScli :: need_ldap_con(); + $obj = new $objType(); + if (!$obj->loadData($dn)) { + self :: log_error("Fail to load object $dn data from LDAP"); + } + else { + $form = $obj -> getForm('modify'); + $form -> autocomplete_attrs_values($opts, $comp_word); + } + } + + return LScli :: autocomplete_opts($opts, $comp_word); + } + /** * CLI relation command * @@ -2894,7 +2974,9 @@ LScli :: add_command( ' -D|--delimiter Delimiter for multiple values attributes', ' (default: "|")', ' -N|--no-confirm Do not ask for confirmation', - ) + ), + true, + array('LSldapObject', 'cli_modify_args_autocompleter') ); LScli :: add_command(