first version of a tree widget (atm the whole tree need to be supplied, like for select)

This commit is contained in:
Ralf Becker 2007-07-22 13:23:44 +00:00
parent edc5dc9db5
commit 9f1df33c74
5 changed files with 337 additions and 79 deletions

View File

@ -0,0 +1,218 @@
<?php
/**
* eGroupWare eTemplate Extension - Tree Widgets
*
* @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License
* @package etemplate
* @subpackage extensions
* @link http://www.egroupware.org
* @author Ralf Becker <RalfBecker@outdoor-training.de>
* @version $Id$
*/
/**
* eTemplate Extension: tree widgets with predefined eGW specific content
*/
class tree_widget
{
/**
* exported methods of this class
* @var array
*/
var $public_functions = array(
'pre_process' => True,
'post_process' => True,
);
/**
* availible extensions and there names for the editor
* @var array
*/
var $human_name = array(
'tree' => 'Tree',
'tree-cat' => 'Category tree',
);
/**
* Constructor of the extension
*
* @param string $ui '' for html
*/
function tree_widget($ui)
{
$this->ui = $ui;
}
/**
* pre-processing of the extension
*
* This function is called before the extension gets rendered
*
* @param string $name form-name of the control
* @param mixed &$value value / existing content, can be modified
* @param array &$cell array with the widget, can be modified for ui-independent widgets
* @param array &$readonlys names of widgets as key, to be made readonly
* @param mixed &$extension_data data the extension can store persisten between pre- and post-process
* @param etemplate &$tmpl reference to the template we belong too
* @return boolean true if extra label is allowed, false otherwise
*/
function pre_process($name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl)
{
list($rows,$type,$type2,$type3) = explode(',',$cell['size']);
$extension_data['type'] = $cell['type'];
$extension_data['multiple'] = $rows;
$readonly = $cell['readonly'] || $readonlys;
switch ($cell['type'])
{
case 'tree-cat': // !$type == globals cats too, $type2: not used, $type3: application, if not current-app
if (!is_object($GLOBALS['egw']->categories))
{
$GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories');
}
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'];
$cell['sel_options'][$path] = $s;
}
// 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 : '');
$cell['no_lang'] = True;
break;
}
// creating a div-id and var-name for the tree-object by replacing brackets with underscores and removing exec or evtl. cont parts
$tree_id = str_replace(array('exec[cont][','exec[','[',']'),array('','','_',''),$name);
$onNodeSelect = 'onNodeSelect_'.$tree_id;
$script = $tmpl->html->input_hidden($name,$value,false)."<script type='text/javascript'>";
if (($onclick = $cell['onclick']))
{
if (strpos($onclick,'$') !== false || $onclick{0} == '@')
{
$onclick = $tmpl->expand_name($onclick,$c,$r,$content['.c'],$content['.row'],$content);
}
$onclick = $tmpl->js_pseudo_funcs($onclick,$tmpl->name_vars);
}
if ($rows >= 1) // multiselction with checkboxes --> use onNodeSelect to check the item
{
unset($cell['sel_options']['']);
$onCheck = 'onCheck_'.$tree_id;
$script .= "
function $onCheck(id) {
document.getElementsByName('$name')[0].value=$tree_id.getAllChecked();
$onclick;
}
function $onNodeSelect(id) {
$tree_id.setCheck(id,$tree_id.isItemChecked(id) ? 0 : 1);
$onCheck(id);
}
";
}
else // single selection
{
$script .= "
function $onNodeSelect(id) {
document.getElementsByName('$name')[0].value=id;
$onclick;
}
";
}
$script .= "</script>\n";
list(,$class) = explode(',',$cell['span']);
$value = $script.$tmpl->html->tree($tmpl->_sel_options($cell,$name),$value,false,$onNodeSelect,$tree_id,$class,'',$onCheck);
$cell = etemplate::empty_cell('html',$cell['name']);
return True; // extra Label Ok
}
/**
* postprocessing method, called after the submission of the form
*
* It has to copy the allowed/valid data from $value_in to $value, otherwise the widget
* will return no data (if it has a preprocessing method). The framework insures that
* the post-processing of all contained widget has been done before.
*
* Only used by select-dow so far
*
* @param string $name form-name of the widget
* @param mixed &$value the extension returns here it's input, if there's any
* @param mixed &$extension_data persistent storage between calls or pre- and post-process
* @param boolean &$loop can be set to true to request a re-submision of the form/dialog
* @param object &$tmpl the eTemplate the widget belongs too
* @param mixed &value_in the posted values (already striped of magic-quotes)
* @return boolean true if $value has valid content, on false no content will be returned!
*/
function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in)
{
//echo "value_in"; _debug_array($value_in);
if (!preg_match('/^[0-9\\/'.($extension_data['multiple']?',':'').']*$/',$value_in)) return false; // guard against xss and other malious content
$value = $extension_data['multiple'] ? explode(',',$value_in) : $value_in;
switch ($extension_data['type'])
{
case 'tree-cat':
if ($extension_data['multiple'])
{
foreach($value as $k => $path)
{
$parts = explode('/',$path);
$value[$k] = array_pop($parts);
}
if ($extension_data['unavailable'])
{
$value += $extension_data['unavailable'];
}
}
else
{
$parts = explode('/',$path);
$value = array_pop($parts);
}
}
//echo "value"; _debug_array($value);
return true;
}
}

