*/ class LSformElement_jsonCompositeAttribute extends LSformElement { var $template = 'LSformElement_jsonCompositeAttribute.tpl'; var $fieldTemplate = 'LSformElement_jsonCompositeAttribute_field.tpl'; public function __construct(&$form, $name, $label, $params, &$attr_html){ parent :: __construct($form, $name, $label, $params,$attr_html); $this -> components = $this -> getParam('html_options.components', array()); } /* * Value components : * * Format : * array ( * '[component1_key]' => array ( * 'label' => '[component label]', * 'type' => '[component type]', * 'required' => '[booléen]', * 'check_data' => array([config LSform_rule]) * ), * '[component2_key]' => array ( * 'label' => 'label2', * 'type' => 'select_list', * 'options' => array([config as LSattr_html_select_list html_options]), * ), * [...] * ) * Types : * - 'select_list' => Component feed by a list of valeur configured like an * atribute LSattr_html :: select_list. * - 'text' => manual entry * */ var $components = array(); /** * Parse values * * @retval array Parsed values */ private function parseValues() { self :: log_trace('values: '.varDump($this -> values)); $parseValues=array(); foreach($this -> values as $val) { $decodedValue = json_decode($val, true); self :: log_trace('decoded value: '.varDump($decodedValue)); if (is_array($decodedValue)) { $parseValue = array('value' => $val); foreach($decodedValue as $c => $cvalue) { $parseValue[$c] = $this -> translateComponentValue($c,$cvalue); } $parseValues[] = $parseValue; } } self :: log_trace('parsed values: '.varDump($parseValues)); return $parseValues; } /** * Retourne les infos d'affichage de l'élément * * Cette méthode retourne les informations d'affichage de l'élement * * @retval array */ public function getDisplay(){ $return = $this -> getLabelInfos(); $return['html'] = $this -> fetchTemplate(NULL, array( 'parseValues' => $this -> parseValues(), 'fullWidth' => $this -> getParam('html_options.fullWidth', false, 'bool'), ) ); LStemplate :: addCssFile('LSformElement_jsonCompositeAttribute.css'); if (!$this -> isFreeze()) { LStemplate :: addJSconfigParam( $this -> name, array ( 'components' => $this -> components, ) ); LStemplate :: addJSscript('LSformElement_jsonCompositeAttribute_field_value_component_text_value.js'); LStemplate :: addJSscript('LSformElement_jsonCompositeAttribute_field_value_component.js'); LStemplate :: addJSscript('LSformElement_jsonCompositeAttribute_field_value.js'); LStemplate :: addJSscript('LSformElement_jsonCompositeAttribute_field.js'); LStemplate :: addJSscript('LSformElement_jsonCompositeAttribute.js'); } return $return; } /** * Retournne un template Smarty compilé dans le contexte d'un LSformElement * * @param[in] string $template Le template à retourner * @param[in] array $variables Variables Smarty à assigner avant l'affichage * * @retval string Le HTML compilé du template */ public function fetchTemplate($template=NULL,$variables=array()) { $components = $this -> components; foreach($components as $c => $cconf) { if ($cconf['type']=='select_list') { $components[$c]['possible_values']=$this -> getSelectListComponentPossibleValues($c); } } $variables['components'] = $components; return parent::fetchTemplate($template, $variables); } /** * Translate componant value * * Return an array containing : * - value : untranslated value * - translated : translated value * * @param[in] $c string The component name * @param[in] $value string The value * @param[in] $inLoop boolean Internal param to control recursion * * @retval array **/ protected function translateComponentValue($c,$value,$inLoop=false) { if (!$inLoop && isset($this -> components[$c]['multiple']) && $this -> components[$c]['multiple']) { $retval = array(); foreach(ensureIsArray($value) as $val) $retval[] = $this -> translateComponentValue($c, $val, true); } else { $retval = array ( 'translated' => $value, 'value' => $value, ); if (isset($this -> components[$c])) { if ($this -> components[$c]['type']=='select_list') { $retval['translated'] = $this -> getSelectListComponentValueLabel($c,$value); } //elseif type == 'text' => no transformation } } return $retval; } /** * Retrieve possible values of an select_list component * * @param[in] $c string The component name * * @retval array **/ protected $_cache_getSelectListComponentPossibleValues=array(); protected function getSelectListComponentPossibleValues($c) { if (!isset($this -> _cache_getSelectListComponentPossibleValues[$c])) { if (!LSsession :: loadLSclass('LSattr_html_select_list')) return; $this -> _cache_getSelectListComponentPossibleValues[$c] = LSattr_html_select_list :: _getPossibleValues( $this -> components[$c]['options'], $this -> name, $this->attr_html->attribute->ldapObject ); self :: log_trace( "Component $c possible values: ".varDump($this -> _cache_getSelectListComponentPossibleValues[$c]) ); } return $this -> _cache_getSelectListComponentPossibleValues[$c]; } /** * Retrieve value's label of an select_list component * * @param[in] $c string The component name * @param[in] $value string The value * * @retval array **/ protected function getSelectListComponentValueLabel($c, $value) { if ($this -> getSelectListComponentPossibleValues($c)) { foreach ($this -> _cache_getSelectListComponentPossibleValues[$c] as $v => $label) { if (is_array($label)) { if (!isset($label['possible_values'])) continue; foreach ($label['possible_values'] as $vk => $vl) if ($vk == $$value) return $vl; } if ($v == $value) return $label; } } self :: log_trace("No label found for value '$value'"); return; } /** * Retrieve LSformElement value from POST data * * This method check present of this element's value in POST data and retrieve * it to feed the array passed in paramater. * * @param[in] &$return array Reference of the array for retrieved values * @param[in] $onlyIfPresent boolean If true and data of this element is not present in POST data, * just ignore it. * * @retval boolean true if value is in POST data, false instead */ public function getPostData(&$return, $onlyIfPresent=false) { if($this -> isFreeze()) { return true; } // Extract value form POST data $parseValues = array(); $present = false; // API mode if ($this -> form -> api_mode) { $present = isset($_POST[$this -> name]); $json_values = $this -> getData($_POST, $this -> name); self :: log_trace( $this." -> getPostData(onlyIfPresent=".($onlyIfPresent?1:0)."): ". "raw JSON values = ".varDump($json_values) ); $json_value_count = 0; foreach((is_array($json_values)?$json_values:array()) as $json_value) { $json_value_count += 1; $input_value = json_decode($json_value, true); self :: log_trace( $this." -> getPostData(onlyIfPresent=".($onlyIfPresent?1:0)."): ". "raw JSON value #$json_value_count = ".varDump($input_value) ); if (!is_array($input_value)) { $this -> form -> setElementError( $this -> attr_html, getFData(_('Fail to decode JSON value #%{idx}.'), $json_value_count) ); continue; } $parseValue = array(); $unemptyComponents = array(); foreach (array_keys($this -> components) as $c) { if (!isset($input_value[$c])) continue; if ($this -> getComponentConfig($c, 'multiple', false, 'bool')) { $parseValue[$c] = array(); if (is_array($input_value[$c])) { foreach($input_value[$c] as $val) { if (is_empty($val)) continue; $parseValue[$c][] = $val; } } } else { $parseValue[$c] = $input_value[$c]; } if (is_empty($parseValue[$c])) { unset($parseValue[$c]); continue; } $unemptyComponents[] = $c; } // Ignore empty value from form if (empty($unemptyComponents)) { $this -> form -> setElementError( $this -> attr_html, getFData(_('JSON value #%{idx} is invalid (or empty).'), $json_value_count) ); continue; } $parseValues[] = $parseValue; } } elseif (is_array($_POST[$this -> name.'__values_uuid'])) { $present = true; // HTML form mode foreach ($_POST[$this -> name.'__values_uuid'] as $uuid) { $parseValue = array(); $unemptyComponents = array(); foreach (array_keys($this -> components) as $c) { if (!isset($_POST[$this -> name.'__'.$c.'__'.$uuid])) continue; $parseValue[$c] = array(); foreach($_POST[$this -> name.'__'.$c.'__'.$uuid] as $val) { if (empty($val)) continue; $parseValue[$c][] = $val; } if (empty($parseValue[$c])) { unset($parseValue[$c]); continue; } if (!$this -> getComponentConfig($c, 'multiple', false, 'bool')) { $parseValue[$c] = $parseValue[$c][0]; } $unemptyComponents[] = $c; } // Ignore empty value from form if (empty($unemptyComponents)) continue; $parseValues[] = $parseValue; } } $return[$this -> name] = array(); // Check extracted values foreach ($parseValues as $parseValue) { // Check component value foreach ($parseValue as $c => $value) $this -> checkComponentValues($c, $value); // Check required components foreach (array_keys($this -> components) as $c) { if ($this -> getComponentConfig($c, 'required', false, 'bool') && !isset($parseValue[$c])) { $this -> form -> setElementError( $this -> attr_html, getFData( _('Component %{c} must be defined'), __($this -> getComponentConfig($c, 'label')) ) ); } } $return[$this -> name][] = json_encode($parseValue); } if (!$present && $onlyIfPresent) { self :: log_trace( $this." -> getPostData(onlyIfPresent=".($onlyIfPresent?1:0)."): ". "not present in POST data, ignore it." ); unset($return[$this -> name]); } return true; } /** * Check one component's values * * @param[] $c The component name * @param[] $value The values of the component * * @retval void **/ private function checkComponentValues($c, $value) { // Check select_list component's values if ($this -> getComponentConfig($c, 'type') == 'select_list') { if ($this -> getComponentConfig($c, 'multiple', false, 'bool')) { foreach ($value as $val) { $this -> checkSelectListComponentValue($c, $val); } } else $this -> checkSelectListComponentValue($c, $value); } // Apply check data rules LSsession :: loadLSclass('LSformRule', null, true); foreach($this -> getComponentConfig($c, 'check_data', array(), 'array') as $ruleType => $rconf) { $errors = LSformRule :: validate_values($ruleType, $value, $rconf, $this); if (is_array($errors)) { $retval = false; foreach ($errors as $error) { $this -> form -> setElementError( $this -> attr_html, getFData( __('%{label}: %{error}'), array( 'label' => __($this -> getComponentConfig($c, 'label')), 'error' => $error, ) ) ); } } } } /** * Check one select_list component's value * * @param[] $c The component name * @param[] $value The value to check * * @retval void **/ private function checkSelectListComponentValue($c, $value) { if (!$this -> getSelectListComponentValueLabel($c, $value)) { $this -> form -> setElementError( $this -> attr_html, getFData( _('Invalid value "%{value}" for component %{component}.'), array( 'value' => $value, 'component' => __($this -> getComponentConfig($c, 'label')) ) ) ); } } /** * Return a configuration parameter for a specific component (or default value) * * @param[] $component The component name * @param[] $param The configuration parameter * @param[] $default The default value (default : null) * @param[] $cast Cast resulting value in specific type (default : disabled) * * @retval mixed The configuration parameter value or default value if not set **/ public function getComponentConfig($component, $param, $default=null, $cast=null) { return LSconfig :: get( $param, $default, $cast, (array_key_exists($component, $this -> components)?$this -> components[$component]:array()) ); } /** * Retrieve value as return in API response * * @param[in] $details boolean If true, returned values will contain details if this field type * support it (optional, default: false) * * @retval mixed API value(s) or null/empty array if no value */ public function getApiValue($details=false) { $values = array(); foreach(ensureIsArray($this -> values) as $value) { $decodedValue = json_decode($value, true); if (is_array($decodedValue)) { $parsedValue = array(); foreach(array_keys($this -> components) as $c) { if (!isset($decodedValue[$c])) continue; if ($this -> getComponentConfig($c, 'multiple', false, 'bool')) { $parsedValue[$c] = ensureIsArray($decodedValue[$c]); } else { $parsedValue[$c] = $decodedValue[$c]; } } $values[] = $parsedValue; } } if ($this -> isMultiple()) { return $values; } if (!$values) return null; return $values[0]; } }