*/ class LSform extends LSlog_staticLoggerClass { /** * Reference of the related LSldapObject * @var LSldapObject */ var $ldapObject; /** * Identifier of the form (create/update/...) * @var string */ var $idForm; /** * Configuration (LSobjects..LSform) * @see LSform::__construct() * @var array */ var $config; /** * Validation ability telltale * @see LSform::validate() * @see LSform::setElementError() * @var boolean */ var $can_validate = true; /** * Form elements with attribute name as key and an implemented LSformElement as value * @see LSform::addElement() * @see LSattr_html::addToForm() * @var array */ var $elements = array(); /** * Registred elements's rules * @var array */ var $_rules = array(); /** * Post data of the form * @see LSform::getPostData() * @var array */ var $_postData = array(); /** * Registered elements's errors * @see LSform::setElementError() * @see LSform::definedError() * @see LSform::getErrors() * @var array */ var $_elementsErrors = array(); /** * Validation telltale * @see LSform::validate() * @var boolean */ var $_isValidate = false; /** * List of attributes explicitly declared as not updated * @see LSformElement_password::getPostData() * @var array */ var $_notUpdate = array(); /** * MAX_FILE_SIZE value of the form * @see LSform::setMaxFileSize() * @var int|null */ var $maxFileSize = NULL; /** * Name of the applied data entry form * @see LSform::applyDataEntryForm() * @var string|null */ var $dataEntryForm = NULL; /** * Configuration of the applied data entry form * (LSobjects..LSform.dataEntryForm.) * @see LSform::applyDataEntryForm() * @var array|null */ var $dataEntryFormConfig = NULL; /** * Registred warnings * @see LSform::addWarning() * @var array */ var $warnings = array(); /** * Telltale of the API mode * @see LSform::__construct() * @var bool */ var $api_mode = false; /** * Submited telltale * @see LSform::setSubmited() * @see LSform::isSubmit() * @var bool */ private $submited = false; /** * The submit button value * Note: used to detect the submited state of the form from the current request data * @see LSform::__construct() * @see LSform::isSubmit() * @var string|null */ private $submit = null; /** * Constructeur * * Cette methode construit l'objet et définis la configuration. * * @author Benjamin Renard * * @param LSldapObject &$ldapObject The related LSldapObject * @param string $idForm The form identifier * @param string|null $submit The submit button value * @param bool $api_mode Set to True to enable API mode * * @return void */ public function __construct(&$ldapObject, $idForm, $submit=NULL, $api_mode=false){ $this -> idForm = $idForm; $this -> submit = ($submit?$submit:_("Validate")); $this -> api_mode = $api_mode; $this -> ldapObject =& $ldapObject; $this -> config = $ldapObject -> getConfig('LSform', array(), 'array'); LSsession :: loadLSclass('LSformElement'); } /** * Return a configuration parameter (or default value) * * @param string $param The configuration parameter * @param mixed $default The default value (default : null) * @param string $cast Cast resulting value in specific type (default : disabled) * * @return mixed The configuration parameter value or default value if not set **/ public function getConfig($param, $default=null, $cast=null) { return LSconfig :: get($param, $default, $cast, $this -> config); } /** * Allow conversion of LSform to string * * @return string The string representation of the LSform */ public function __toString() { return " idForm." on ".$this -> ldapObject -> toString(false).">"; } /** * Display the form * * @param string|null $LSform_action The form action attribute value (optional, default: $_SERVER['PHP_SELF']) * @author Benjamin Renard * * @return void */ public function display($LSform_action=null){ // Load view dependencies self :: loadDependenciesDisplayView(); // Load form dependencies LStemplate :: addJSscript('LSformElement_field.js'); LStemplate :: addJSscript('LSformElement.js'); LStemplate :: addHelpInfo( 'LSform', array( 'addFieldBtn' => _('Add a field to add another values.'), 'removeFieldBtn' => _('Delete this field.') ) ); LStemplate :: assign('LSform_action', ($LSform_action?$LSform_action:$_SERVER['PHP_SELF'])); $LSform_header = "\t\n \t\n \t\n \t\n"; $LSform_object = array( 'type' => $this -> ldapObject -> getType(), 'dn' => $this -> ldapObject -> getValue('dn') ); LStemplate :: assign('LSform_object',$LSform_object); $layout_config = $this -> getConfig("layout"); if (!isset($this -> dataEntryFormConfig['disabledLayout']) || $this -> dataEntryFormConfig['disabledLayout']==false) { if (is_array($layout_config)) { LStemplate :: assign('LSform_layout',$layout_config); } } $fields = array(); if (!isset($this -> dataEntryFormConfig['displayedElements']) || !is_array($this -> dataEntryFormConfig['displayedElements'])) { foreach($this -> elements as $element) { $field = array(); $field = $element -> getDisplay(); if (isset($this -> _elementsErrors[$element -> name])) { $field['errors']= $this -> _elementsErrors[$element -> name]; } $fields[$element -> name] = $field; } } else { foreach($this -> dataEntryFormConfig['displayedElements'] as $elementName) { if (!isset($this -> elements[$elementName])) { LSerror :: addErrorCode('LSform_09',$elementName); continue; } $element = $this -> elements[$elementName]; if ((isset($this -> dataEntryFormConfig['requiredAllAttributes']) && $this -> dataEntryFormConfig['requiredAllAttributes']) || isset($this -> dataEntryFormConfig['requiredAttributes']) && is_array($this -> dataEntryFormConfig['requiredAttributes']) && in_array($elementName,$this -> dataEntryFormConfig['requiredAttributes'])) { $element -> setRequired(); } $field = array(); $field = $element -> getDisplay(); if (isset($this -> _elementsErrors[$element -> name])) { $field['errors']= $this -> _elementsErrors[$element -> name]; } $fields[$element -> name] = $field; } // Add warning for other elements errors foreach(array_keys($this -> elements) as $name) { if (isset($this -> _elementsErrors[$name]) && !isset($fields[$name])) { foreach ($this -> _elementsErrors[$name] as $error) { $this -> addWarning("$name : $error"); } } } $LSform_header .= "\t\n"; } if ($this -> maxFileSize) { $LSform_header.="\t\n"; } LStemplate :: assign('LSform_header',$LSform_header); LStemplate :: assign('LSform_fields',$fields); $JSconfig = array ( 'ajaxSubmit' => intval($this -> getConfig('ajaxSubmit', true, 'boolean')), 'onFailureMessage' => _('Unexpected error occurred submiting this form. Please retry again or contact support.'), ); if (!empty($this -> warnings)) { $JSconfig['warnings']=$this -> warnings; } LStemplate :: addJSconfigParam('LSform_'.$this -> idForm,$JSconfig); LStemplate :: assign('LSform_submittxt',$this -> submit); } /** * Méthode chargeant les dépendances d'affichage d'une LSview * * @return void */ public static function loadDependenciesDisplayView($ldapObject=false, $search_view=false) { LStemplate :: addCssFile('LSform.css'); LStemplate :: addJSscript('LSform.js'); $customActionLabels = array (); if (is_a($ldapObject,'LSldapObject')) { $objectname=($search_view?$ldapObject -> getLabel():$ldapObject -> getDisplayName()); $customActionsConfig = LSconfig :: get('LSobjects.'.$ldapObject->type_name.($search_view?'.LSsearch':'').'.customActions'); if (is_array($customActionsConfig)) { foreach($customActionsConfig as $name => $config) { if (isset($config['question_format'])) { $customActionLabels['custom_action_'.$name.'_confirm_text'] = getFData(__($config['question_format']), $objectname); } elseif ($search_view) { $customActionLabels['custom_action_'.$name.'_confirm_text'] = getFData( _('Do you really want to execute custom action %{title} on this search ?'), $name ); } else { $customActionLabels['custom_action_'.$name.'_confirm_text'] = getFData( _('Do you really want to execute custom action %{customAction} on %{objectname} ?'), array( 'objectname' => $objectname, 'customAction' => $name ) ); } } } } LStemplate :: addJSconfigParam('LSview_labels', array_merge(array( 'delete_confirm_text' => _('Do you really want to delete "%{name}"?'), 'delete_confirm_title' => _("Caution"), 'delete_confirm_validate' => _("Delete") ),$customActionLabels)); if (LSsession :: loadLSclass('LSconfirmBox')) { LSconfirmBox :: loadDependenciesDisplay(); } LStemplate :: addJSscript('LSview.js'); } /** * Affiche la vue * * @author Benjamin Renard * * @return void */ public function displayView(){ self :: loadDependenciesDisplayView($this -> ldapObject); $LSform_object = array( 'type' => $this -> ldapObject -> getType(), 'dn' => $this -> ldapObject -> getDn() ); LStemplate :: assign('LSform_object',$LSform_object); $fields = array(); foreach($this -> elements as $element) { $field = $element -> getDisplay(); $fields[$element -> name] = $field; } LStemplate :: assign('LSform_fields',$fields); $layout_config = $this -> getConfig("layout"); if (is_array($layout_config)) { LStemplate :: assign('LSform_layout',$layout_config); } } /** * Défini l'erreur sur un champ * * @author Benjamin Renard * * @param LSformElement $element Le nom du champ * @param string|null $msg Le format du message d'erreur à afficher (pouvant comporter * des valeurs %{[n'importe quoi]} qui seront remplacé par le label * du champs concerné. * @param array|null $extra_values Extra values to compute the LSformat of the message * (optional, by default, only the field label is provided to getFData) * * @return void */ public function setElementError($element, $msg=null, $extra_values=null) { $this -> _elementsErrors[$element->name][] = getFData( $msg?$msg:_("%{label} attribute data is not valid."), is_array($extra_values)? array_merge(array('label' => $element->getLabel()), $extra_values): $element->getLabel() ); $this -> can_validate=false; } /** * Savoir si des erreurs son définie pour un élement du formulaire * * @author Benjamin Renard * * @param string|null $element Le nom de l'élement * * @return boolean */ public function definedError($element=NULL) { if ($element) { return isset($this -> _elementsErrors[$element]); } else { return !empty($this -> _elementsErrors); } } /** * Retourne le tableau des erreurs * * @return array array(element => array(errors)) */ public function getErrors() { return $this -> _elementsErrors; } /** * Check form is submited and its data are validat * * @param boolean $onlyIfPresent If true and data of this element is not present in POST data, * just ignore it. * @author Benjamin Renard * * @return boolean true if form is submited and its data are valid, false otherwise */ public function validate($onlyIfPresent=false){ if(!$this -> can_validate) return false; if ($this -> isSubmit()) { if (!$this -> getPostData($onlyIfPresent)) { LSerror :: addErrorCode('LSform_01'); return false; } // Check getPostData do not trigger fields errors if(!$this -> can_validate) return false; $this -> setValuesFromPostData(); //Validation des données ici !!! /// if (!$this -> checkData()) { return false; } LSdebug("Data are checked up"); $this -> _isValidate = true; return true; } return false; } /** * Vérifier les données du formulaire à partir des régles définis sur les champs * * @author Benjamin Renard * * @return boolean true si toutes la saisie est OK, false sinon */ public function checkData() { $retval=true; foreach ($this -> _postData as $element => $values) { if ($this -> definedError($element)) { $retval=false; } if(!is_array($values)) { $values=array($values); } if ($this -> elements[$element] -> isRequired()) { if (!$this -> checkRequired($values)) { $this -> setElementError($this -> elements[$element],_("Mandatory field")); $retval=false; } } // If no rule configured for this attribute, just ignore this check if (!isset($this -> _rules[$element]) || !is_array($this -> _rules[$element])) continue; // Load LSformRule class LSsession :: loadLSclass('LSformRule', null, true); // Iter on rules and check element values with each of them foreach($this -> _rules[$element] as $rule) { $errors = LSformRule :: validate_values( $rule['name'], $values, $rule['options'], $this -> elements[$element] ); if (is_array($errors)) { $retval = false; foreach ($errors as $error) $this -> setElementError($this -> elements[$element], $error); } } } return $retval; } /** * Check if at least one non-empty value is present in the specifed array * * @author Benjamin Renard * * @param array $data Array of values * * @return boolean True if at least one non-empty value is present, false otherwise */ public function checkRequired($data) { foreach($data as $val) { if (!is_empty($val)) return true; } return false; } /** * Verifie si la saisie du formulaire est présente en POST * * @author Benjamin Renard * * @return boolean true si la saisie du formulaire est présente en POST, false sinon */ public function isSubmit() { if ($this -> submited) return true; if( (isset($_POST['validate']) && ($_POST['validate']=='LSform')) && (isset($_POST['idForm']) && ($_POST['idForm'] == $this -> idForm)) ) return true; return false; } /** * Set form as submited * * @return void */ public function setSubmited() { $this -> submited = true; } /** * Défini arbitrairement des données en POST * * @author Benjamin Renard * * @param array $data Tableau des données du formulaire : array('attr' => array(values)) * @param bool $consideredAsSubmit Définie si on force le formualaire comme envoyer * * @return boolean true si les données ont été définies, false sinon */ public function setPostData($data,$consideredAsSubmit=false) { if (is_array($data)) { foreach($data as $key => $values) { if (!is_array($values)) { $values = array($values); } $_POST[$key] = $values; } if ($consideredAsSubmit) { $_POST['validate']='LSform'; $_POST['idForm']=$this -> idForm; } return true; } return false; } /** * Retrieve POST data of the form * * @param boolean $onlyIfPresent If true and data of this element is not present in POST data, * just ignore it. * @author Benjamin Renard * * @return boolean true if POST data are retrieved, false otherwise */ public function getPostData($onlyIfPresent=false) { if (is_null($this -> dataEntryForm)) { foreach($this -> elements as $element_name => $element) { if( !($element -> getPostData($this -> _postData, $onlyIfPresent)) ) { LSerror :: addErrorCode('LSform_02',$element_name); return false; } } } else { $defaultValues = LSconfig::get( 'defaultValues', array(), 'array', $this -> dataEntryFormConfig ); $elementsList = array_merge( $this -> dataEntryFormConfig['displayedElements'], array_keys($this -> dataEntryFormConfig['defaultValues']) ); foreach($elementsList as $elementName) { if (!isset($this -> elements[$elementName])) { LSerror :: addErrorCode('LSform_09',$elementName); continue; } $element = $this -> elements[$elementName]; // Set required from dataEntryForm config if ( LSconfig :: get('requiredAllAttributes', false, 'bool', $this -> dataEntryFormConfig) || in_array( $elementName, LSconfig :: get('requiredAttributes', array(), 'array', $this -> dataEntryFormConfig) ) ){ $element -> setRequired(); } // Set default value if not present in POST data if (!isset($_POST[$elementName]) && isset($defaultValues[$elementName])) { $_POST[$elementName] = ensureIsArray($defaultValues[$elementName]); } // Retrieve POST data of the element if( !($element -> getPostData($this -> _postData, $onlyIfPresent)) ) { LSerror :: addErrorCode('LSform_02',$elementName); return false; } } } return true; } /** * Ajoute un élément au formulaire * * Ajoute un élément au formulaire et définis les informations le concernant. * * @param string $type Le type de l'élément * @param string $name Le nom de l'élément * @param string $label Le label de l'élément * @param array $params Paramètres supplémentaires * @param LSattr_html &$attr_html The related LSattr_html * * @return LSformElement|false The LSformElement on success, false otherwise */ public function addElement($type,$name,$label,$params,&$attr_html) { $elementType='LSformElement_'.$type; LSsession :: loadLSclass($elementType); if (!class_exists($elementType)) { LSerror :: addErrorCode('LSform_05',array('type' => $type)); return false; } $element=$this -> elements[$name] = new $elementType($this,$name,$label,$params,$attr_html); if ($element) { return $element; } else { unset ($this -> elements[$name]); LSerror :: addErrorCode('LSform_06',array('element' => $name)); return false; } } /** * Check if form has a specified element (by attr name) * * @param string $name The element/attribute name * * @return boolean **/ public function hasElement($name) { return isset($this -> elements[$name]); } /** * Check if a specified element (by attr name) is freezed * * @param string $name The element/attribute name * * @return boolean **/ public function isFreeze($name) { return isset($this -> elements[$name]) && $this -> elements[$name] -> isFreeze(); } /** * Ajoute une règle sur un élément du formulaire * * @author Benjamin Renard * * @param string $element Le nom de l'élément conserné * @param string $rule Le nom de la règle à ajouter * @param array $options Options (facultative) * * @return boolean */ public function addRule($element, $rule, $options=null) { if ( isset($this ->elements[$element]) ) { if ($this -> isRuleRegistered($rule)) { $this -> _rules[$element][] = array( 'name' => $rule, 'options' => (is_array($options)?$options:array()), ); return true; } else { LSerror :: addErrorCode('LSattribute_03', array('attr' => $element, 'rule' => $rule)); return false; } } else { LSerror :: addErrorCode('LSform_04', array('element' => $element)); return false; } } /** * Définis comme requis un élément * * @author Benjamin Renard * * @param string $element Le nom de l'élément conserné * * @return boolean */ public function setRequired($element) { if (isset( $this -> elements[$element] ) ) { $this -> elements[$element] -> setRequired(); return true; } return false; } /** * Détermine la valider de la règle * * Devra déterminer si la règle passez en paramètre est correcte * * @author Benjamin Renard * * @param string $rule The rule name * @return bool */ public function isRuleRegistered($rule) { LSsession :: loadLSclass('LSformRule'); LSsession :: loadLSclass('LSformRule_'.$rule); return class_exists('LSformRule_'.$rule); } /** * Retourne les valeurs validés du formulaire * * @return mixed Les valeurs validés du formulaire, ou false si elles ne le sont pas */ public function exportValues() { if ($this -> _isValidate) { $retval=array(); foreach($this -> _postData as $element => $values) { $retval[$element] = $this -> elements[$element] -> exportValues(); } return $retval; } else { return false; } } /** * Retourn un élement du formulaire * * @param string $element Nom de l'élement voulu * * @return LSformElement L'élement du formulaire voulu */ public function getElement($element) { return $this -> elements[$element]; } /** * Return the values of an element * * If form is posted, retrieve values from postData, otherwise * retrieve value from the element. * * @param string $element The element name * * @return mixed The element values **/ public function getValue($element) { if ($this -> isSubmit() && $this -> _postData) { return $this -> _postData[$element]; } return $this -> elements[$element] -> values; } /** * Défini les valeurs des élements à partir des valeurs postées * * @return boolean True si les valeurs ont été définies, false sinon. */ public function setValuesFromPostData() { if (empty($this -> _postData)) { return false; } foreach($this -> _postData as $element => $values) { $this -> elements[$element] -> setValueFromPostData($values); } return true; } /** * Return the HTML code of an empty form field * * @param string $element The form element name * @param int|null $value_idx The value index (optional, default: null == 0) * * @return string|null The HTML code of the specified field if exist, null otherwise */ public function getEmptyField($element, $value_idx=null) { $element = $this -> getElement($element); if ($element) { return $element -> getEmptyField($value_idx); } else { return null; } } /** * Défini la taille maximal pour les fichiers envoyés par le formualaire * * @param int $size La taille maximal en octets * * @return void **/ public function setMaxFileSize($size) { $this -> maxFileSize = $size; } /** * Applique un masque de saisie au formulaire * * @param string $dataEntryForm Le nom du masque de saisie * * @return boolean True si le masque de saisie a été appliqué, False sinon **/ public function applyDataEntryForm($dataEntryForm) { $dataEntryForm=(string)$dataEntryForm; $objType = $this -> ldapObject -> getType(); $config = $this -> getConfig("dataEntryForm.$dataEntryForm"); if (is_array($config)) { if (!is_array($config['displayedElements'])) { LSerror :: addErrorCode('LSform_08',$dataEntryForm); } $this -> dataEntryForm = $dataEntryForm; $this -> dataEntryFormConfig = $config; // Set default value of displayed elements if(is_array($config['defaultValues'])) { foreach($config['displayedElements'] as $el) { if (isset($config['defaultValues'][$el])) { if (isset($this -> elements[$el])) { $this -> elements[$el] -> setValueFromPostData($config['defaultValues'][$el]); } } } } return true; } LSerror :: addErrorCode('LSform_07',$dataEntryForm); return false; } /** * Liste les dataEntryForm disponible pour un type d'LSldapObject * * @param string $type Le type d'LSldapObject * * @return array Tableau contenant la liste de dataEntryForm disponible pour ce type d'LSldapObject (nom => label) **/ public static function listAvailableDataEntryForm($type) { $retval=array(); if (LSsession ::loadLSobject($type)) { // Static method: couldn't use $this -> getConfig() $config=LSconfig :: get("LSobjects.".$type.".LSform.dataEntryForm"); if (is_array($config)) { foreach($config as $name => $conf) { if (isset($conf['label'])) { $retval[$name]=__($conf['label']); } else { $retval[$name]=__($name); } } } } return $retval; } /** * Ajoute un avertissement au sujet du formulaire * * @param string $txt Le texte de l'avertissement * * @return void **/ public function addWarning($txt) { $this -> warnings[]=$txt; } /** * Méthode Ajax permetant de retourner le code HTML d'un élément du formulaire vide * * @param array &$data Variable de retour * * @return void **/ public static function ajax_onAddFieldBtnClick(&$data) { if ((isset($_REQUEST['attribute'])) && (isset($_REQUEST['objecttype'])) && (isset($_REQUEST['objectdn'])) && (isset($_REQUEST['idform'])) && (isset($_REQUEST['fieldId'])) ) { if (LSsession ::loadLSobject($_REQUEST['objecttype'])) { $object = new $_REQUEST['objecttype'](); $object -> loadData($_REQUEST['objectdn']); $form = $object -> getForm($_REQUEST['idform']); $value_idx = (isset($_REQUEST['value_idx'])?$_REQUEST['value_idx']:0); $emptyField = $form -> getEmptyField($_REQUEST['attribute'], $value_idx); if ( $emptyField ) { $data = array( 'html' => $emptyField, 'value_idx' => $value_idx, 'fieldId' => $_REQUEST['fieldId'], 'fieldtype' => get_class($form -> getElement($_REQUEST['attribute'])) ); } } } } /** * CLI autocompleter for form attributes values * * @param array &$opts Array of avalaible autocomplete options * @param string $comp_word The command word to autocomplete * @param string $multiple_value_delimiter The multiple value delimiter * (optional, default: "|") * * @return 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 retrieved 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); } } } } /** * Error Codes */ LSerror :: defineError('LSform_01', ___("LSform : Error during the recovery of the values of the form.") ); LSerror :: defineError('LSform_02', ___("LSform : Error durring the recovery of the value of the field '%{element}'.") ); // No longer used /*LSerror :: defineError(203, ___("LSform : Data of the field %{element} are not validate.") );*/ LSerror :: defineError('LSform_04', ___("LSform : The field %{element} doesn't exist.") ); LSerror :: defineError('LSform_05', ___("LSfom : Field type unknow (%{type}).") ); LSerror :: defineError('LSform_06', ___("LSform : Error during the creation of the element '%{element}'.") ); LSerror :: defineError('LSform_07', ___("LSform : The data entry form %{name} doesn't exist.") ); LSerror :: defineError('LSform_08', ___("LSform : The data entry form %{name} is not correctly configured.") ); LSerror :: defineError('LSform_09', ___("LSform : The element %{name}, listed as displayed in data entry form configuration, doesn't exist.") );