From 30ed6a8ab76d246b03e84a9b1aa6c989b638869d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 19 Aug 2011 08:22:19 +0000 Subject: [PATCH] - added legacy-options parsing and defintions for currently implemented widgets - added more server-side debuging: * etemplate_widget_template class can be called via url and displays parsed xml, eg: http://localhost/egroupware/etemplate/inc/class.etemplate_widget_template.inc.php?name=infolog.edit This is different from directly calling the xet file, as it show what got parsed and how: http://localhost/egroupware/infolog/templates/default/edit.xet * etemplate_new class can be called via url to render an arbitrary template, eg: http://localhost/egroupware/etemplate/inc/class.etemplate_new.inc.php?name=timesheet.edit Thought not all are already rendered correct, eg. infolog.edit is not: http://localhost/egroupware/etemplate/inc/class.etemplate_new.inc.php?name=infolog.edit --- etemplate/inc/class.etemplate.inc.php | 2 +- etemplate/inc/class.etemplate_new.inc.php | 42 ++++++- etemplate/inc/class.etemplate_widget.inc.php | 108 +++++++++++++++++- .../class.etemplate_widget_template.inc.php | 12 +- .../class.etemplate_widget_textbox.inc.php | 43 +++++-- etemplate/inc/class.xul_io.inc.php | 11 +- etemplate/setup/etemplates.inc.php | 4 +- etemplate/templates/default/editor.widget.xet | 96 ++++++++-------- 8 files changed, 249 insertions(+), 69 deletions(-) diff --git a/etemplate/inc/class.etemplate.inc.php b/etemplate/inc/class.etemplate.inc.php index 15296ad9bc..9d96c541c6 100644 --- a/etemplate/inc/class.etemplate.inc.php +++ b/etemplate/inc/class.etemplate.inc.php @@ -1698,7 +1698,7 @@ class etemplate extends boetemplate case 'vbox': case 'hbox': case 'groupbox': - case 'box': + case 'box': // size: num,orient,cellpadding,cellspacing,keep $rows = array(); $box_row = 1; $box_col = 'A'; diff --git a/etemplate/inc/class.etemplate_new.inc.php b/etemplate/inc/class.etemplate_new.inc.php index 119ae861b1..e148d2d9d4 100644 --- a/etemplate/inc/class.etemplate_new.inc.php +++ b/etemplate/inc/class.etemplate_new.inc.php @@ -11,6 +11,19 @@ * @version $Id$ */ +// allow to call direct for tests (see end of class) +if (!isset($GLOBALS['egw_info'])) +{ + $GLOBALS['egw_info'] = array( + 'flags' => array( + 'currentapp' => 'login', + 'nonavbar' => true, + 'debug' => 'etemplate_new', + ) + ); + include_once '../../header.inc.php'; +} + /** * New eTemplate serverside contains: * - main server methods like read, exec @@ -198,8 +211,9 @@ class etemplate_new extends etemplate_widget_template { $this->rel_path = self::relPath($this->name=$name, $this->template_set=$template_set, $this->version=$version, $this->laod_via = $load_via); + error_log(__METHOD__."('$name', '$template_set', '$lang', $group, '$version', '$load_via') rel_path=".array2string($this->rel_path)); - return (boolean)$this->real_path; + return (boolean)$this->rel_path; } /** @@ -390,4 +404,30 @@ class etemplate_new extends etemplate_widget_template } return $old; } + + /** + * Debug callback just outputting content + * + * @param array $content=null + */ + public function debug(array $content=null) + { + common::egw_header(); + _debug_array($content); + common::egw_footer(); + } } + +if ($GLOBALS['egw_info']['flags']['debug'] == 'etemplate_new') +{ + $name = isset($_GET['name']) ? $_GET['name'] : 'timesheet.edit'; + $template = new etemplate_new(); + if (!$template->read($name)) + { + header('HTTP-Status: 404 Not Found'); + echo "Not Found