View File

@ -706,7 +706,7 @@ foreach($sess as $key => $val)
}
else
{
$cell = &$cols[$c_key];
$cell = $cols[$c_key];
list($col_width,$col_disabled) = explode(',',$opts[$col]);
if (!$cell['height']) // if not set, cell-height = height of row
@ -742,7 +742,7 @@ foreach($sess as $key => $val)
unset($row_data[$col]); // omit empty/disabled cells if only one row
continue;
}
if (strlen($cell['onclick']) > 1 && !in_array($cell['type'],array('button','buttononly')))
if (strlen($cell['onclick']) > 1)
{
$onclick = $cell['onclick'];
if (strpos($onclick,'$') !== false || $onclick{0} == '@')
@ -859,7 +859,7 @@ foreach($sess as $key => $val)
* @param string $path path in the widget tree
* @return string the generated HTML
*/
function show_cell($cell,$content,$readonlys,$cname,$show_c,$show_row,&$span,&$class,$path='')
function show_cell(&$cell,$content,$readonlys,$cname,$show_c,$show_row,&$span,&$class,$path='')
{
if ($this->debug && (is_int($this->debug) && $this->debug >= 3 || $this->debug == $cell['type']))
{
@ -969,7 +969,7 @@ foreach($sess as $key => $val)
{
$onFocus .= "self.status='".addslashes($this->html->htmlspecialchars($help))."'; return true;";
$onBlur .= "self.status=''; return true;";
if ($cell['type'] == 'button' || $cell['type'] == 'buttononly' || $cell['type'] == 'file') // for button additionally when mouse over button
if (in_array($cell['type'],array('button','buttononly','file'))) // for button additionally when mouse over button
{
$options .= " onMouseOver=\"self.status='".addslashes($this->html->htmlspecialchars($help))."'; return true;\"";
$options .= " onMouseOut=\"self.status=''; return true;\"";
@ -1176,6 +1176,7 @@ foreach($sess as $key => $val)
{
$onclick = $this->js_pseudo_funcs($onclick,$cname);
}
unset($cell['onclick']); // otherwise the grid will handle it
if ($this->java_script() && ($cell['onchange'] != '' || $img && !$readonly) && !$cell['needed']) // use a link instead of a button
{
$onclick = ($onclick ? preg_replace('/^return(.*);$/','if (\\1) ',$onclick) : '').
@ -1321,46 +1322,8 @@ foreach($sess as $key => $val)
}
$multiple = 0;
}
if (!empty($cell['sel_options']))
{
if (!is_array($cell['sel_options']))
{
$opts = explode(',',$cell['sel_options']);
while (list(,$opt) = each($opts))
{
list($k,$v) = explode('=',$opt);
$sels[$k] = $v;
}
}
else
{
$sels += $cell['sel_options'];
}
}
if (isset($this->sel_options[$name]) && is_array($this->sel_options[$name]))
{
$sels += $this->sel_options[$name];
}
else
{
$name_parts = explode('[',str_replace(']','',$name));
if (count($name_parts))
{
$org_name = $name_parts[count($name_parts)-1];
if (isset($this->sel_options[$org_name]) && is_array($this->sel_options[$org_name]))
{
$sels += $this->sel_options[$org_name];
}
elseif (isset($this->sel_options[$name_parts[0]]) && is_array($this->sel_options[$name_parts[0]]))
{
$sels += $this->sel_options[$name_parts[0]];
}
}
}
if (isset($content["options-$name"]))
{
$sels += $content["options-$name"];
}
$sels += $this->_sel_options($cell,$name,$content);
if ($multiple && !is_array($value)) $value = explode(',',$value);
if ($readonly || $cell['noprint'])
{
@ -1457,7 +1420,8 @@ foreach($sess as $key => $val)
if (!$orient) $orient = $type == 'hbox' ? 'horizontal' : ($type == 'box' ? false : 'vertical');
for ($n = 1; $n <= (int) $num; ++$n)
{
$h = $this->show_cell($cell[$n],$content,$readonlys,$cname,$show_c,$show_row,$nul,$cl,$path.'/'.$n);
$child = $cell[$n]; // first param is a var_param now!
$h = $this->show_cell($child,$content,$readonlys,$cname,$show_c,$show_row,$nul,$cl,$path.'/'.$n);
if ($h != '' && $h != '&nbsp;' || $keep_empty)
{
if ($orient != 'horizontal')
@ -1479,14 +1443,13 @@ foreach($sess as $key => $val)
$box_anz++;
if ($cell[$n]['align'])
{
$rows[$box_row]['.'.$box_col] = $this->html->formatOptions($cell[$n]['align'],'align');
$rows[$box_row]['.'.$box_col] = $this->html->formatOptions($child['align'],'align');
$sub_cell_has_align = true;
}
// can only be set via source at the moment
if (strlen($cell[$n]['onclick']) > 1 && !($cell[$n]['type'] == 'button' || $cell[$n]['type'] == 'buttononly'))
if (strlen($child['onclick']) > 1)
{
$rows[$box_row]['.'.$box_col] .= ' onclick="'.$this->js_pseudo_funcs($cell[$n]['onclick'],$cname).'"'.
($cell[$n]['id'] ? ' id="'.$cell[$n]['id'].'"' : '');
$rows[$box_row]['.'.$box_col] .= ' onclick="'.$this->js_pseudo_funcs($child['onclick'],$cname).'"'.
($child['id'] ? ' id="'.$child['id'].'"' : '');
}
// allow to set further attributes in the tablecell, beside the class
if (is_array($cl))
@ -1563,10 +1526,11 @@ foreach($sess as $key => $val)
for ($n = 1; $n <= $cell_options; ++$n)
{
$html .= $this->html->div($this->show_cell($cell[$n],$content,$readonlys,$cname,$show_c,
$child = $cell[$n]; // first param is a var_param now!
$html .= $this->html->div($this->show_cell($child,$content,$readonlys,$cname,$show_c,
$show_row,$nul,$cl,$path.'/'.$n),$this->html->formatOptions(array(
'display: '.($value == $cell[$n]['name'] ? 'inline' : 'none').';',
$cell[$n]['name']
'display: '.($value == $child['name'] ? 'inline' : 'none').';',
$child['name']
),'style,id'));
}
break;
@ -1671,6 +1635,61 @@ foreach($sess as $key => $val)
return $html;
}
/**
* Retrive options for selectboxes and similar widgets (eg. the tree)
*
* @param array $cell
* @param string $name
* @param array $content=array();
* @return array
*/
function _sel_options($cell,$name,$content=array())
{
$sels = array();
if (!empty($cell['sel_options']))
{
if (!is_array($cell['sel_options']))
{
$opts = explode(',',$cell['sel_options']);
while (list(,$opt) = each($opts))
{
list($k,$v) = explode('=',$opt);
$sels[$k] = $v;
}
}
else
{
$sels += $cell['sel_options'];
}
}
if (isset($this->sel_options[$name]) && is_array($this->sel_options[$name]))
{
$sels += $this->sel_options[$name];
}
else
{
$name_parts = explode('[',str_replace(']','',$name));
if (count($name_parts))
{
$org_name = $name_parts[count($name_parts)-1];
if (isset($this->sel_options[$org_name]) && is_array($this->sel_options[$org_name]))
{
$sels += $this->sel_options[$org_name];
}
elseif (isset($this->sel_options[$name_parts[0]]) && is_array($this->sel_options[$name_parts[0]]))
{
$sels += $this->sel_options[$name_parts[0]];
}
}
}
if (isset($content["options-$name"]))
{
$sels += $content["options-$name"];
}
return $sels;
}
/**
* Resolve javascript pseudo functions:
* - egw::link('$l','$p') calls $egw->link($l,$p)

View File

@ -52,6 +52,7 @@ cant delete a single widget from a grid !!! etemplate de Kann kein einzelnes Wid
cant delete the only column of a grid !!! etemplate de Kann nicht die einzige Spalte einer Tabelle löschen !!!
cant delete the only row in a grid !!! etemplate de Kann nicht die einzige Zeile einer Tabelle löschen !!!
category etemplate de Kategorie
category tree etemplate de Kategoriebaum
cellpadding for the table-tag etemplate de Innenabstand (cellpadding) der Tabelle
cells etemplate de Zellen
cellspacing for the table-tag etemplate de Zellenabstand (cellspacing) der Tabelle
@ -367,6 +368,7 @@ to start the etemplate editor etemplate de startet den eTemplate Editor
to start the search etemplate de startet die Suche
today etemplate de Heute
top etemplate de Oben
tree etemplate de Baum
type etemplate de Typ
type of the column etemplate de Typ der Spalte
type of the field (select label if field should be empty) etemplate de Type des Feldes (Beschriftung auswählen wenn Feld leer sein soll)

View File

@ -52,6 +52,7 @@ cant delete a single widget from a grid !!! etemplate en cant delete a single wi
cant delete the only column of a grid !!! etemplate en cant delete the only column of a grid !!!
cant delete the only row in a grid !!! etemplate en cant delete the only row in a grid !!!
category etemplate en Category
category tree etemplate en Category tree
cellpadding for the table-tag etemplate en Cellpadding for the table-tag
cells etemplate en Cells
cellspacing for the table-tag etemplate en Cellspacing for the table-tag
@ -367,6 +368,7 @@ to start the etemplate editor etemplate en to start the eTemplate editor
to start the search etemplate en to start the search
today etemplate en Today
top etemplate en Top
tree etemplate en Tree
type etemplate en Type
type of the column etemplate en type of the column
type of the field (select label if field should be empty) etemplate en type of the field (select Label if field should be empty)

View File

@ -233,6 +233,11 @@ class html
}
else
{
if (isset($data['lable']))
{
$k = $data['lable'];
unset($data['lable']);
}
$out .= '<optgroup label="'.$this->htmlspecialchars($no_lang || $k == '' ? $k : lang($k))."\">\n";
foreach($data as $k => $label)
@ -1074,40 +1079,40 @@ class html
* @param string $_selected path of selected folder
* @param mixed $_topFolder=false node of topFolder or false for none
* @param string $_onNodeSelect='alert' js function to call if node gets selected
* @param string $_divId='foldertree' id of the div
* @param string $_tree='foldertree' id of the div and name of the variable containing the tree object
* @param string $_divClass='' css class of the div
* @param string $_leafImage='' default image of a leaf-node, ''=default of foldertree, set it eg. 'folderClosed.gif' to show leafs as folders
* @param boolean/string $_onCheckHandler=false string with handler-name to display a checkbox for each folder, or false (default)
* @param boolean/string $_onCheckHandler=false string with handler-name to display a checkbox for each folder, or false (default), 'null' switches checkboxes on without an handler!
* @param string $delimiter='/' path-delimiter, default /
* @param mixed $folderImageDir=null string path to the tree menu images, null uses default path
*
* @return string the html code, to be added into the template
*/
function tree($_folders,$_selected,$_topFolder=false,$_onNodeSelect="null",$_divId='foldertree',$_divClass='',$_leafImage='',$_onCheckHandler=false,$delimiter='/',$folderImageDir=null)
function tree($_folders,$_selected,$_topFolder=false,$_onNodeSelect="null",$tree='foldertree',$_divClass='',$_leafImage='',$_onCheckHandler=false,$delimiter='/',$folderImageDir=null)
{
if(is_null($folderImageDir))
{
$folderImageDir = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/templates/default/images/';
}
if(is_null($folderImageDir))
{
$folderImageDir = $GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/templates/default/images/';
}
$html = $this->div("\n",'id="'.$_divId.'"',$_divClass);
$html = $this->div("\n",'id="'.$tree.'"',$_divClass);
static $tree_initialised=false;
if (!$tree_initialised)
{
$html .= '<link rel="STYLESHEET" type="text/css" href="'.$GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/js/dhtmlxtree/css/dhtmlXTree.css">'."\n";
$html .= '<link rel="STYLESHEET" type="text/css" href="'.$GLOBALS['egw_info']['server']['webserver_url'].'/phpgwapi/js/dhtmlxtree/css/dhtmlXTree.css" />'."\n";
$html .= "<script type='text/javascript' src='{$GLOBALS['egw_info']['server']['webserver_url']}/phpgwapi/js/dhtmlxtree/js/dhtmlXCommon.js'></script>\n";
$html .= "<script type='text/javascript' src='{$GLOBALS['egw_info']['server']['webserver_url']}/phpgwapi/js/dhtmlxtree/js/dhtmlXTree.js'></script>\n";
$tree_initialised = true;
}
$html .= "<script type='text/javascript'>\n";
$html .= "tree=new dhtmlXTreeObject('$_divId','100%','100%',0);\n";
$html .= "tree.setImagePath('$folderImageDir/dhtmlxtree/');\n";
$html .= "$tree=new dhtmlXTreeObject('$tree','100%','100%',0);\n";
$html .= "$tree.setImagePath('$folderImageDir/dhtmlxtree/');\n";
if($_onCheckHandler)
{
$html .= "tree.enableCheckBoxes(1);\n";
$html .= "tree.setOnCheckHandler('$_onCheckHandler');\n";
$html .= "$tree.enableCheckBoxes(1);\n";
$html .= "$tree.setOnCheckHandler('$_onCheckHandler');\n";
}
$top = 0;
@ -1127,11 +1132,11 @@ class html
{
$label = $_topFolder;
}
$html .= "\ntree.insertNewItem(0,'$top','".addslashes($label)."',$_onNodeSelect,'$topImage','$topImage','$topImage','CHILD,TOP');\n";
$html .= "\n$tree.insertNewItem(0,'$top','".addslashes($label)."',$_onNodeSelect,'$topImage','$topImage','$topImage','CHILD,TOP');\n";
if (is_array($_topFolder) && isset($_topFolder['title']))
{
$html .= "tree.setItemText('$top','".addslashes($label)."','".addslashes($_topFolder['title'])."');\n";
$html .= "$tree.setItemText('$top','".addslashes($label)."','".addslashes($_topFolder['title'])."');\n";
}
}
// evtl. remove leading delimiter
@ -1159,25 +1164,37 @@ class html
$parentName = implode((array)$folderParts,$delimiter);
if(empty($parentName)) $parentName = $top;
$entryOptions = 'CHILD,CHECKED';
// highlight currently item
if ($_selected === $path)
$entryOptions = 'CHILD';
if ($_onCheckHandler && $_selected) // check selected items on multi selection
{
if (!is_array($_selected)) $_selected = explode(',',$_selected);
if (in_array($path,$_selected,!is_numeric($path))) $entryOptions .= ',CHECKED';
//echo "<p>path=$path, _selected=".print_r($_selected,true).": $entryOptions</p>\n";
}
// highlight current item
elseif ((string)$_selected === (string)$path)
{
$entryOptions .= ',SELECT';
}
$html .= "tree.insertNewItem('".addslashes($parentName)."','".addslashes($path)."','".addslashes($label).
$html .= "$tree.insertNewItem('".addslashes($parentName)."','".addslashes($path)."','".addslashes($label).
"',$_onNodeSelect,$image1,$image2,$image3,'$entryOptions');\n";
if (isset($data['title']))
{
$html .= "tree.setItemText('".addslashes($path)."','".addslashes($label)."','".addslashes($data['title'])."');\n";
}
if($_displayCheckBox)
{
$html .= "tree.setCheck('".addslashes($path)."','".(int)$data['checked']."');";
$html .= "$tree.setItemText('".addslashes($path)."','".addslashes($label)."','".addslashes($data['title'])."');\n";
}
}
$html .= "tree.closeAllItems(0);\n";
$html .= "tree.openItem('".($_selected ? addslashes($_selected) : $top)."');\n";
$html .= "$tree.closeAllItems(0);\n";
if ($_selected)
{
foreach(is_array($_selected)?$_selected:array($_selected) as $path)
{
$html .= "$tree.openItem('".addslashes($path)."');\n";
}
}
else
{
$html .= "$tree.openItem('$top');\n";
}
$html .= "</script>\n";
return $html;