From a1b66d286d62619163fc8b3beed2e17d6dc91b9f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 3 Nov 2013 13:33:32 +0000 Subject: [PATCH] keep client/javascript from re-ordering nummeric option-values by sending them as array of objects with attribute value --- .../class.etemplate_widget_menupopup.inc.php | 75 ++++++++++++------- etemplate/js/et2_widget_selectbox.js | 57 +++++++------- 2 files changed, 79 insertions(+), 53 deletions(-) diff --git a/etemplate/inc/class.etemplate_widget_menupopup.inc.php b/etemplate/inc/class.etemplate_widget_menupopup.inc.php index 1533cc42e7..5915741308 100644 --- a/etemplate/inc/class.etemplate_widget_menupopup.inc.php +++ b/etemplate/inc/class.etemplate_widget_menupopup.inc.php @@ -83,18 +83,9 @@ class etemplate_widget_menupopup extends etemplate_widget { $value = $value_in = self::get_array($content, $form_name); - $allowed = $this->attrs['multiple'] ? array() : array('' => $this->attrs['options']); - /* if beforeSendToClient is used, we dont need to call it again here - if ($this->attrs['type']) - { - $allowed += self::typeOptions($form_name, $this->attrs['type'], $this->attrs['no_lang']); - // current eTemplate uses sel_options too, not sure if we want/need to keep that - //$allowed += self::selOptions($form_name, $this->attrs['no_lang']); - } - else*/ - { - $allowed += self::selOptions($form_name); - } + $allowed = self::selOptions($form_name, true); // true = return array of option-values + if (!$this->attrs['multiple']) $allowed[] = ''; + foreach((array) $value as $val) { // array_key_exists() (used below) is inconsistent in how it handles empty/false @@ -102,8 +93,8 @@ class etemplate_widget_menupopup extends etemplate_widget if(!$val && $val !== 0) $val = ''; // Special for select-account - selOptions doesn't always load all accounts - if($this->attrs['type'] == 'select-account' && !$GLOBALS['egw']->accounts->visible($val) && !isset($allowed[$val]) || - $this->attrs['type'] != 'select-account' && !array_key_exists($val,$allowed)) + if($this->attrs['type'] == 'select-account' && !$GLOBALS['egw']->accounts->visible($val) && !in_array($val, $allowed) ||//!isset($allowed[$val]) || + $this->attrs['type'] != 'select-account' && !in_array($val, $allowed))//!array_key_exists($val,$allowed)) { self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",array_keys($allowed))),''); $value = ''; @@ -144,7 +135,6 @@ class etemplate_widget_menupopup extends etemplate_widget ($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']), $no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name)); - // if no_lang was modified, forward modification to the client if ($no_lang != $this->attr['no_lang']) { @@ -152,7 +142,6 @@ class etemplate_widget_menupopup extends etemplate_widget } } - // Make sure  s, etc. are properly encoded when sent, and not double-encoded $options = (self::$request->sel_options[$form_name] ? $form_name : $this->id); if(is_array(self::$request->sel_options[$options])) @@ -172,37 +161,57 @@ class etemplate_widget_menupopup extends etemplate_widget * * @param array $options */ - public static function fix_encoded_options(array &$options) + public static function fix_encoded_options(array &$options, $use_array_of_objects=null) { - foreach($options as &$label) + $backup_options = $options; + + foreach($options as $value => &$label) { + if (is_null($use_array_of_objects) && is_numeric($value)) + { + $options = $backup_options; + return self::fix_encoded_options($options, true); + } // optgroup or values for keys "label" and "title" if(is_array($label)) { - self::fix_encoded_options($label); + self::fix_encoded_options($label, false); + if ($use_array_of_objects) $label['value'] = $value; } else { $label = html_entity_decode($label, ENT_NOQUOTES, 'utf-8'); + + if ($use_array_of_objects) + { + $label = array( + 'value' => $value, + 'label' => $label, + ); + } } } + if ($use_array_of_objects) + { + $options = array_values($options); + } } /** * Get options from $sel_options array for a given selectbox name * * @param string $name - * @param boolean $no_lang=false value of no_lang attribute + * @param boolean $return_values=false true: return array with option values, instead of value => label pairs * @return array */ - public static function selOptions($name) + public static function selOptions($name, $return_values=false) { $options = array(); // Check for exact match on name if (isset(self::$request->sel_options[$name]) && is_array(self::$request->sel_options[$name])) { - $options += self::$request->sel_options[$name]; + $options = array_merge($options, self::$request->sel_options[$name]); } // Check for base of name in root of sel_options @@ -214,11 +223,11 @@ class etemplate_widget_menupopup extends etemplate_widget $org_name = $name_parts[count($name_parts)-1]; if (isset(self::$request->sel_options[$org_name]) && is_array(self::$request->sel_options[$org_name])) { - $options += self::$request->sel_options[$org_name]; + $options = array_merge($options, self::$request->sel_options[$org_name]); } elseif (isset(self::$request->sel_options[$name_parts[0]]) && is_array(self::$request->sel_options[$name_parts[0]])) { - $options += self::$request->sel_options[$name_parts[0]]; + $options = array_merge($options, self::$request->sel_options[$name_parts[0]]); } } } @@ -226,7 +235,23 @@ class etemplate_widget_menupopup extends etemplate_widget // Check for options-$name in content if (is_array(self::$request->content['options-'.$name])) { - $options += self::$request->content['options-'.$name]; + $options = array_merge($options, self::$request->content['options-'.$name]); + } + if ($return_values) + { + $values = array(); + foreach($options as $key => $val) + { + if (is_array($val) && isset($val['value'])) + { + $values[] = $val['value']; + } + else + { + $values[] = $key; + } + } + $options = $values; } //error_log(__METHOD__."('$name') returning ".array2string($options)); return $options; diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js index bc31655c30..734246d9b5 100644 --- a/etemplate/js/et2_widget_selectbox.js +++ b/etemplate/js/et2_widget_selectbox.js @@ -78,7 +78,7 @@ var et2_selectbox = et2_inputWidget.extend( }, // Type specific legacy options. Avoid using. "other": { - "ignore": true, + "ignore": true, "type": "any" } }, @@ -87,7 +87,7 @@ var et2_selectbox = et2_inputWidget.extend( /** * Construtor - * + * * @memberOf et2_selectbox */ init: function() { @@ -95,14 +95,14 @@ var et2_selectbox = et2_inputWidget.extend( this.input = null; this.value = ''; - + // Allow no other widgets inside this one this.supportedWidgetClasses = []; - // Legacy options could have row count or empty label in first slot + // Legacy options could have row count or empty label in first slot if(typeof this.options.rows == "string") { - if(isNaN(this.options.rows)) + if(isNaN(this.options.rows)) { this.options.empty_label = this.options.rows; this.options.rows = 1; @@ -113,7 +113,7 @@ var et2_selectbox = et2_inputWidget.extend( } } - if(this.options.rows > 1) + if(this.options.rows > 1) { this.options.multiple = true; if(this.options.tags) @@ -183,7 +183,7 @@ var et2_selectbox = et2_inputWidget.extend( { content_options = this.getArrayMgr("sel_options").getEntry(name_parts[name_parts.length-1]); } - + // Try name like widget[$row] if(!content_options || content_options.length == 0) { @@ -249,7 +249,7 @@ var et2_selectbox = et2_inputWidget.extend( { return this._appendMultiOption(_value, _label, _title, dom_element); } - + var option = $j(document.createElement("option")) .attr("value", _value) .text(_label+""); @@ -263,14 +263,14 @@ var et2_selectbox = et2_inputWidget.extend( // Make sure empty / all option is first option.prependTo(this.input); } - else + else { option.appendTo(dom_element || this.input); } }, /** - * Append a value to multi-select + * Append a value to multi-select */ _appendMultiOption: function(_value, _label, _title, dom_element) { var option_data = null; @@ -282,7 +282,7 @@ var et2_selectbox = et2_inputWidget.extend( // Already in header if(_label == this.options.empty_label) return; - + var opt_id = this.dom_id + "_opt_" + _value; var label = jQuery(document.createElement("label")) .attr("for", opt_id) @@ -317,7 +317,7 @@ var et2_selectbox = et2_inputWidget.extend( } label.append(jQuery(""+_label+"")); var li = jQuery(document.createElement("li")).append(label); - + li.appendTo(dom_element || this.multiOptions); }, @@ -366,14 +366,14 @@ var et2_selectbox = et2_inputWidget.extend( .addClass("ui-multiselect-header") .appendTo(header); } - + // Set up for options to be added later var options = this.multiOptions = jQuery(document.createElement("ul")); this.multiOptions.addClass("ui-multiselect-checkboxes ui-helper-reset") .css("height", 1.9*this.options.rows + "em") .appendTo(node); - if(this.options.rows >= 5) + if(this.options.rows >= 5) { // Check / uncheck all var header_controls = { @@ -397,7 +397,7 @@ var et2_selectbox = et2_inputWidget.extend( } } - + this.setDOMNode(node[0]); }, @@ -534,14 +534,14 @@ var et2_selectbox = et2_inputWidget.extend( */ set_tags: function(tags) { this.options.tags = tags; - + // Can't actually do chosen until attached, loadingFinished should call again if(!this.isAttached()) return; if(this.input != null && !this.options.tags && !this.options.search) { this.input.unchosen().css('width', ''); - + return; } @@ -614,12 +614,12 @@ var et2_selectbox = et2_inputWidget.extend( .append('
  • '+key+'
  • ') .appendTo(this.multiOptions); } - + for(var sub in _options[key]) { if (typeof _options[key][sub] === 'object') { - this._appendOptionElement(sub, + this._appendOptionElement(sub, _options[key][sub]["label"] ? _options[key][sub]["label"] : "", _options[key][sub]["title"] ? _options[key][sub]["title"] : "", group @@ -635,11 +635,12 @@ var et2_selectbox = et2_inputWidget.extend( { // Allow some special extras for objects by passing the whole thing _options[key]["label"] = _options[key]["label"] ? _options[key]["label"] : ""; - this._appendMultiOption(key, _options[key], _options[key]["title"]); + this._appendMultiOption(_options[key].value ? _options[key].value : key, + _options[key], _options[key]["title"]); } else { - this._appendOptionElement(key, + this._appendOptionElement(_options[key].value ? _options[key].value : key, _options[key]["label"] ? _options[key]["label"] : "", _options[key]["title"] ? _options[key]["title"] : ""); } @@ -667,7 +668,7 @@ var et2_selectbox = et2_inputWidget.extend( } return this.value; }, - + isDirty: function() { if(this.input == null) { @@ -689,14 +690,14 @@ et2_register_widget(et2_selectbox, ["menupopup", "listbox", "select", "select-ca /** * et2_selectbox_ro is the readonly implementation of the selectbox. - * + * * @augments et2_selectbox */ -var et2_selectbox_ro = et2_selectbox.extend([et2_IDetachedDOM], +var et2_selectbox_ro = et2_selectbox.extend([et2_IDetachedDOM], { /** * Constructor - * + * * @memberOf et2_selectbox_ro */ init: function() { @@ -917,14 +918,14 @@ et2_register_widget(et2_selectbox_ro, ["menupopup_ro", "listbox_ro", "select_ro" /** * Class which just implements the menulist container - * + * * @augments et2_DOMWidget - */ + */ var et2_menulist = et2_DOMWidget.extend( { /** * Construtor - * + * * @memberOf et2_menulist */ init: function() {