* @copyright 2002-11 by RalfBecker@outdoor-training.de * @version $Id$ */ /** * eTemplate select widget * * @todo new account selection method */ class etemplate_widget_menupopup extends etemplate_widget { /** * @var array */ protected static $monthnames = array( 0 => '', 1 => 'January', 2 => 'February', 3 => 'March', 4 => 'April', 5 => 'May', 6 => 'June', 7 => 'July', 8 => 'August', 9 => 'September', 10 => 'October', 11 => 'November', 12 => 'December' ); /** * Parse and set extra attributes from xml in template object * * Reimplemented to parse our differnt attributes * * @param string|XMLReader $xml * @return etemplate_widget_template current object or clone, if any attribute was set * @todo Use legacy_attributes instead of leaving it to typeOptions method to parse them */ public function set_attrs($xml) { parent::set_attrs($xml); // set attrs[multiple] from attrs[options], unset options only if it just contains number or rows if ($this->attrs['options'] > 1) { $this->attrs['multiple'] = (int)$this->attrs['options']; if ((string)$this->attrs['multiple'] == $this->attrs['options']) { unset($this->attrs['options']); } } } /** * Validate input * * @param string $cname current namespace * @param array $expand values for keys 'c', 'row', 'c_', 'row_', 'cont' * @param array $content * @param array &$validated=array() validated content */ public function validate($cname, array $expand, array $content, &$validated=array()) { $form_name = self::form_name($cname, $this->id, $expand); $ok = true; if (!$this->is_readonly($cname, $form_name)) { $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); } foreach((array) $value as $val) { if (!($this->attrs['multiple'] && !$val) && !isset($allowed[$val])) { self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",array_keys($allowed))),''); $value = ''; break; } } if ($ok && $value === '' && $this->attrs['needed']) { self::set_validation_error($form_name,lang('Field must not be empty !!!',$value),''); } $valid =& self::get_array($validated, $form_name, true); $valid = $value; error_log(__METHOD__."() $form_name: ".array2string($value_in).' --> '.array2string($value).', allowed='.array2string($allowed)); } } /** * Fill type options in self::$request->sel_options to be used on the client * * @param string $cname */ public function beforeSendToClient($cname) { $form_name = self::form_name($cname, $this->id); if (!is_array(self::$request->sel_options[$form_name])) self::$request->sel_options[$form_name] = array(); if ($this->attrs['type']) { // += to keep further options set by app code self::$request->sel_options[$form_name] += self::typeOptions($this->attrs['type'], // typeOptions thinks # of rows is the first thing in options ($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']) { self::setElementAttribute($form_name, 'no_lang', $no_lang); } } // Make sure s, etc. are properly encoded when sent, and not double-encoded foreach(self::$request->sel_options[$form_name] as &$label) { if(!is_array($label)) { $label = html_entity_decode($label, ENT_NOQUOTES,'utf-8'); } elseif($label['label']) { $label['label'] = html_entity_decode($label['label'], ENT_NOQUOTES,'utf-8'); } } } /** * Get options from $sel_options array for a given selectbox name * * @param string $name * @param boolean $no_lang=false value of no_lang attribute * @return array */ public static function selOptions($name) { $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]; } // Check for base of name in root of sel_options if(!$options) { $name_parts = explode('[',str_replace(array('[',']',']'),array('['),$name)); if (count($name_parts)) { $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]; } 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]]; } } } // Check for options-$name in content if (isset(self::$request->content['options-'.$name])) { $options += self::$request->content['options-'.$name]; } //error_log(__METHOD__."('$name') returning ".array2string($options)); return $options; } /** * Fetch options for certain select-box types * * @param string $widget_type * @param string $legacy_options options string of widget * @param boolean $no_lang=false initial value of no_lang attribute (some types set it to true) * @param boolean $readonly=false for readonly we dont need to fetch all options, only the one for value * @param mixed $value=null value for readonly * @return array with value => label pairs */ public static function typeOptions($widget_type, $legacy_options, &$no_lang=false, $readonly=false, $value=null) { list($rows,$type,$type2,$type3,$type4,$type5,$type6) = explode(',',$legacy_options); $no_lang = false; $options = array(); switch ($widget_type) { case 'select-percent': // options: #row,decrement(default=10) $decr = $type > 0 ? $type : 10; for ($i=0; $i <= 100; $i += $decr) { $options[intval($i)] = intval($i).'%'; } $options[100] = '100%'; if (!$rows || !empty($value)) { $value = intval(($value+($decr/2)) / $decr) * $decr; } $no_lang = True; break; case 'select-priority': $options = array('','low','normal','high'); break; case 'select-bool': // equal to checkbox, can be used with nextmatch-customfilter to filter a boolean column $options = array(0 => 'no',1 => 'yes'); break; case 'select-access': $options = array( 'private' => 'Private', 'public' => 'Global public', 'group' => 'Group public' ); break; case 'select-country': // #Row|Extralabel,1=use country name, 0=use 2 letter-code,custom country field name if($type == 0 && $type2) { $custom_label = is_numeric($type2) ? 'Custom' : $type2; $options = array('-custom-' => lang($custom_label)) + $GLOBALS['egw']->country->countries(); } else { $options = $GLOBALS['egw']->country->countries(); } if (($extension_data['country_use_name'] = $type) && $value) { $value = $GLOBALS['egw']->country->country_code($value); if (!isset($options[$value])) { if($type2) { $options[$value] = $value; } } } $no_lang = True; break; case 'select-state': $options = $GLOBALS['egw']->country->us_states(); $no_lang = True; break; case 'select-cat': // !$type == globals cats too, $type2: extraStyleMultiselect, $type3: application, if not current-app, $type4: parent-id, $type5=owner (-1=global),$type6=show missing if ((!$type3 || $type3 === $GLOBALS['egw']->categories->app_name) && (!$type5 || $type5 == $GLOBALS['egw']->categories->account_id)) { $categories = $GLOBALS['egw']->categories; } else // we need to instanciate a new cat object for the correct application { $categories = new categories($type5,$type3); } // Allow text for global $type = ($type && strlen($type) > 1 ? $type : !$type); // we cast $type4 (parent) to int, to get default of 0 if omitted foreach((array)$categories->return_sorted_array(0,False,'','','',$type,(int)$type4,true) as $cat) { $s = str_repeat(' ',$cat['level']) . stripslashes($cat['name']); if (categories::is_global($cat)) { static $global_marker; if (is_null($global_marker)) { // as we add options with .text(), it can't be entities, but php knows no string literals with utf-8 $global_marker = html_entity_decode(' ♦', ENT_NOQUOTES, 'utf-8'); } $s .= $global_marker; } $options[$cat['id']] = array( 'label' => $s, 'title' => empty($cat['description']) ? $s : $cat['description'], ); // Send data too $options[$cat['id']] += $cat['data']; } // preserv unavailible cats (eg. private user-cats) /* TODO if ($value && ($unavailible = array_diff(is_array($value) ? $value : explode(',',$value),array_keys((array)$options)))) { $extension_data['unavailible'] = $unavailible; } $cell['size'] = $rows.($type2 ? ','.$type2 : ''); */ $no_lang = True; break; case 'select-account': // options: #rows,{accounts(default)|both|groups|owngroups},{0(=lid)|1(default=name)|2(=lid+name),expand-multiselect-rows,not-to-show-accounts,...)} //echo "
select-account widget: name=$cell[name], type='$type', rows=$rows, readonly=".(int)($cell['readonly'] || $readonlys)."
\n"; // in case of readonly, we read/create only the needed entries, as reading accounts is expensive if ($readonly) { $no_lang = True; if (!is_array($value) && strpos($value,',') !== false) $value = explode(',',$value); foreach(is_array($value) ? $value : array($value) as $id) { $options[$id] = !$id && !is_numeric($rows) ? lang($rows) : self::accountInfo($id,$acc,$type2,$type=='both'); } break; } if($type == 'owngroups') { $type = 'groups'; $owngroups = true; foreach($GLOBALS['egw']->accounts->membership() as $group) $mygroups[] = $group['account_id']; } /* account-selection for hughe number of accounts if ($this->ui == 'html' && $type != 'groups') // use eGW's new account-selection (html only) { $not = array_slice(explode(',',$cell['size']),4); $help = (int)$no_lang < 2 ? lang($cell['help']) : $cell['help']; $onFocus = "self.status='".addslashes(htmlspecialchars($help))."'; return true;"; $onBlur = "self.status=''; return true;"; if ($cell['noprint']) { foreach(is_array($value) ? $value : (strpos($value,',') !== false ? explode(',',$value) : array($value)) as $id) { if ($id) $onlyPrint[] = self::accountInfo($id,$acc,$type2,$type=='both'); } $onlyPrint = $onlyPrint ? implode('