Work in progress on tree

This commit is contained in:
Nathan Gray 2012-03-07 00:30:47 +00:00
parent 8b20b2d314
commit 548ac686d8
2 changed files with 380 additions and 0 deletions

View File

@ -0,0 +1,232 @@
<?php
/**
* EGroupware - eTemplate serverside tree widget
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright 2012 Nathan Gray
* @version $Id$
*/
/**
* eTemplate tree widget
*/
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], 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 $content
* @param array &$validated=array() validated content
*/
public function validate($cname, array $content, &$validated=array())
{
$form_name = self::form_name($cname, $this->id);
$ok = true;
if (!$this->is_readonly($cname))
{
$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 &nbsp;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');
}
}
error_log(array2string(self::$request->sel_options[$form_name]));
}
/**
* 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) as $cat)
{
$s = stripslashes($cat['name']);
if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1')
{
$s .= ' &#9830;';
}
$cat2path[$cat['id']] = $path = ($cat['parent'] ? $cat2path[$cat['parent']].'/' : '').(string)$cat['id'];
//$options[$cat['id']] = $cat + array('text' => $cat['name'], 'path' => $path);
$options[] = array($cat['id'],$cat['parent'],$cat['name']);
}
// 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;
}
}

View File

@ -0,0 +1,148 @@
/**
* eGroupWare eTemplate2 - JS Tree object
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage api
* @link http://www.egroupware.org
* @author Nathan Gray
* @copyright Nathan Gray 2011
* @version $Id$
*/
"use strict";
/*egw:uses
et2_core_inputWidget;
/phpgwapi/js/dhtmlxtree/js/dhtmlXCommon.js;
/phpgwapi/js/dhtmlxtree/js/dhtmlXTree.js;
/phpgwapi/js/dhtmlxtree/dhtmlxTree/codebase/ext/dhtmlxtree_json.js;
/phpgwapi/js/dhtmlxtree/dhtmlxTree/sources/ext/dhtmlxtree_start.js;
*/
var et2_tree = et2_baseWidget.extend({
attributes: {
"multiple": {
"name": "multiple",
"type": "boolean",
"default": false,
"description": "Allow selecting multiple options"
},
"select_options": {
"type": "any",
"name": "Select options",
"default": {},
"description": "Used to set the tree options."
},
"onnodeselect": {
"name": "onNodeSelect",
"type": "string",
"default": "",
"description": "Javascript executed when user selects a node"
},
"oncheck": {
"name": "onNodeSelect",
"type": "string",
"default": "",
"description": "Javascript executed when user checks a node"
},
"value": {
"type": "any",
"default": {}
}
},
init: function() {
this._super.apply(this, arguments);
this.input = null;
this.div = $j(document.createElement("div")).addClass("dhtmlxTree");
this.setDOMNode(this.div[0]);
},
/**
* Get tree items from the sel_options data array
*/
transformAttributes: function(_attrs) {
this._super.apply(this, arguments);
// If select_options are already known, skip the rest
if(this.options && this.options.select_options && !jQuery.isEmptyObject(this.options.select_options))
{
return;
}
var name_parts = this.id.replace(/]/g,'').split('[');
// Try to find the options inside the "sel-options" array
if(this.getArrayMgr("sel_options"))
{
// Select options tend to be defined once, at the top level, so try that first
var content_options = this.getArrayMgr("sel_options").getRoot().getEntry(name_parts[name_parts.length-1]);
// Try again according to ID
if(!content_options) content_options = this.getArrayMgr("sel_options").getEntry(this.id);
if(_attrs["select_options"] && content_options)
{
_attrs["select_options"] = jQuery.extend({},_attrs["select_options"],content_options);
} else if (content_options) {
_attrs["select_options"] = content_options;
}
}
// Check whether the options entry was found, if not read it from the
// content array.
if (_attrs["select_options"] == null)
{
// Again, try last name part at top level
var content_options = this.getArrayMgr('content').getRoot().getEntry(name_parts[name_parts.length-1]);
// If that didn't work, check according to ID
_attrs["select_options"] = content_options ? content_options : this.getArrayMgr('content')
.getEntry("options-" + this.id)
}
// Default to an empty object
if (_attrs["select_options"] == null)
{
_attrs["select_options"] = {};
}
},
createTree: function(widget) {
this.egw().debug("log","ID",widget.div.attr("id"));
this.egw().debug("log",widget.div[0].parentElement);
window = this.egw().window;
widget.input = new dhtmlXTreeObject({
parent: widget.div[0],
width: '100%',
height: '100%',
checkbox: (widget.options.oncheck),
onCheck: widget.options.oncheck,
// multiselect: widget.options.multiple // Documented, but not available
});
},
set_select_options: function(options) {
if(this.input !== null)
{
this.input.loadJSArray(options);
return;
}
var xmp = jQuery(document.createElement('ul')).attr("container",true).appendTo(this.div);
for(var key in options)
{
var name = (!this.options.no_lang) ? options[key][2] : this.egw().lang(options[key].name ? options[key].name : options[key][2]);
var item = jQuery(document.createElement('li'))
.attr("id", options[key][0])
.text( name)
.appendTo(xmp);
}
this.input = dhtmlXTreeFromHTML(this.div[0]);
}
});
et2_register_widget(et2_tree, ["tree","tree-cat"]);