0, 'item' => array( * array('id' => '/INBOX', 'text' => 'INBOX', 'tooltip' => 'Your inbox', 'open' => 1, 'im1' => 'kfm_home.png', 'im2' => 'kfm_home.png', 'child' => '1', 'item' => array( * array('id' => '/INBOX/sub', 'text' => 'sub', 'im0' => 'folderClosed.gif'), * array('id' => '/INBOX/sub2', 'text' => 'sub2', 'im0' => 'folderClosed.gif'), * )), * array('id' => '/user', 'text' => 'user', 'child' => '1', 'item' => array( * array('id' => '/user/birgit', 'text' => 'birgit', 'im0' => 'folderClosed.gif'), * )), * )); * * Please note: * - id of root-item has to be 0, ids of sub-items can be strings or numbers * - item has to be an array (not json object: numerical keys 0, 1, ...) * - im0: leaf image (default: leaf.gif), im1: open folder image (default: folderOpen.gif), im2: closed folder (default: folderClosed.gif) * - for arbitrary image eg. $icon = common::image($app, 'navbar') use following code: * list(,$icon) = explode($GLOBALS['egw_info']['server']['webserver_url'], $icon); * $icon = '../../../../..'.$icon; * - json autoloading uses identical data-structur */ egw_framework::includeCSS('/phpgwapi/js/dhtmlxtree/css/dhtmlXTree.css'); class etemplate_widget_tree extends etemplate_widget { /** * 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 */ public function set_attrs($xml) { $this->attrs['type'] = $xml->localName; parent::set_attrs($xml); // set attrs[multiple] from attrs[options] if ($this->attrs['options'] > 1) { $this->setElementAttribute($this->id, 'multiple', true); } } /** * 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']); $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 (is_array($value)) $value = implode(',',$value); 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'], $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(); if (isset(self::$request->sel_options[$name]) && is_array(self::$request->sel_options[$name])) { $options += self::$request->sel_options[$name]; } else { $name_parts = explode('[',str_replace(']','',$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]]; } } } 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 tree 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 * @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) = explode(',',$legacy_options); $no_lang = false; $options = array(); switch ($widget_type) { case 'tree-cat': // !$type == globals cats too, $type2: extraStyleMultiselect, $type3: application, if not current-app, $type4: parent-id, $type5=owner (-1=global),$type6=show missing if ($readonly) // for readonly we dont need to fetch all cat's, nor do we need to indent them by level { $cell['no_lang'] = True; foreach(is_array($value) ? $value : (strpos($value,',') !== false ? explode(',',$value) : array($value)) as $id) { if ($id) $cell['sel_options'][$id] = stripslashes($GLOBALS['egw']->categories->id2name($id)); } break; } if (!$type3 || $type3 === $GLOBALS['egw']->categories->app_name) { $categories =& $GLOBALS['egw']->categories; } else // we need to instanciate a new cat object for the correct application { $categories = new categories('',$type3); } $cat2path=array(); foreach((array)$categories->return_sorted_array(0,False,'','','',!$type,0,true) as $cat) { $s = stripslashes($cat['name']); if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1') { $s .= ' ♦'; } $cat2path[$cat['id']] = $path = ($cat['parent'] ? $cat2path[$cat['parent']].'/' : '').(string)$cat['id']; // 1D array $options[$cat['id']] = $cat + array( 'text' => $s, 'path' => $path, /* These ones to play nice when a user puts a tree & a selectbox with the same ID on the form (addressbook edit): if tree overwrites selectbox options, selectbox will still work */ 'label' => $s, 'title' => $cat['description'] ); // Tree in array //$options[$cat['parent']][] = $cat; } // change cat-ids to pathes and preserv unavailible cats (eg. private user-cats) if ($value) { $pathes = $extension_data['unavailable'] = array(); foreach(is_array($value) ? $value : explode(',',$value) as $cat) { if (isset($cat2path[$cat])) { $pathes[] = $cat2path[$cat]; } else { $extension_data['unavailable'][] = $cat; } } $value = $rows ? $pathes : $pathes[0]; } $cell['size'] = $rows.($type2 ? ','.$type2 : ''); $no_lang = True; break; } //error_log(__METHOD__."('$widget_type', '$legacy_options', no_lang=".array2string($no_lang).', readonly='.array2string($readonly).", value=$value) returning ".array2string($options)); return $options; } }