Not Found

The requested eTemplate '$name' was not found!

\n"; + exit; + } + $GLOBALS['egw_info']['flags']['app_header'] = $name; + $template->exec('etemplate.etemplate.debug', array(), array(), array(), array(), 2); +} \ No newline at end of file diff --git a/etemplate/inc/class.etemplate_widget.inc.php b/etemplate/inc/class.etemplate_widget.inc.php index 6bcf903de1..2d7f643e09 100644 --- a/etemplate/inc/class.etemplate_widget.inc.php +++ b/etemplate/inc/class.etemplate_widget.inc.php @@ -53,6 +53,13 @@ class etemplate_widget */ protected $children = array(); + /** + * (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs + * + * @var string|array + */ + protected $legacy_options; + /** * Request object of the currently created request * @@ -132,6 +139,17 @@ class etemplate_widget { $reader = self::get_reader($xml); + // check if we have to split legacy options (can be by type) + $legacy_options = $this->legacy_options; + if (is_array($legacy_options)) + { + if (!($type = $reader->getAttribute('type'))) + { + $type = $this->type; + } + $legacy_options = $legacy_options[$type]; + } + // read and set all attributes $template = $this; while($reader->moveToNextAttribute()) @@ -140,11 +158,66 @@ class etemplate_widget { if (!$cloned) $template = clone($this); $template->attrs[$reader->name] = $reader->value; + + // split legacy options + if ($legacy_options && $reader->name == 'options') + { + $legacy_options = explode(',', $legacy_options); + foreach(self::csv_split($reader->value, count($legacy_options)) as $n => $val) + { + if ($legacy_options[$n] && (string)$val !== '') $template->attrs[$legacy_options[$n]] = $val; + } + } } } return $template; } + /** + * Split a $delimiter-separated options string, which can contain parts with delimiters enclosed in $enclosure + * + * Examples: + * - csv_split('"1,2,3",2,3') === array('1,2,3','2','3') + * - csv_split('1,2,3',2) === array('1','2,3') + * - csv_split('"1,2,3",2,3',2) === array('1,2,3','2,3') + * - csv_split('"a""b,c",d') === array('a"b,c','d') // to escape enclosures double them! + * + * @param string $str + * @param int $num=null in how many parts to split maximal, parts over this number end up (unseparated) in the last part + * @param string $delimiter=',' + * @param string $enclosure='"' + * @return array + */ + public static function csv_split($str,$num=null,$delimiter=',',$enclosure='"') + { + if (strpos($str,$enclosure) === false) + { + return is_null($num) ? explode($delimiter,$str) : explode($delimiter,$str,$num); // no need to run this more expensive code + } + $parts = explode($delimiter,$str); + for($n = 0; isset($parts[$n]); ++$n) + { + $part =& $parts[$n]; + if ($part[0] === $enclosure) + { + while (isset($parts[$n+1]) && substr($part,-1) !== $enclosure) + { + $part .= $delimiter.$parts[++$n]; + unset($parts[$n]); + } + $part = substr(str_replace($enclosure.$enclosure,$enclosure,$part),1,-1); + } + } + $parts = array_values($parts); // renumber the parts (in case we had to concat them) + + if ($num > 0 && count($parts) > $num) + { + $parts[$num-1] = implode($delimiter,array_slice($parts,$num-1,count($parts)-$num+1)); + $parts = array_slice($parts,0,$num); + } + return $parts; + } + /** * Registry of classes implementing widgets * @@ -249,7 +322,7 @@ class etemplate_widget */ public function __toString() { - return $this->type.'#'.$this->id; + return $this->type.($this->attrs['type'] && $this->attrs['type'] != $this->type ? '('.$this->attrs['type'].')' : '').'#'.$this->id; } /** @@ -264,6 +337,11 @@ class etemplate_widget if ($this->id) echo ' id="'.htmlspecialchars($this->id).'"'; foreach($this->attrs as $name => $value) { + if ($name == 'options' && $this->legacy_options && (!is_array($this->legacy_options) || + isset($this->legacy_options[$this->attrs['type'] ? $this->attrs['type'] : $this->type]))) + { + continue; // do NOT output already converted legacy options + } echo ' '.$name.'="'.htmlspecialchars($value).'"'; } echo ' php-class="'.get_class($this).'"'; @@ -431,6 +509,19 @@ class etemplate_widget */ class etemplate_widget_named extends etemplate_widget { + /** + * (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs + * + * @var string|array + */ + protected $legacy_options = array( + 'box' => ',cellpadding,cellspacing,keep', + 'hbox' => 'cellpadding,cellspacing,keep', + 'vbox' => 'cellpadding,cellspacing,keep', + 'groupbox' => 'cellpadding,cellspacing,keep', + 'grid' => null, // not used + ); + /** * Validate input * @@ -450,3 +541,18 @@ class etemplate_widget_named extends etemplate_widget } // register class for layout widgets, which can have an own namespace etemplate_widget::registerWidget('etemplate_widget_named', array('grid', 'box', 'hbox', 'vbox', 'groupbox')); + +/** + * Describtion widget + * + * Reimplemented to set legacy options + */ +class etemplate_widget_description extends etemplate_widget +{ + /** + * (Array of) comma-separated list of legacy options to automatically replace when parsing with set_attrs + * + * @var string|array + */ + protected $legacy_options = 'bold-italic,link,activate_links,label_for,link_target,link_popup_size,link_title'; +} diff --git a/etemplate/inc/class.etemplate_widget_template.inc.php b/etemplate/inc/class.etemplate_widget_template.inc.php index 682683f165..f5462893b7 100644 --- a/etemplate/inc/class.etemplate_widget_template.inc.php +++ b/etemplate/inc/class.etemplate_widget_template.inc.php @@ -17,6 +17,7 @@ if (!isset($GLOBALS['egw_info'])) $GLOBALS['egw_info'] = array( 'flags' => array( 'currentapp' => 'login', + 'debug' => 'etemplate_widget_template', ) ); include_once '../../header.inc.php'; @@ -114,6 +115,7 @@ class etemplate_widget_template extends etemplate_widget * @param array &$validated=array() validated content * @param string $cname='' current namespace * @return boolean true if no validation error, false otherwise + * @todo handle template references containing content in id, eg. id="edit.$cont[something]" */ public function validate(array $content, &$validated=array(), $cname = '') { @@ -123,9 +125,15 @@ class etemplate_widget_template extends etemplate_widget } } -if ($GLOBALS['egw_info']['flags']['currentapp'] == 'login') +if ($GLOBALS['egw_info']['flags']['debug'] == 'etemplate_widget_template') { - $template = etemplate_widget_template::instance('timesheet.edit'); + $name = isset($_GET['name']) ? $_GET['name'] : 'timesheet.edit'; + if (!($template = etemplate_widget_template::instance($name))) + { + header('HTTP-Status: 404 Not Found'); + echo "Not Found

Not Found

The requested eTemplate '$name' was not found!

\n"; + exit; + } header('Content-Type: text/xml'); echo $template->toXml(); } \ No newline at end of file diff --git a/etemplate/inc/class.etemplate_widget_textbox.inc.php b/etemplate/inc/class.etemplate_widget_textbox.inc.php index dc60f1798a..15ed51607e 100644 --- a/etemplate/inc/class.etemplate_widget_textbox.inc.php +++ b/etemplate/inc/class.etemplate_widget_textbox.inc.php @@ -13,8 +13,8 @@ /** * eTemplate textbox widget with following sub-types: - * - textbox optional multiline="true" and rows="123" - * - int + * - textbox with optional multiline="true" and rows="123" + * - integer or int * - float * - hidden * - colorpicker @@ -22,6 +22,26 @@ */ class etemplate_widget_textbox extends etemplate_widget { + /** + * Constructor + * + * @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct + * @throws egw_exception_wrong_parameter + */ + public function __construct($xml) + { + parent::__construct($xml); + + // normalize types + if ($this->type !== 'textbox') + { + if ($this->type == 'int') $this->type = 'integer'; + + $this->attrs['type'] = $this->type; + $this->type = 'textbox'; + } + } + /** * Validate input * @@ -40,14 +60,13 @@ class etemplate_widget_textbox extends etemplate_widget public function validate(array $content, &$validated=array(), $cname = '') { $ok = true; - $type = isset($this->attrs['type']) ? $this->attrs['type'] : $this->type; if (!$this->is_readonly($cname)) { if (!isset($this->attrs['preg'])) { - switch($type) + switch($this->type) { - case 'int': + case 'integer': $this->attrs['preg'] = '/^-?[0-9]*$/'; break; case 'float': @@ -74,9 +93,9 @@ class etemplate_widget_textbox extends etemplate_widget } if ($this->attrs['preg'] && !preg_match($this->attrs['preg'],$value)) { - switch($type) + switch($this->type) { - case 'int': + case 'integer': self::set_validation_error($form_name,lang("'%1' is not a valid integer !!!",$value),''); break; case 'float': @@ -88,22 +107,22 @@ class etemplate_widget_textbox extends etemplate_widget } $ok = false; } - elseif ($type == 'int' || $type == 'float') // cast int and float and check range + elseif ($this->type == 'integer' || $this->type == 'float') // cast int and float and check range { if ((string)$value !== '' || $this->attrs['needed']) // empty values are Ok if needed is not set { - $value = $type == 'int' ? (int) $value : (float) str_replace(',','.',$value); // allow for german (and maybe other) format + $value = $this->type == 'integer' ? (int) $value : (float) str_replace(',','.',$value); // allow for german (and maybe other) format if (!empty($this->attrs['min']) && $value < $this->attrs['min']) { self::set_validation_error($form_name,lang("Value has to be at least '%1' !!!",$this->attrs['min']),''); - $value = $type == 'int' ? (int) $this->attrs['min'] : (float) $this->attrs['min']; + $value = $this->type == 'integer' ? (int) $this->attrs['min'] : (float) $this->attrs['min']; $ok = false; } if (!empty($this->attrs['max']) && $value > $this->attrs['max']) { self::set_validation_error($form_name,lang("Value has to be at maximum '%1' !!!",$this->attrs['max']),''); - $value = $type == 'int' ? (int) $this->attrs['max'] : (float) $this->attrs['max']; + $value = $this->type == 'integer' ? (int) $this->attrs['max'] : (float) $this->attrs['max']; $ok = false; } } @@ -113,4 +132,4 @@ class etemplate_widget_textbox extends etemplate_widget return parent::validate($content, $validated, $cname) && $ok; } } -etemplate_widget::registerWidget('etemplate_widget_textbox', array('textbox','int','float','passwd','hidden','colorpicker')); \ No newline at end of file +etemplate_widget::registerWidget('etemplate_widget_textbox', array('textbox','int','integer','float','passwd','hidden','colorpicker')); \ No newline at end of file diff --git a/etemplate/inc/class.xul_io.inc.php b/etemplate/inc/class.xul_io.inc.php index ad064012f7..bdf527a816 100644 --- a/etemplate/inc/class.xul_io.inc.php +++ b/etemplate/inc/class.xul_io.inc.php @@ -51,7 +51,7 @@ class xul_io var $widget2xul = array( 'label' => array( '.name' => 'description', - 'label' => 'value' + 'label' => 'value', ), 'text' => array( '.name' => 'textbox', @@ -65,12 +65,17 @@ class xul_io 'integer' => array( '.name' => 'textbox', '.set' => 'type=integer', - 'size' => 'min,max,size' + 'size' => 'min,max,size,precision,step' + ), + 'int' => array( + '.name' => 'textbox', + '.set' => 'type=integer', + 'size' => 'min,max,size,precision,step' ), 'float' => array( '.name' => 'textbox', '.set' => 'type=float', - 'size' => 'min,max,size,precision' + 'size' => 'min,max,size,precision,step' ), 'select' => array( '.name' => 'menulist,menupopup', diff --git a/etemplate/setup/etemplates.inc.php b/etemplate/setup/etemplates.inc.php index f6637d202a..0749a6565d 100644 --- a/etemplate/setup/etemplates.inc.php +++ b/etemplate/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * EGroupware - eTemplates for Application etemplate * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2011-07-12 11:52 + * generated by soetemplate::dump4setup() 2011-08-19 09:30 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate @@ -183,6 +183,8 @@ $templ_data[] = array('name' => 'etemplate.test.duration','template' => '','lang $templ_data[] = array('name' => 'etemplate.test.grid-export','template' => '','lang' => '','group' => '0','version' => '2','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:9:"some cell";}}i:2;a:1:{s:1:"A";a:6:{s:5:"class";s:9:"hboxClass";s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:3:{s:4:"size";s:1:"1";s:4:"type";s:5:"label";s:5:"label";s:2:"1.";}i:2;a:2:{s:4:"type";s:5:"label";s:5:"label";s:2:"2.";}i:3;a:3:{s:5:"align";s:5:"right";s:4:"type";s:5:"label";s:5:"label";s:10:"last right";}}}}s:4:"cols";i:1;s:4:"rows";i:2;s:4:"size";s:16:"100%,,,gridClass";}}','size' => '100%,,,gridClass','style' => '','modified' => '1179507244',); +$templ_data[] = array('name' => 'etemplate.test.html','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:8:"htmlarea";s:4:"name";s:4:"test";}}}s:4:"rows";i:1;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1313581424',); + $templ_data[] = array('name' => 'etemplate.test.infolog','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:13:"infolog-value";s:4:"name";s:4:"test";s:7:"options";a:0:{}}}}s:4:"rows";i:1;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1242560130',); $templ_data[] = array('name' => 'etemplate.test.required','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:6:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Text";}s:1:"B";a:3:{s:4:"type";s:4:"text";s:4:"name";s:4:"text";s:6:"needed";s:1:"1";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"File";}s:1:"B";a:3:{s:4:"type";s:4:"file";s:4:"name";s:4:"file";s:6:"needed";s:1:"1";}}i:3;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:8:"Checkbox";}s:1:"B";a:4:{s:4:"type";s:8:"checkbox";s:5:"label";s:22:"Gelesen und verstanden";s:4:"name";s:5:"check";s:6:"needed";s:1:"1";}}i:4;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Radios";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:5:"radio";s:4:"size";s:3:"one";s:5:"label";s:3:"One";s:4:"name";s:5:"radio";s:6:"needed";s:1:"1";}i:2;a:5:{s:4:"type";s:5:"radio";s:4:"name";s:5:"radio";s:4:"size";s:3:"two";s:5:"label";s:3:"Two";s:6:"needed";s:1:"1";}}}i:5;a:2:{s:1:"A";a:2:{s:4:"type";s:6:"button";s:5:"label";s:6:"Submit";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:5;s:4:"cols";i:2;}}','size' => '','style' => '','modified' => '1279699676',); diff --git a/etemplate/templates/default/editor.widget.xet b/etemplate/templates/default/editor.widget.xet index 1208edd546..9759c1d574 100644 --- a/etemplate/templates/default/editor.widget.xet +++ b/etemplate/templates/default/editor.widget.xet @@ -13,44 +13,44 @@ - + - + - - - - + + + + - - - - + + + + - + - - + + - - - + + + - - + + - - + + - - + + @@ -60,18 +60,18 @@ - + - + - + - + - + @@ -87,21 +87,21 @@ - + - + - + - + - + @@ -109,46 +109,46 @@ - - + + - + - - + + -