From 486a32e86db4ecd706e4e9e73ffb05fcea58c2c2 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 16 Mar 2009 12:58:24 +0000 Subject: [PATCH] Refractured eTemplate to use: - the etemplate_request object which stores the request data in the a) session (as before) or b) compressed and encrypted in the form transmitted to the user Benefit of b) is that the session does not grow and the form can be submitted as long as the session exists, as we need no garbadge collection. Of cause more data needs to be submitt between browser and webserver. b) is choosen automatic if mcrypt and gzcompress are available, but can be turned off via setting etemplate_request::$request_class = 'etemplate_request_session'; - static class variables instead of the before used global ones --> This new version of eTemplate is fully backward compatible with 1.6! --- .../inc/class.ajax_select_widget.inc.php | 582 +++++----- etemplate/inc/class.boetemplate.inc.php | 187 +-- etemplate/inc/class.date_widget.inc.php | 13 +- etemplate/inc/class.editor.inc.php | 4 +- etemplate/inc/class.etemplate.inc.php | 577 +++++----- etemplate/inc/class.etemplate_request.inc.php | 372 ++++-- .../class.etemplate_request_session.inc.php | 171 +++ etemplate/inc/class.link_widget.inc.php | 22 +- etemplate/inc/class.nextmatch_widget.inc.php | 16 +- etemplate/inc/class.select_widget.inc.php | 1013 ++++++++--------- etemplate/inc/class.soetemplate.inc.php | 42 +- etemplate/inc/class.tab_widget.inc.php | 34 +- etemplate/inc/class.uietemplate_gtk.inc.php | 2 +- etemplate/inc/class.xul_io.inc.php | 2 +- 14 files changed, 1609 insertions(+), 1428 deletions(-) create mode 100644 etemplate/inc/class.etemplate_request_session.inc.php diff --git a/etemplate/inc/class.ajax_select_widget.inc.php b/etemplate/inc/class.ajax_select_widget.inc.php index f8d0476953..f2d6403734 100644 --- a/etemplate/inc/class.ajax_select_widget.inc.php +++ b/etemplate/inc/class.ajax_select_widget.inc.php @@ -1,134 +1,208 @@ - * @version $Id$ - */ +/** + * eGroupWare eTemplate Extension - AJAX Select Widget + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package etemplate + * @subpackage extensions + * @link http://www.egroupware.org + * @author Nathan Gray + * @version $Id$ + */ - /** - * AJAX Select Widget - * - * Using AJAX, this widget allows a type-ahead find similar to a ComboBox, where as the user enters information, - * a drop-down box is populated with the n closest matches. If the user clicks on an item in the drop-down, that - * value is selected. - * n is the maximum number of results set in the user's preferences. - * The user is restricted to selecting values in the list. - * This widget can get data from any function that can provide data to a nextmatch widget. - * This widget is generating html, so it does not work (without an extra implementation) in an other UI - */ - class ajax_select_widget +/** + * AJAX Select Widget + * + * Using AJAX, this widget allows a type-ahead find similar to a ComboBox, where as the user enters information, + * a drop-down box is populated with the n closest matches. If the user clicks on an item in the drop-down, that + * value is selected. + * n is the maximum number of results set in the user's preferences. + * The user is restricted to selecting values in the list. + * This widget can get data from any function that can provide data to a nextmatch widget. + * This widget is generating html, so it does not work (without an extra implementation) in an other UI + */ +class ajax_select_widget +{ + var $public_functions = array( + 'pre_process' => True, + 'post_process' => True, + 'ajax_search' => True, + ); + var $human_name = 'AJAX Select'; // this is the name for the editor + + private $debug = false; + + function ajax_select_widget($ui='') { - var $public_functions = array( - 'pre_process' => True, - 'post_process' => True, - 'ajax_search' => True, - ); - var $human_name = 'AJAX Select'; // this is the name for the editor - private $debug = false; - - function ajax_select_widget($ui='') + switch($ui) { + case '': + case 'html': + $this->ui = 'html'; + break; + default: + echo "UI='$ui' not implemented"; + } + return 0; + } + + /** + * 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 object &$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) + { + if($this->debug) { + echo __METHOD__ . '
'; + printf("Name:%20s
", $name); + echo 'Value:'; + _debug_array($value); + echo 'Cell:'; + _debug_array($cell); + + echo 'Readonlys:'; + _debug_array($readonlys); + + echo 'Extension_data:'; + _debug_array($extension_data); - switch($ui) - { - case '': - case 'html': - $this->ui = 'html'; - break; - default: - echo "UI='$ui' not implemented"; - } - return 0; } - /** - * 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 object &$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) - { - if($this->debug) { - echo __METHOD__ . '
'; - printf("Name:%20s
", $name); - echo 'Value:'; - _debug_array($value); - echo 'Cell:'; - _debug_array($cell); + // Get Options + $options = array(); + if(!is_array($cell['size'])) { + list( + $options['get_rows'], + $options['get_title'], + $options['id_field'], + $options['template'], + $options['filter'], + $options['filter2'], + $options['link'], + $options['icon'] + ) = explode(',', $cell['size']); + } else { + $options = $cell['size']; + } - echo 'Readonlys:'; - _debug_array($readonlys); + if(is_array($value)) { + $options = array_merge($options, $value); + } - echo 'Extension_data:'; - _debug_array($extension_data); + if(!$options['template']) { + $options['template'] = 'etemplate.ajax_select_widget.row'; + } - } + $onchange = ($cell['onchange'] ? $cell['onchange'] : 'false'); - // Get Options - $options = array(); - if(!is_array($cell['size'])) { - list( - $options['get_rows'], - $options['get_title'], - $options['id_field'], - $options['template'], - $options['filter'], - $options['filter2'], - $options['link'], - $options['icon'] - ) = explode(',', $cell['size']); + // Set current value + if(!is_array($value)) { + $current_value = $value; + } elseif($value[$options['id_field']]) { + $current_value = $value[$options['id_field']]; + } + $extension_data['old_value'] = $value; + + list($title_app, $title_class, $title_method) = explode('.', $options['get_title']); + if($title_app && $title_class) { + if (is_object($GLOBALS[$title_class])) { // use existing instance (put there by a previous CreateObject) + $title_obj =& $GLOBALS[$title_class]; } else { - $options = $cell['size']; + $title_obj =& CreateObject($title_app . '.' . $title_class); + } + } + if(!is_object($title_obj) || !method_exists($title_obj,$title_method)) { + echo "$entry_app.$entry_class.$entry_method is not a valid method for getting the title"; + } elseif($current_value) { + $title = $title_obj->$title_method($current_value); + } + + // Check get_rows method + list($get_rows_app, $get_rows_class, $get_rows_method) = explode('.', $options['get_rows']); + if($get_rows_app && $get_rows_class) { + if (is_object($GLOBALS[$get_rows_class])) { // use existing instance (put there by a previous CreateObject) + $get_rows_obj =& $GLOBALS[$get_rows_class]; + } else { + $get_rows_obj =& CreateObject($get_rows_app . '.' . $get_rows_class); } - if(is_array($value)) { - $options = array_merge($options, $value); + if(!is_object($get_rows_obj) || !method_exists($get_rows_obj, $get_rows_method)) { + echo "$get_rows_app.$get_rows_class.$get_rows_method is not a valid method for getting the rows"; } + } - if(!$options['template']) { - $options['template'] = 'etemplate.ajax_select_widget.row'; + + // Set up widget + $cell['type'] = 'template'; + $cell['size'] = $cell['name']; + $value = array('value' => $current_value, 'search' => $title); + $widget =& new etemplate('etemplate.ajax_select_widget'); + $widget->no_onclick = True; + + // Link if readonly & link is set + $search =& $widget->get_widget_by_name('search'); + if(($cell['readonly'] || $readonlys['search']) && $options['link']) { + $cell['readonly'] = false; + if(!is_array($readonlys)) { + $readonlys = array('search' => true); + } else { + $readonlys['search'] = true; } + $search['type'] = 'label'; + $search['no_lang'] = 1; + $search['size'] = ',' . $options['link']; + $extension_data['readonly'] = true; + } else { + $search['type'] = 'text'; + $search['size'] = ''; + } - $onchange = ($cell['onchange'] ? $cell['onchange'] : 'false'); + // Icon + $icon =& $widget->get_widget_by_path('/0/1A'); + $icon['name'] = $options['icon']; - // Set current value - if(!is_array($value)) { - $current_value = $value; - } elseif($value[$options['id_field']]) { - $current_value = $value[$options['id_field']]; - } - $extension_data['old_value'] = $value; + $cell['obj'] = &$widget; - list($title_app, $title_class, $title_method) = explode('.', $options['get_title']); - if($title_app && $title_class) { - if (is_object($GLOBALS[$title_class])) { // use existing instance (put there by a previous CreateObject) - $title_obj =& $GLOBALS[$title_class]; - } else { - $title_obj =& CreateObject($title_app . '.' . $title_class); - } - } - if(!is_object($title_obj) || !method_exists($title_obj,$title_method)) { - echo "$entry_app.$entry_class.$entry_method is not a valid method for getting the title"; - } elseif($current_value) { - $title = $title_obj->$title_method($current_value); - } + // Save options for post_processing + $extension_data['options'] = $options; + $extension_data['needed'] = $cell['needed']; - // Check get_rows method - list($get_rows_app, $get_rows_class, $get_rows_method) = explode('.', $options['get_rows']); + // xajax + $GLOBALS['egw_info']['flags']['include_xajax'] = True; + + // JavaScript + $options = $GLOBALS['egw']->js->convert_phparray_jsarray("options['$name']", $options, true); + $GLOBALS['egw']->js->set_onload("if(!options) { + var options = new Object(); + }\n + $options;\n + ajax_select_widget_setup('$name', '$onchange', options['$name'], '" . $GLOBALS['egw_info']['flags']['currentapp'] . "'); + "); + $GLOBALS['egw']->js->validate_file('', 'ajax_select', 'etemplate'); + + return True; // no extra label + } + + function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in) + { + //echo "

ajax_select_widget.post_process: $name = "; _debug_array($value_in);_debug_array($extension_data); + if(!is_array($value_in)) { + $value_in = $extension_data['old_value']; + } + + // They typed something in, but didn't choose a result + if(!$value_in['value'] && $value_in['search']) { + list($get_rows_app, $get_rows_class, $get_rows_method) = explode('.', $extension_data['options']['get_rows']); if($get_rows_app && $get_rows_class) { if (is_object($GLOBALS[$get_rows_class])) { // use existing instance (put there by a previous CreateObject) $get_rows_obj =& $GLOBALS[$get_rows_class]; @@ -138,198 +212,120 @@ if(!is_object($get_rows_obj) || !method_exists($get_rows_obj, $get_rows_method)) { echo "$get_rows_app.$get_rows_class.$get_rows_method is not a valid method for getting the rows"; - } - } - - - // Set up widget - $cell['type'] = 'template'; - $cell['size'] = $cell['name']; - $value = array('value' => $current_value, 'search' => $title); - $widget =& new etemplate('etemplate.ajax_select_widget'); - $widget->no_onclick = True; - - // Link if readonly & link is set - $search =& $widget->get_widget_by_name('search'); - if(($cell['readonly'] || $readonlys['search']) && $options['link']) { - $cell['readonly'] = false; - if(!is_array($readonlys)) { - $readonlys = array('search' => true); } else { - $readonlys['search'] = true; - } - $search['type'] = 'label'; - $search['no_lang'] = 1; - $search['size'] = ',' . $options['link']; - $extension_data['readonly'] = true; - } else { - $search['type'] = 'text'; - $search['size'] = ''; - } + $query = array_merge($extension_data['options'], $value_in); + $count = $get_rows_obj->$get_rows_method($query, $results); - // Icon - $icon =& $widget->get_widget_by_path('/0/1A'); - $icon['name'] = $options['icon']; - - $cell['obj'] = &$widget; - - // Save options for post_processing - $extension_data['options'] = $options; - $extension_data['needed'] = $cell['needed']; - - // xajax - $GLOBALS['egw_info']['flags']['include_xajax'] = True; - - // JavaScript - $options = $GLOBALS['egw']->js->convert_phparray_jsarray("options['$name']", $options, true); - $GLOBALS['egw']->js->set_onload("if(!options) { - var options = new Object(); - }\n - $options;\n - ajax_select_widget_setup('$name', '$onchange', options['$name'], '" . $GLOBALS['egw_info']['flags']['currentapp'] . "'); - "); - $GLOBALS['egw']->js->validate_file('', 'ajax_select', 'etemplate'); - - return True; // no extra label - } - - function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in) - { - //echo "

ajax_select_widget.post_process: $name = "; _debug_array($value_in);_debug_array($extension_data); - if(!is_array($value_in)) { - $value_in = $extension_data['old_value']; - } - - // They typed something in, but didn't choose a result - if(!$value_in['value'] && $value_in['search']) { - list($get_rows_app, $get_rows_class, $get_rows_method) = explode('.', $extension_data['options']['get_rows']); - if($get_rows_app && $get_rows_class) { - if (is_object($GLOBALS[$get_rows_class])) { // use existing instance (put there by a previous CreateObject) - $get_rows_obj =& $GLOBALS[$get_rows_class]; + if($count == 1) { + $value = $results[0][$extension_data['options']['id_field']]; + return true; + } elseif ($count > 1) { + etemplate::set_validation_error($name,lang("More than 1 match for '%1'",$value_in['search'])); + $loop = true; + return false; } else { - $get_rows_obj =& CreateObject($get_rows_app . '.' . $get_rows_class); - } - - if(!is_object($get_rows_obj) || !method_exists($get_rows_obj, $get_rows_method)) { - echo "$get_rows_app.$get_rows_class.$get_rows_method is not a valid method for getting the rows"; - } else { - $query = array_merge($extension_data['options'], $value_in); - $count = $get_rows_obj->$get_rows_method($query, $results); - - if($count == 1) { - $value = $results[0][$extension_data['options']['id_field']]; - return true; - } elseif ($count > 1) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = lang("More than 1 match for '%1'",$value_in['search']); - $loop = true; - return false; - } else { - $value = $value_in['search']; - return true; - } + $value = $value_in['search']; + return true; } } - } elseif ($extension_data['readonly']) { + } + } elseif ($extension_data['readonly']) { + $value = $extension_data['old_value']; + return true; + } elseif ($value_in['search'] == '') { + // They're trying to clear the form + $value = null; + + // True if not needed, false if needed and they gave no value + $return = !($extension_data['needed'] && trim($value_in['value']) == ''); + + if(!$return) { $value = $extension_data['old_value']; - return true; - } elseif ($value_in['search'] == '') { - // They're trying to clear the form - $value = null; - - // True if not needed, false if needed and they gave no value - $return = !($extension_data['needed'] && trim($value_in['value']) == ''); - - if(!$return) { - $value = $extension_data['old_value']; - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = lang('Required'); - } - - // Loop if some other widget wants to loop, or if this is required and they gave no value - $loop = $GLOBALS['egw_info']['etemplate']['loop'] || !$return; - - if($this->debug && $loop) { - echo 'Looping...
Returning ' . $return . '
'; - } - return $return; - } else { - $value = $value_in['value']; - $loop = $GLOBALS['egw_info']['etemplate']['loop'] || false; - return true; - } - } - - function ajax_search($id, $value, $set_id, $query) { - $base_id = substr($id, 0, strrpos($id, '[')); - $result_id = ($set_id ? $set_id : $base_id . '[results]'); - $response = new xajaxResponse(); - if($query['get_rows']) { - list($app, $class, $method) = explode('.', $query['get_rows']); - $this->bo = CreateObject($app . '.' . $class); - unset($query['get_rows']); - } else { - return $response->getXML(); + etemplate::set_validation_error($name,lang('Required')); + $loop = true; } - // Expand lists - foreach($query as $key => &$row) { - if(strpos($row, ',')) { - $query[$key] = explode(',', $row); - } - - // sometimes it sends 'null' (not null) - if($row == 'null') { - unset($query[$key]); - } elseif (is_string($row) && strtolower($row) == 'false') { - $row = false; - } + if($this->debug && $loop) { + echo 'Looping...
Returning ' . $return . '
'; } - $query['search'] = $value; - - $result_list = array(); - $readonlys = array(); - if(is_object($this->bo)) { - $count = $this->bo->$method($query, $result_list, $readonlys); - } - if(is_array($count)) { - $count = count($result_list); - } - - $response->addScript("remove_ajax_results('$result_id')"); - if($count > 0) { - $response->addScript("add_ajax_result('$result_id', '', '', '" . lang('Select') ."');"); - $count = 0; - - if(!$query['template'] || $query['template'] == 'etemplate.ajax_select_widget.row') { - $query['template'] = 'etemplate.ajax_select_widget.row'; - } - foreach($result_list as $key => &$row) { - if(!is_array($row)) { - continue; - } - if($query['id_field'] && $query['get_title']) { - if($row[$query['id_field']]) { - $row['title'] = ExecMethod($query['get_title'], $row[$query['id_field']]); - } - } - - $data = ($query['nextmatch_template']) ? array(1=>$row) : $row; - $widget =& CreateObject('etemplate.etemplate', $query['template']); - $html = addslashes(str_replace("\n", '', $widget->show($data, '', $readonlys))); - - // If we use htmlspecialchars, it causes issues with mixed quotes. addslashes() seems to handle it. - $row['id_field'] = addslashes($row[$query['id_field']]); - $row['title'] = addslashes($row['title']); - $response->addScript("add_ajax_result('$result_id', '${row['id_field']}', '" . $row['title'] . "', '$html');"); - $count++; - if($count > $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']) { - $response->addScript("add_ajax_result('$result_id', '', '', '" . lang("%1 more...", (count($result_list) - $count)) . "');"); - break; - } - } - } else { - $response->addScript("add_ajax_result('$result_id', '', '', '" . lang('No matches found') ."');"); - } - return $response->getXML(); + return $return; + } else { + $value = $value_in['value']; + return true; } } -?> + + function ajax_search($id, $value, $set_id, $query) { + $base_id = substr($id, 0, strrpos($id, '[')); + $result_id = ($set_id ? $set_id : $base_id . '[results]'); + $response = new xajaxResponse(); + if($query['get_rows']) { + list($app, $class, $method) = explode('.', $query['get_rows']); + $this->bo = CreateObject($app . '.' . $class); + unset($query['get_rows']); + } else { + return $response->getXML(); + } + + // Expand lists + foreach($query as $key => &$row) { + if(strpos($row, ',')) { + $query[$key] = explode(',', $row); + } + + // sometimes it sends 'null' (not null) + if($row == 'null') { + unset($query[$key]); + } elseif (is_string($row) && strtolower($row) == 'false') { + $row = false; + } + } + $query['search'] = $value; + + $result_list = array(); + $readonlys = array(); + if(is_object($this->bo)) { + $count = $this->bo->$method($query, $result_list, $readonlys); + } + if(is_array($count)) { + $count = count($result_list); + } + + $response->addScript("remove_ajax_results('$result_id')"); + if($count > 0) { + $response->addScript("add_ajax_result('$result_id', '', '', '" . lang('Select') ."');"); + $count = 0; + + if(!$query['template'] || $query['template'] == 'etemplate.ajax_select_widget.row') { + $query['template'] = 'etemplate.ajax_select_widget.row'; + } + foreach($result_list as $key => &$row) { + if(!is_array($row)) { + continue; + } + if($query['id_field'] && $query['get_title']) { + if($row[$query['id_field']]) { + $row['title'] = ExecMethod($query['get_title'], $row[$query['id_field']]); + } + } + + $data = ($query['nextmatch_template']) ? array(1=>$row) : $row; + $widget =& CreateObject('etemplate.etemplate', $query['template']); + $html = addslashes(str_replace("\n", '', $widget->show($data, '', $readonlys))); + + // If we use htmlspecialchars, it causes issues with mixed quotes. addslashes() seems to handle it. + $row['id_field'] = addslashes($row[$query['id_field']]); + $row['title'] = addslashes($row['title']); + $response->addScript("add_ajax_result('$result_id', '${row['id_field']}', '" . $row['title'] . "', '$html');"); + $count++; + if($count > $GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']) { + $response->addScript("add_ajax_result('$result_id', '', '', '" . lang("%1 more...", (count($result_list) - $count)) . "');"); + break; + } + } + } else { + $response->addScript("add_ajax_result('$result_id', '', '', '" . lang('No matches found') ."');"); + } + return $response->getXML(); + } +} diff --git a/etemplate/inc/class.boetemplate.inc.php b/etemplate/inc/class.boetemplate.inc.php index b342a2e4de..207256fca4 100644 --- a/etemplate/inc/class.boetemplate.inc.php +++ b/etemplate/inc/class.boetemplate.inc.php @@ -4,7 +4,7 @@ * * @link http://www.egroupware.org * @author Ralf Becker - * @copyright 2002-8 by RalfBecker@outdoor-training.de + * @copyright 2002-9 by RalfBecker@outdoor-training.de * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api @@ -12,13 +12,11 @@ */ /** - * Business Object for eTemplates, extending the Storage Object - * - * Not so much so far, as the most logic is still in the UI-class - */ + * Business Object for eTemplates, extending the Storage Object + */ class boetemplate extends soetemplate { - var $types = array( + static public $types = array( 'label' => 'Label', // Label $cell['label'] is (to be translated) textual content 'text' => 'Text', // Textfield 1 Line (size = [length][,maxlength]) 'int' => 'Integer', // like text, but only numbers (size = [min][,max]) @@ -45,7 +43,22 @@ class boetemplate extends soetemplate 'deck' => 'Deck', // a container of elements where only one is visible, size = # of elem. 'passwd' => 'Password' // a text of type password ); - private static $garbage_collection_done; + + /** + * Flag if form validation requires to loop + * + * @var boolean + */ + static public $loop = false; + + /** + * Request object of the currecntly created request + * + * It's a static variable as etemplates can contain further etemplates (rendered by a different object) + * + * @var etemplate_request + */ + static public $request; /** * constructor of class @@ -55,9 +68,9 @@ class boetemplate extends soetemplate * @param string/array $name name of etemplate or array with name and other keys * @param string/array $load_via name or array with keys of other etemplate to load in order to get $name */ - function boetemplate($name='',$load_via='') + function __construct($name='',$load_via='') { - $this->soetemplate(); + parent::__construct(); $tname = &$name; if (is_array($name)) @@ -223,107 +236,6 @@ class boetemplate extends soetemplate return $Ok; } - /** - * creates a new appsession-id via microtime() - * @return string - */ - protected static function appsession_id() - { - list($msec,$sec) = explode(' ',microtime()); - $time = 100 * $sec + (int)(100 * $msec); // gives precision of 1/100 sec - $id = $GLOBALS['egw_info']['flags']['currentapp'] .':'. $time; - //echo "

microtime()=".microtime().", sec=$sec, msec=$msec, id=$id

\n"; - return $id; - } - - /** - * saves content,readonlys,template-keys, ... via the appsession function - * - * As a user may open several windows with the same content/template wie generate a location-id from microtime - * which is used as location for appsession to descriminate between the different windows. This location-id - * is then saved as a hidden-var in the form. The above mentions session-id has nothing to do / is different - * from the session-id which is constant for all windows opened in one session. - * - * @param array $data the data to save - * @param string $id the id to use or '' to generate a new id - * @return string location-id - */ - protected static function save_appsession($data,$id='') - { - if (!$id) - { - $id = self::appsession_id(); - } - $GLOBALS['egw']->session->appsession($id,'etemplate',$data); - - if (substr($GLOBALS['egw_info']['server']['sessions_type'],0,4) == 'php4' && !self::$garbage_collection_done) - { - return self::php_session_garbage_collection(); - } - return $id; - } - - /** - * gets content,readonlys,template-keys, ... back from the appsession function - * - * @param string $id the location-id - * @return array with session-data - */ - protected function get_appsession($id) - { - $data = egw_session::appsession($id,'etemplate'); - //echo "boetemplate::get_appsession('$id')"; _debug_array($data); - - self::php_session_garbage_collection($id); - - return $data; - } - - /** - * a little bit of garbage collection for php4 sessions (their size is limited by memory_limit) - * - * With constant eTemplate use it can grow quite big and lead to unusable sessions (php terminates - * before any output with "Allowed memory size of ... exhausted"). - * We delete now sessions once used after 10min and sessions never or multiple used after 60min. - * - * @param string $id_used id of session just read by get_appsession to increment the usage counter - */ - static private function php_session_garbage_collection($id_used='') - { - // now we are on php4 sessions and do a bit of garbage collection - $app_sessions =& $_SESSION[egw_session::EGW_APPSESSION_VAR]['etemplate']; - $session_used =& $app_sessions['session_used']; - - if ($id_used) - { - //echo "session_used[$id_used]='".$session_used[$id_used]."'
\n"; - ++$session_used[$id_used]; // count the number of times a session got used - } - self::$garbage_collection_done = true; - - if (count($app_sessions) < 20) return $data; // we dont need to care - - list($msec,$sec) = explode(' ',microtime()); - $now = 100 * $sec + (int)(100 * $msec); // gives precision of 1/100 sec - - foreach(array_keys($app_sessions) as $id) - { - list($app,$time) = explode(':',$id); - - if (!$time) continue; // other data, no session - - //echo ++$n.') '.$id.': '.(($now-$time)/100.0)."secs old, used=".$session_used[$id].", size=".egw_vfs::hsize(strlen(serialize($app_sessions[$id])))."
\n"; - - if ($session_used[$id] == 1 && $time < $now - 10*6000 || // session used and older then 10min - $time < $now - 30*6000) // session not used and older then 30min - { - //echo "

boetemplate::php_session_garbage_collection('$id_used'): unsetting session '$id' (now=$now)

\n"; - unset($app_sessions[$id]); - unset($session_used[$id]); - } - } - } - /** * gets an attribute in a named cell * @@ -453,6 +365,13 @@ class boetemplate extends soetemplate $this->set_column_attributes($c,0,!$enable,$path); } + /** + * Cache for extension objects + * + * @var array + */ + static private $extensions = array(); + /** * trys to load the Extension / Widget-class from the app or etemplate * @@ -482,12 +401,12 @@ class boetemplate extends soetemplate if (!file_exists(EGW_SERVER_ROOT."/$app/inc/class.$class.inc.php")) { //echo "

boetemplate::loadExtension($type) extension not found

\n"; - return $GLOBALS['egw_info']['etemplate']['extension'][$type] = False; + return self::$extensions[$type] = False; } - $GLOBALS['egw_info']['etemplate']['extension'][$type] =& CreateObject($app.'.'.$class,$ui='html'); + self::$extensions[$type] =& CreateObject($app.'.'.$class,$ui='html'); //echo "

boetemplate::loadExtension($type) extension found in App. $app

\n"; - return $GLOBALS['egw_info']['etemplate']['extension'][$type]->human_name; + return self::$extensions[$type]->human_name; } /** @@ -500,8 +419,8 @@ class boetemplate extends soetemplate */ protected function haveExtension($type,$function='') { - return ($GLOBALS['egw_info']['etemplate']['extension'][$type] || $this->loadExtension($type,$ui)) && - ($function == '' || $GLOBALS['egw_info']['etemplate']['extension'][$type]->public_functions[$function]); + return (self::$extensions[$type] || $this->loadExtension($type,$ui)) && + ($function == '' || self::$extensions[$type]->public_functions[$function]); } /** @@ -522,11 +441,11 @@ class boetemplate extends soetemplate } // only supply extension data for non-readonly widgets or if it's already set // otherwise lists store >10k unnecessary data in each etemplate-session - if (!($cell['readonly'] || $readonlys && !is_array($readonlys)) || isset($GLOBALS['egw_info']['etemplate']['extension_data'][$name])) + if (!($cell['readonly'] || $readonlys && !is_array($readonlys)) || isset(self::$request->extension_data[$name])) { - $extension_data =& $GLOBALS['egw_info']['etemplate']['extension_data'][$name]; + $extension_data =& self::$request->extension_data[$name]; } - return $GLOBALS['egw_info']['etemplate']['extension'][$type]->pre_process($name,$value,$cell,$readonlys,$extension_data,$this); + return self::$extensions[$type]->pre_process($name,$value,$cell,$readonlys,$extension_data,$this); } /** @@ -544,9 +463,9 @@ class boetemplate extends soetemplate { return True; } - return $GLOBALS['egw_info']['etemplate']['extension'][$type]->post_process($name,$value, - $GLOBALS['egw_info']['etemplate']['extension_data'][$name], - $GLOBALS['egw_info']['etemplate']['loop'],$this,$value_in); + return self::$extensions[$type]->post_process($name,$value, + self::$request->extension_data[$name], + self::$loop,$this,$value_in); } /** @@ -565,8 +484,8 @@ class boetemplate extends soetemplate { return False; } - return $GLOBALS['egw_info']['etemplate']['extension'][$type]->render($cell,$name,$value,$readonly, - $GLOBALS['egw_info']['etemplate']['extension_data'][$name],$this); + return self::$extensions[$type]->render($cell,$name,$value,$readonly, + self::$request->extension_data[$name],$this); } /** @@ -798,12 +717,19 @@ class boetemplate extends soetemplate } /** - * stores the etemplate in the cache in phpgw_info + * Cache for already read etemplates + * + * @var array + */ + private static $template_cache = array(); + + /** + * stores the etemplate in the cache in egw_info */ private function store_in_cache() { //echo "

store_in_cache('$this->name','$this->template','$this->lang','$this->version')

\n"; - $GLOBALS['egw_info']['etemplate']['cache'][$this->cache_name()] = $this->as_array(1); + self::$template_cache[$this->cache_name()] = $this->as_array(1); } /** @@ -812,7 +738,7 @@ class boetemplate extends soetemplate private function delete_in_cache() { //echo "

delete_in_cache('$this->name','$this->template','$this->lang','$this->version')

\n"; - unset($GLOBALS['egw_info']['etemplate']['cache'][$this->cache_name()]); + unset(self::$template_cache[$this->cache_name()]); } /** @@ -833,8 +759,8 @@ class boetemplate extends soetemplate $version = $name['version']; $name = $name['name']; } - if (!isset($GLOBALS['egw_info']['etemplate']['cache'][$cname]) || - !empty($version) && $GLOBALS['egw_info']['etemplate']['cache'][$cname]['version'] != $version) + if (!isset(self::$template_cache[$cname]) || + !empty($version) && self::$template_cache[$cname]['version'] != $version) { //echo " NOT found in cache

\n"; return False; @@ -860,7 +786,7 @@ class boetemplate extends soetemplate //if (is_array($name)) $version = $name['version']; echo "

read_from_cache(,,,version='$version'): "; if ($cname = $this->in_cache($name,$template,$lang,$group)) { - $this->init($GLOBALS['egw_info']['etemplate']['cache'][$cname]); + $this->init(self::$template_cache[$cname]); return True; } @@ -956,7 +882,6 @@ class boetemplate extends soetemplate */ static function _init_static() { - self::$garbage_collection_done =& $GLOBALS['egw_info']['etemplate']['garbage_collection_done']; } } boetemplate::_init_static(); @@ -997,3 +922,5 @@ if (!function_exists('set_cell_attribute_helper')) } } } +// just in case someone still uses the old var +$GLOBALS['egw_info']['flags']['etemplate']['loop'] =& boetemplate::$loop; \ No newline at end of file diff --git a/etemplate/inc/class.date_widget.inc.php b/etemplate/inc/class.date_widget.inc.php index 3710d3da0d..cd8f0d9a79 100644 --- a/etemplate/inc/class.date_widget.inc.php +++ b/etemplate/inc/class.date_widget.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @copyright 2002-8 by RalfBecker@outdoor-training.de + * @copyright 2002-9 by RalfBecker@outdoor-training.de * @package etemplate * @subpackage extensions * @version $Id$ @@ -231,10 +231,9 @@ class date_widget } if ($cell['needed']) { - $GLOBALS['egw_info']['etemplate']['to_process'][$name] = array( - 'type' => 'ext-'.$type, + etemplate::$request->set_to_process($name,'ext-'.$type,array( 'needed' => $cell['needed'], - ); + )); } $tpl =& new etemplate; $tpl->init('*** generated fields for date','','',0,'',0,0); // make an empty template @@ -539,7 +538,7 @@ class date_widget } elseif (!preg_match('/^-?[0-9]*[,.]?[0-9]*'.($extension_data['percent_allowed'] ? '%?' : '').'$/',$value_in)) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = lang("'%1' is not a valid floatingpoint number !!!",$value_in); + etemplate::set_validation_error($name,lang("'%1' is not a valid floatingpoint number !!!",$value_in)); return false; } else @@ -613,8 +612,8 @@ class date_widget // checking the date is a correct one if (!checkdate($value['m'],$value['d'],$value['Y'])) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] .= lang("'%1' is not a valid date !!!", - $GLOBALS['egw']->common->dateformatorder($value['Y'],$value['m'],$value['d'],true)); + etemplate::set_validation_error($name,lang("'%1' is not a valid date !!!", + common::dateformatorder($value['Y'],$value['m'],$value['d'],true))); } $data_format = $extension_data['data_format']; if (empty($data_format)) diff --git a/etemplate/inc/class.editor.inc.php b/etemplate/inc/class.editor.inc.php index da148b4a65..2274adf02c 100644 --- a/etemplate/inc/class.editor.inc.php +++ b/etemplate/inc/class.editor.inc.php @@ -430,7 +430,7 @@ class editor $additional = array(); if ($app == 'etemplate') { - $additional = $this->etemplate->types + $this->extensions + $this->aligns + $this->valigns + + $additional = etemplate::$types + $this->extensions + $this->aligns + $this->valigns + $this->edit_menu + $this->box_menu + $this->row_menu + $this->column_menu + $this->onclick_types + $this->onchange_types; } else // try to call the writeLangFile function of the app's ui-layer @@ -1360,7 +1360,7 @@ class editor $GLOBALS['egw_info']['flags']['java_script'] = "\n"; $GLOBALS['egw_info']['flags']['app_header'] = lang('Editable Templates - Editor'); $editor->exec('etemplate.editor.widget',$content,array( - 'type' => array_merge($this->etemplate->types,$this->extensions), + 'type' => array_merge(etemplate::$types,$this->extensions), 'align' => &$this->aligns, 'valign' => &$this->valigns, 'part' => $allowed_parts, diff --git a/etemplate/inc/class.etemplate.inc.php b/etemplate/inc/class.etemplate.inc.php index ce0986ee55..0b147ab68a 100644 --- a/etemplate/inc/class.etemplate.inc.php +++ b/etemplate/inc/class.etemplate.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @author Ralf Becker -* @copyright 2002-8 by RalfBecker@outdoor-training.de +* @copyright 2002-9 by RalfBecker@outdoor-training.de * @package etemplate * @subpackage api * @version $Id$ @@ -32,62 +32,96 @@ class etemplate extends boetemplate * 1=calls to show and process_show, 2=content after process_show, * 3=calls to show_cell and process_show_cell * - * @var int/string + * @public int/string */ - var $debug; - var $xslt = false; /* do we run in the xslt framework (true) or the regular eGW one (false) */ - var $class_conf = array('nmh' => 'th','nmr0' => 'row_on','nmr1' => 'row_off'); - var $public_functions = array('process_exec' => True); + public $debug; /** * Inner width of browser window * - * @var int + * @public int */ - var $innerWidth; + public $innerWidth; /** * Reference to the content-param of the last call to show, for extensions to use * - * @var array + * @public array */ - var $content; + public $content; /** * Reference to the sel_options-param of the last call to show, for extensions to use * - * @var array + * @public array */ - var $sel_options; + public $sel_options; /** - * Name of the currently processed etemplate, reference to $GLOBALS['egw_info']['etemplate']['name_form'] + * Name of the form of the currently processed etemplate * - * @var string + * @public string */ - var $name_form; + static $name_form='eTemplate'; /** - * Used form-names in this request, reference to $GLOBALS['egw_info']['etemplate']['name_forms'] + * Used form-names in this request * - * @var array + * @public array */ - var $name_forms; + static $name_forms=array(); /** * Basename of the variables (content) in $_POST and id's, usually 'exec', * if there's not more then one eTemplate on the page (then it will be exec, exec2, exec3, ... * - * @var string + * @public string */ - var $name_vars='exec'; + static $name_vars='exec'; /** * Are we running as sitemgr module or not * - * @var boolean + * @public boolean */ - var $sitemgr=false; + public $sitemgr=false; /** * Javascript to be called, when a widget get's double-clicked (used only by the editor) * A '%p' gets replace with the colon ':' separated template-name, -version and path of the clicked widget. * - * @var string + * @public string */ - var $onclick_handler; + public $onclick_handler; + + /** + * Extra options for forms, eg. enctype="multipart/form-data" + * + * @public string + */ + static protected $form_options = ''; + + /** + * Validation errors from process_show and the extensions, should be set via etemplate::set_validation_error + * + * @public array form_name => message pairs + */ + static protected $validation_errors = array(); + + /** + * Flag if the browser has javascript enabled + * + * @var boolean + */ + static public $java_script; + + /** + * Flag if exec() is called as part of a hook, replaces the 1.6 and earlier $GLOBALS['egw_info']['etemplate']['hooked'] global variable + * + * @var boolean + */ + static public $hooked; + + /** + * The following 3 static vars are used to allow eTemplate apps to hook into each other + * + * @var mixed + */ + static private $previous_content; + static private $hook_content; + static private $hook_app; /** * constructor of etemplate class, reads an eTemplate if $name is given @@ -95,11 +129,9 @@ class etemplate extends boetemplate * @param string $name of etemplate or array with name and other keys * @param string/array $load_via with keys of other etemplate to load in order to get $name */ - function etemplate($name='',$load_via='') + function __construct($name='',$load_via='') { - $this->boetemplate($name,$load_via); - - //$this->xslt = is_object($GLOBALS['egw']->xslttpl); + parent::__construct($name,$load_via); $this->sitemgr = isset($GLOBALS['Common_BO']) && is_object($GLOBALS['Common_BO']); @@ -112,9 +144,6 @@ class etemplate extends boetemplate $this->innerWidth = 1018; // default width for an assumed screen-resolution of 1024x768 } //echo "

_POST[innerWidth]='$_POST[innerWidth]', innerWidth=$this->innerWidth

\n"; - $this->name_form =& $GLOBALS['egw_info']['etemplate']['name_form']; - $this->name_forms =& $GLOBALS['egw_info']['etemplate']['name_forms']; - if (!is_array($this->name_forms)) $this->name_forms = array(); } /** @@ -127,7 +156,7 @@ class etemplate extends boetemplate */ static function location($params='') { - $GLOBALS['egw']->redirect_link(is_array($params) ? '/index.php' : $params, + egw::redirect_link(is_array($params) ? '/index.php' : $params, is_array($params) ? $params : ''); } @@ -157,7 +186,7 @@ class etemplate extends boetemplate */ function exec($method,$content,$sel_options='',$readonlys='',$preserv='',$output_mode=0,$ignore_validation='',$changes='') { - //echo "
globals[java_script] = '".$GLOBALS['egw_info']['etemplate']['java_script']."', this->java_script() = '".$this->java_script()."'\n"; + //echo "
globals[java_script] = '".self::$java_script."', this->java_script() = '".$this->java_script()."'\n"; if (!$sel_options) { $sel_options = array(); @@ -182,59 +211,66 @@ class etemplate extends boetemplate { $GLOBALS['egw']->translation->add_app('etemplate'); // some extensions have own texts } - $id = $this->appsession_id(); - //echo "

unsetting existing egw_info[etemplate] which had keys=".implode(',',array_keys($GLOBALS['egw_info']['etemplate']))."

\n"; - // initialise $GLOBALS['egw_info']['etemplate'], in case there are multiple eTemplates on a page - $GLOBALS['egw_info']['etemplate'] = array( - 'name_forms' => $GLOBALS['egw_info']['etemplate']['name_forms'], - 'validation_errors' => $GLOBALS['egw_info']['etemplate']['validation_errors'], - 'hooked' => $GLOBALS['egw_info']['etemplate']['hooked'], - 'content' => $GLOBALS['egw_info']['etemplate']['content'], - 'hook_content' => $GLOBALS['egw_info']['etemplate']['hook_content'], - 'hook_app' => $GLOBALS['egw_info']['etemplate']['hook_app'], - 'extension_data' => $GLOBALS['egw_info']['etemplate']['extension_data'], - ); - //echo "

hooked=".(int)!!$GLOBALS['egw_info']['etemplate']['hooked'].", content=".(int)!!$GLOBALS['egw_info']['etemplate']['content'].", hook_content=".(int)!!$GLOBALS['egw_info']['etemplate']['hook_content'].", hook_app={$GLOBALS['egw_info']['etemplate']['hook_app']}

\n"; - $this->name_form =& $GLOBALS['egw_info']['etemplate']['name_form']; - $this->name_forms =& $GLOBALS['egw_info']['etemplate']['name_forms']; - if (!is_array($this->name_forms)) $this->name_forms = array(); - // use different form-names to allows multiple eTemplates in one page, eg. addressbook-view - $this->name_form = 'eTemplate'; - if (in_array($this->name_form,$this->name_forms)) + self::$name_form = 'eTemplate'; + if (in_array(self::$name_form,self::$name_forms)) { - $this->name_form .= 1+count($this->name_forms); - $this->name_vars .= 1+count($this->name_forms); + self::$name_form .= 1+count(self::$name_forms); + self::$name_vars .= 1+count(self::$name_forms); } - $this->name_forms[] = $this->name_form; + self::$name_forms[] = self::$name_form; - $GLOBALS['egw_info']['etemplate']['output_mode'] = $output_mode; // let extensions "know" they are run eg. in a popup - $GLOBALS['egw_info']['etemplate']['form_options'] = ''; // might be set in show - $GLOBALS['egw_info']['etemplate']['to_process'] = array(); + self::$request = etemplate_request::read(); + self::$request->output_mode = $output_mode; // let extensions "know" they are run eg. in a popup + self::$request->readonlys = $readonlys; + self::$request->content = $content; + self::$request->changes = $changes; + self::$request->sel_options = $sel_options; + self::$request->preserv = $preserv; + self::$request->method = $method; + self::$request->ignore_validation = $ignore_validation; + self::$request->name_vars = self::$name_vars; - $html = html::form($this->include_java_script(1). - html::input_hidden(array( - 'submit_button' => '', - 'innerWidth' => '', - ),'',False). - $this->show($this->complete_array_merge($content,$changes),$sel_options,$readonlys,$this->name_vars),array( - 'etemplate_exec_id' => $id - ),$this->sitemgr ? '' : '/etemplate/process_exec.php?menuaction='.$method, - '',$this->name_form,$GLOBALS['egw_info']['etemplate']['form_options']. - // dont set the width of popups! - ($output_mode != 0 ? '' : ' onsubmit="this.innerWidth.value=window.innerWidth ? window.innerWidth : document.body.clientWidth;"')); - //echo "to_process="; _debug_array($GLOBALS['egw_info']['etemplate']['to_process']); + $html = $this->include_java_script(1). + $this->show($this->complete_array_merge($content,$changes),$sel_options,$readonlys,self::$name_vars); + self::$request->java_script = self::$java_script; + self::$request->java_script_from_flags = $GLOBALS['egw_info']['flags']['java_script']; + self::$request->java_script_body_tags = $GLOBALS['egw']->js->body; + self::$request->java_script_files = $GLOBALS['egw']->js->files; + self::$request->include_xajax = $GLOBALS['egw_info']['flags']['include_xajax']; + if (!$this->sitemgr) + { + $hooked = isset(self::$previous_content) || !isset($GLOBALS['egw']->template) ? + self::$previous_content : $GLOBALS['egw']->template->get_var('phpgw_body'); + } + self::$request->hooked = $hooked ? $hooked : self::$hook_content; + self::$request->hook_app = $hooked ? $GLOBALS['egw_info']['flags']['currentapp'] : self::$hook_app; + self::$request->app_header = $GLOBALS['egw_info']['flags']['app_header']; + if (self::$request->output_mode == -1) self::$request->output_mode = 0; + self::$request->template = $this->as_array(2); + + $html = html::form(html::input('etemplate_exec_id',self::$request->id(),'hidden',' id="etemplate_exec_id"').$html. + html::input_hidden(array( + 'submit_button' => '', + 'innerWidth' => '', + ),'',false),array(),$this->sitemgr ? '' : '/etemplate/process_exec.php?menuaction='.$method, + '',self::$name_form,self::$form_options. + // dont set the width of popups! + ($output_mode != 0 ? '' : ' onsubmit="this.innerWidth.value=window.innerWidth ? window.innerWidth : document.body.clientWidth;"')); + //echo "to_process="; _debug_array(self::$request->to_process); + + //echo '

'.__METHOD__."($method,...) etemplate[hooked]=".(int)self::$hooked.", etemplate[hook_app]='".self::$hook_app."', isset(etemplate[content])=".(int)isset(self::$previous_content)."

\n"; if ($this->sitemgr) { $GLOBALS['egw_info']['flags']['java_script'] .= $this->include_java_script(2); } - elseif (!$this->xslt) + else { - $hooked = isset($GLOBALS['egw_info']['etemplate']['content']) || !isset($GLOBALS['egw']->template) ? - $GLOBALS['egw_info']['etemplate']['content'] : $GLOBALS['egw']->template->get_var('phpgw_body'); + // support the old global var, in case old apps like 1.6 infolog use it + if (isset($GLOBALS['egw_info']['etemplate']['hooked'])) self::$hooked = $GLOBALS['egw_info']['etemplate']['hooked']; - if (!@$GLOBALS['egw_info']['etemplate']['hooked'] && (int) $output_mode != 1 && (int) $output_mode != -1) // not just returning the html + if (!@self::$hooked && (int) $output_mode != 1 && (int) $output_mode != -1) // not just returning the html { $GLOBALS['egw_info']['flags']['java_script'] .= $this->include_java_script(2); @@ -251,109 +287,47 @@ class etemplate extends boetemplate $GLOBALS['egw']->common->egw_header(); } - elseif (!isset($GLOBALS['egw_info']['etemplate']['content'])) + elseif (!isset(self::$previous_content)) { $html = $this->include_java_script(2).$html; // better than nothing } // saving the etemplate content for other hooked etemplate apps (atm. infolog hooked into addressbook) - $GLOBALS['egw_info']['etemplate']['content'] =& $html; + self::$previous_content =& $html; } - else - { - $hooked = $GLOBALS['egw']->xslttpl->get_var('phpgw'); - $hooked = $hooked['body_data']; - $GLOBALS['egw']->xslttpl->set_var('phpgw',array('java_script' => $GLOBALS['egw_info']['flags']['java_script'].$this->include_java_script(2))); - } - //echo "

uietemplate::exec($method,...) after show: sitemgr=$this->sitemgr, xslt=$this->xslt, hooked=$hooked, output_mode=$output_mode

\n"; + //echo '

'.__METHOD__."($method,...) after show: sitemgr=$this->sitemgr, hooked=".(int)$hooked.", output_mode=$output_mode

\n"; if (!$this->sitemgr && (int) $output_mode != 1 && (int) $output_mode != -1) // NOT returning html { - if (!$this->xslt) + if (!@self::$hooked) { - if (!@$GLOBALS['egw_info']['etemplate']['hooked']) + if((int) $output_mode != 2) { - if((int) $output_mode != 2) + echo parse_navbar(); + } + else + { + echo '
'."\n"; + if ($GLOBALS['egw_info']['user']['apps']['manual']) // adding a manual icon to every popup { - echo parse_navbar(); - } - else - { - echo '
'."\n"; - if ($GLOBALS['egw_info']['user']['apps']['manual']) // adding a manual icon to every popup - { - $manual =& new etemplate('etemplate.popup.manual'); - echo $manual->show(array()); - unset($manual); - echo ''."\n"; - echo '
'.html::image('phpgwapi','ajax-loader') . '
'; - } + $manual =& new etemplate('etemplate.popup.manual'); + echo $manual->show(array()); + unset($manual); + echo ''."\n"; + echo '
'.html::image('phpgwapi','ajax-loader') . '
'; } } - echo $GLOBALS['egw_info']['etemplate']['hook_content'].$html; + } + echo self::$hook_content.$html; - if (!$GLOBALS['egw_info']['etemplate']['hooked'] && - (!isset($_GET['menuaction']) || - strpos($_SERVER['PHP_SELF'],'process_exec.php')!==false)) - { - if((int) $output_mode == 2) - { - echo "
\n"; - } - $GLOBALS['egw']->common->egw_footer(); - } - } - else + if (!self::$hooked && (!isset($_GET['menuaction']) || strpos($_SERVER['PHP_SELF'],'process_exec.php') !== false)) { - // need to add some logic here to support popups (output_mode==2) for xslt, but who cares ... - $GLOBALS['egw']->xslttpl->set_var('phpgw',array('body_data' => $html)); + if((int) $output_mode == 2) + { + echo "
\n"; + } + $GLOBALS['egw']->common->egw_footer(); } } - $this->save_appsession($sess = $this->as_array(2) + array( - 'readonlys' => $readonlys, - 'content' => $content, - 'changes' => $changes, - 'sel_options' => $sel_options, - 'preserv' => $preserv, - 'extension_data' => $GLOBALS['egw_info']['etemplate']['extension_data'], - 'to_process' => $GLOBALS['egw_info']['etemplate']['to_process'], - 'java_script' => $GLOBALS['egw_info']['etemplate']['java_script'], - 'java_script_from_flags' => $GLOBALS['egw_info']['flags']['java_script'], - 'java_script_body_tags' => $GLOBALS['egw']->js->body, - 'java_script_files' => $GLOBALS['egw']->js->files, - 'include_xajax' => $GLOBALS['egw_info']['flags']['include_xajax'], - 'dom_enabled' => $GLOBALS['egw_info']['etemplate']['dom_enabled'], - 'hooked' => $hooked ? $hooked : $GLOBALS['egw_info']['etemplate']['hook_content'], - 'hook_app' => $hooked ? $GLOBALS['egw_info']['flags']['currentapp'] : $GLOBALS['egw_info']['etemplate']['hook_app'], - 'app_header' => $GLOBALS['egw_info']['flags']['app_header'], - 'output_mode' => $output_mode != -1 ? $output_mode : 0, - 'session_used' => 0, - 'ignore_validation' => $ignore_validation, - 'method' => $method, - 'name_vars' => $this->name_vars, - ),$id); - //echo "

hooked=".(int)!!$hooked.", content=".(int)!!$GLOBALS['egw_info']['etemplate']['content'].", hook_content=".(int)!!$GLOBALS['egw_info']['etemplate']['hook_content'].", hook_app={$GLOBALS['egw_info']['etemplate']['hook_app']}

\n"; - //echo "

session: "; foreach($sess as $key => $val) echo "$key=$val, "; echo "

\n"; - /* - echo "

total size session data = ".($total=strlen(serialize($sess)))."

\n"; - echo "

shares bigger then 1.0% percent of it:

\n"; - foreach($sess as $key => $val) - { - $len = strlen(is_array($val) ? serialize($val) : $val); - $len .= ' ('.sprintf('%2.1lf',($percent = 100.0 * $len / $total)).'%)'; - if ($percent < 1.0) continue; - echo "

$key: strlen(\$val)=$len

\n"; - if (is_array($val) && $len > 2000) - { - foreach($val as $k => $v) - { - $l = strlen(is_array($v) ? serialize($v) : $v); - $l .= ' ('.sprintf('%2.1lf',($p = 100.0 * $l / $total)).'%)'; - if ($p < 1.0) continue; - echo "

 - {$key}[$k]: strlen(\$v)=$l

\n"; - } - } - } - */ if ($this->sitemgr || (int) $output_mode == 1 || (int) $output_mode == -1) // return html { return $html; @@ -364,16 +338,16 @@ class etemplate extends boetemplate * Check if we have not ignored validation errors * * @param string $ignore_validation='' if not empty regular expression for validation-errors to ignore - * @param string $cname=null name-prefix, which need to be ignored, default $this->name_vars + * @param string $cname=null name-prefix, which need to be ignored, default self::$name_vars * @return boolean true if there are not ignored validation errors, false otherwise */ function validation_errors($ignore_validation='',$cname=null) { - if (is_null($cname)) $cname = $this->name_vars; - //echo "

uietemplate::validation_errors('$ignore_validation','$cname') validation_error="; _debug_array($GLOBALS['egw_info']['etemplate']['validation_errors']); - if (!$ignore_validation) return count($GLOBALS['egw_info']['etemplate']['validation_errors']) > 0; + if (is_null($cname)) $cname = self::$name_vars; + //echo "

uietemplate::validation_errors('$ignore_validation','$cname') validation_error="; _debug_array(self::$validation_errors); + if (!$ignore_validation) return count(self::$validation_errors) > 0; - foreach($GLOBALS['egw_info']['etemplate']['validation_errors'] as $name => $error) + foreach(self::$validation_errors as $name => $error) { if ($cname) $name = preg_replace('/^'.$cname.'\[([^\]]+)\](.*)$/','\\1\\2',$name); @@ -408,43 +382,38 @@ class etemplate extends boetemplate if(!$exec) $exec = $_POST; //echo "process_exec: _POST ="; _debug_array($_POST); - $session_data = $this->get_appsession($etemplate_exec_id); - //echo "

process_exec: session_data ="; _debug_array($session_data); - - if (!$etemplate_exec_id || !is_array($session_data) || count($session_data) < 10) + if (!$etemplate_exec_id || !(self::$request = etemplate_request::read($etemplate_exec_id))) { if ($this->sitemgr) return false; //echo "uitemplate::process_exec() id='$_POST[etemplate_exec_id]' invalid session-data !!!"; _debug_array($_SESSION); // this prevents an empty screen, if the sessiondata gets lost somehow $this->location(array('menuaction' => $_GET['menuaction'],'post_empty' => (int)!$_POST)); } - $this->name_vars = $session_data['name_vars']; + self::$name_vars = self::$request->name_vars; if (isset($submit_button) && !empty($submit_button)) { - $this->set_array($exec,$submit_button,'pressed'); + self::set_array($exec,$submit_button,'pressed'); } - $content = $exec[$this->name_vars]; + $content = $exec[self::$name_vars]; if (!is_array($content)) { $content = array(); } - $this->init($session_data); - $GLOBALS['egw_info']['etemplate']['extension_data'] = $session_data['extension_data']; - $GLOBALS['egw_info']['etemplate']['java_script'] = $session_data['java_script'] || $_POST['java_script']; - $GLOBALS['egw_info']['etemplate']['dom_enabled'] = $session_data['dom_enabled'] || $_POST['dom_enabled']; - //echo "globals[java_script] = '".$GLOBALS['egw_info']['etemplate']['java_script']."', session_data[java_script] = '".$session_data['java_script']."', _POST[java_script] = '".$_POST['java_script']."'\n"; + $this->init(self::$request->template); + self::$java_script = self::$request->java_script || $_POST['java_script']; + //echo "globals[java_script] = '".self::$java_script."', session_data[java_script] = '".self::$request->java_script."', _POST[java_script] = '".$_POST['java_script']."'\n"; //echo "process_exec($this->name) content ="; _debug_array($content); if ($GLOBALS['egw_info']['flags']['currentapp'] != 'etemplate') { $GLOBALS['egw']->translation->add_app('etemplate'); // some extensions have own texts } - $this->process_show($content,$session_data['to_process'],$this->name_vars,$type); + $this->process_show($content,self::$request->to_process,self::$name_vars,$type); - $GLOBALS['egw_info']['etemplate']['loop'] |= !$this->canceled && $this->button_pressed && - $this->validation_errors($session_data['ignore_validation']); // set by process_show + self::$loop |= !$this->canceled && $this->button_pressed && + $this->validation_errors(self::$request->ignore_validation); // set by process_show // If a tab has an error on it, change to that tab - foreach($GLOBALS['egw_info']['etemplate']['validation_errors'] as $form_name => $msg) + foreach(self::$validation_errors as $form_name => $msg) { $name = $this->template_name($form_name); if (!$this->get_widget_by_name($name)) @@ -466,55 +435,48 @@ class etemplate extends boetemplate } //echo "process_exec($this->name) process_show(content) ="; _debug_array($content); - //echo "process_exec($this->name) session_data[changes] ="; _debug_array($session_data['changes']); - $content = $this->complete_array_merge($session_data['changes'],$content); + //echo "process_exec($this->name) session_data[changes] ="; _debug_array(self::$request->changes']); + $content = $this->complete_array_merge(self::$request->changes,$content); //echo "process_exec($this->name) merge(changes,content) ="; _debug_array($content); - if ($GLOBALS['egw_info']['etemplate']['loop']) + if (self::$loop) { - if ($session_data['hooked'] != '') // set previous phpgw_body if we are called as hook + if (self::$request->hooked != '') // set previous phpgw_body if we are called as hook { - if (!$this->xslt) - { - $GLOBALS['egw_info']['etemplate']['hook_content'] = $session_data['hooked']; - $GLOBALS['egw_info']['flags']['currentapp'] = $GLOBALS['egw_info']['etemplate']['hook_app'] = $session_data['hook_app']; - } - else - { - $GLOBALS['egw']->xslttpl->set_var('phpgw',array('body_data' => $session_data['hooked'])); - } + self::$hook_content = self::$request->hooked; + $GLOBALS['egw_info']['flags']['currentapp'] = self::$hook_app = self::$request->hook_app; } - if($session_data['include_xajax']) $GLOBALS['egw_info']['flags']['include_xajax'] = true; + if(self::$request->include_xajax) $GLOBALS['egw_info']['flags']['include_xajax'] = true; - if (!empty($session_data['app_header'])) + if (!empty(self::$request->app_header)) { - $GLOBALS['egw_info']['flags']['app_header'] = $session_data['app_header']; + $GLOBALS['egw_info']['flags']['app_header'] = self::$request->app_header; } - $GLOBALS['egw_info']['flags']['java_script'] .= $session_data['java_script_from_flags']; - if (!empty($session_data['java_script_body_tags'])) + $GLOBALS['egw_info']['flags']['java_script'] .= self::$request->java_script_from_flags; + if (!empty(self::$request->java_script_body_tags)) { - foreach ($session_data['java_script_body_tags'] as $tag => $code) + foreach (self::$request->java_script_body_tags as $tag => $code) { //error_log($GLOBALS['egw']->js->body[$tag]); $GLOBALS['egw']->js->body[$tag] .= $code; } } - if (is_array($session_data['java_script_files'])) + if (is_array(self::$request->java_script_files)) { - $GLOBALS['egw']->js->files = !is_array($GLOBALS['egw']->js->files) ? $session_data['java_script_files'] : - $this->complete_array_merge($GLOBALS['egw']->js->files,$session_data['java_script_files']); + $GLOBALS['egw']->js->files = !is_array($GLOBALS['egw']->js->files) ? self::$request->java_script_files : + $this->complete_array_merge($GLOBALS['egw']->js->files,self::$request->java_script_files); } //echo "

process_exec($this->name): loop is set, content=

\n"; _debug_array($content); - return $this->exec($session_data['method'],$session_data['content'],$session_data['sel_options'], - $session_data['readonlys'],$session_data['preserv'],$session_data['output_mode'], - $session_data['ignore_validation'],$content); + return $this->exec(self::$request->method,self::$request->content,self::$request->sel_options, + self::$request->readonlys,self::$request->preserv,self::$request->output_mode, + self::$request->ignore_validation,$content); } else { //echo "

process_exec($this->name): calling $session_data[method]

\n"; - return ExecMethod($session_data['method'],$this->complete_array_merge($session_data['preserv'],$content)); + return ExecMethod(self::$request->method,$this->complete_array_merge(self::$request->preserv,$content)); } } @@ -528,26 +490,29 @@ class etemplate extends boetemplate function process_values2url() { //echo "process_exec: _GET ="; _debug_array($_GET); - $session_data = $this->get_appsession($_GET['etemplate_exec_id']); - //echo "

process_exec: session_data ="; _debug_array($session_data); - - if (!$_GET['etemplate_exec_id'] || !is_array($session_data) || count($session_data) < 10) + if (!$_GET['etemplate_exec_id'] || !($request = etemplate_request::read($_GET['etemplate_exec_id']))) { return false; } - $this->name_vars = $session_data['name_vars']; - $GLOBALS['egw_info']['etemplate']['extension_data'] = $session_data['extension_data']; + self::$name_vars = $request->name_vars; - $content = $_GET[$this->name_vars]; + $content = $_GET[self::$name_vars]; if (!is_array($content)) { $content = array(); } - $this->process_show($content,$session_data['to_process'],$this->name_vars); + $this->process_show($content,$request->to_process,self::$name_vars); - return $this->complete_array_merge($session_data['preserv'],$content); + return $this->complete_array_merge($request->preserv,$content); } + /** + * Flag if the styles of a certain template are already included + * + * @var array template-name => boolean + */ + static private $styles_included = array(); + /** * creates HTML from an eTemplate * @@ -592,13 +557,13 @@ class etemplate extends boetemplate { $content = array(); // happens if incl. template has no content } - // make the content availible as class-var for extensions + // make the content availible as class-public for extensions $this->content =& $content; $html = "\n\n\n

name\">\n\n"; - if (!$GLOBALS['egw_info']['etemplate']['styles_included'][$this->name]) + if (!self::$styles_included[$this->name]) { - $GLOBALS['egw_info']['etemplate']['styles_included'][$this->name] = True; + self::$styles_included[$this->name] = True; $html .= html::style($this->style)."\n\n"; } $path = '/'; @@ -746,7 +711,7 @@ class etemplate extends boetemplate { $cl = 'row_'.($nmr_alternate++ & 1 ? 'off' : 'on').substr($cl,3); // alternate color } - $cl = isset($this->class_conf[$cl]) ? $this->class_conf[$cl] : $cl; + $cl = isset(self::$class_conf[$cl]) ? self::$class_conf[$cl] : $cl; $rows[".$row"] .= html::formatOptions($cl,'class'); $rows[".$row"] .= html::formatOptions($class,',valign'); reset ($cols); @@ -848,7 +813,7 @@ class etemplate extends boetemplate } $cl = $cl['class']; } - $cl = $this->expand_name(isset($this->class_conf[$cl]) ? $this->class_conf[$cl] : $cl, + $cl = $this->expand_name(isset(self::$class_conf[$cl]) ? self::$class_conf[$cl] : $cl, $c,$r,$show_c,$show_row,$content); // else the class is set twice, in the table and the table-cell, which is not good for borders if ($cl && $cell['type'] != 'template' && $cell['type'] != 'grid') @@ -867,7 +832,7 @@ class etemplate extends boetemplate } $html = html::table($rows,html::formatOptions($options,'width,height,border,class,cellspacing,cellpadding'). html::formatOptions($grid['span'],',class'). - html::formatOptions($grid['name']?$this->form_name($cname,$grid['name']):'','id')); + html::formatOptions($grid['name']?self::form_name($cname,$grid['name']):'','id')); if (!empty($overflow)) { if (is_numeric($height)) $height .= 'px'; @@ -926,6 +891,8 @@ class etemplate extends boetemplate return $name; } + static private $class_conf = array('nmh' => 'th','nmr0' => 'row_on','nmr1' => 'row_off'); + /** * generates HTML for one widget (input-field / cell) * @@ -965,7 +932,7 @@ class etemplate extends boetemplate { list($name) = explode('=',$name); } - $form_name = $this->form_name($cname,$name); + $form_name = self::form_name($cname,$name); $value = $this->get_array($content,$name); @@ -1000,7 +967,7 @@ class etemplate extends boetemplate // the while loop allows to build extensions from other extensions // please note: only the first extension's post_process function is called !!! list($type,$sub_type) = explode('-',$cell['type']); - while ((!$this->types[$cell['type']] || !empty($sub_type)) && $this->haveExtension($type,'pre_process')) + while ((!self::$types[$cell['type']] || !empty($sub_type)) && $this->haveExtension($type,'pre_process')) { //echo "

pre_process($cell[name]/$cell[type])

\n"; if (strchr($cell['size'],'$') || $cell['size'][0] == '@') @@ -1018,7 +985,7 @@ class etemplate extends boetemplate $readonly = $cell['readonly'] !== false && ($readonly || $cell['readonly']); // might be set or unset (===false) by extension - $this->set_array($content,$name,$value); + self::set_array($content,$name,$value); if ($cell['type'] == $type.'-'.$sub_type) break; // stop if no further type-change @@ -1153,14 +1120,13 @@ class etemplate extends boetemplate if (!$readonly) { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $cell['type'], + self::$request->set_to_process($form_name,$cell['type'],array( 'maxlength' => $cell_opts[1], 'needed' => $cell['needed'], 'preg' => $cell_opts[2], 'min' => $min, // int and float only 'max' => $max, - ); + )); } } unset($cell_opts); @@ -1177,10 +1143,9 @@ class etemplate extends boetemplate } if (!$readonly) { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $cell['type'], + self::$request->set_to_process($form_name,$cell['type'],array( 'needed' => $cell['needed'], - ); + )); } break; case 'htmlarea': // Multiline formatted Text Input, size: {simple|extended|advanced},height,width,toolbar-expanded,upload-path @@ -1198,10 +1163,9 @@ class etemplate extends boetemplate ); $html .= html::fckEditor($form_name,$value,$mode,$fckoptions,$height,$width,$baseref); - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $cell['type'], + self::$request->set_to_process($form_name,$cell['type'],array( 'needed' => $cell['needed'], - ); + )); } else { @@ -1238,17 +1202,16 @@ class etemplate extends boetemplate } $html .= html::input($form_name,$set_val,'checkbox',$options); - if ($multiple) $form_name = $this->form_name($cname,substr($cell['name'],0,-2)); + if ($multiple) $form_name = self::form_name($cname,substr($cell['name'],0,-2)); - if (!isset($GLOBALS['egw_info']['etemplate']['to_process'][$form_name])) + if (!self::$request->isset_to_process($form_name)) { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $cell['type'], + self::$request->set_to_process($form_name,$cell['type'],array( 'unset_value' => $unset_val, 'multiple' => $multiple, - ); + )); } - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name]['values'][] = $set_val; + self::$request->set_to_process_attribute($form_name,'values',$set_val,true); if (!$multiple) unset($set_val); // otherwise it will be added to the label } break; @@ -1271,7 +1234,7 @@ class etemplate extends boetemplate else { $html .= html::input($form_name,$set_val,'RADIO',$options); - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = $cell['type']; + self::$request->set_to_process($form_name,$cell['type']); } break; case 'button': @@ -1290,7 +1253,7 @@ class etemplate extends boetemplate { $onclick = ($onclick ? preg_replace('/^return(.*);$/','if (\\1) ',$onclick) : ''). (((string)$cell['onchange'] === '1' || $img) ? - "return submitit($this->name_form,'".addslashes($form_name)."');" : $cell['onchange']).'; return false;'; + 'return submitit('.self::$name_form.",'".addslashes($form_name)."');" : $cell['onchange']).'; return false;'; if (!html::$netscape4 && substr($img,-1) == '%' && is_numeric($percent = substr($img,0,-1))) { @@ -1319,10 +1282,10 @@ class etemplate extends boetemplate $extra_label = False; if (!$readonly && $type != 'buttononly') // input button, are never submitted back! { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = $cell['type']; + self::$request->set_to_process($form_name,$cell['type']); if ($name == 'cancel' || stripos($name,'[cancel]') !== false) { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = 'cancel'; + self::$request->set_to_process($form_name,'cancel'); } } break; @@ -1478,7 +1441,7 @@ class etemplate extends boetemplate $html .= html::select($form_name.($multiple > 1 ? '[]' : ''),$value,$sels, $cell['no_lang'],$options,$multiple); } - if (!isset($GLOBALS['egw_info']['etemplate']['to_process'][$form_name])) + if (!self::$request->isset_to_process($form_name)) { // fix for optgroup's $options=array(); @@ -1495,12 +1458,11 @@ class etemplate extends boetemplate } } } - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $cell['type'], + self::$request->set_to_process($form_name,$cell['type'],array( 'needed' => $cell['needed'], 'allowed' => array_keys($options), 'multiple'=> $multiple, - ); + )); } } break; @@ -1527,7 +1489,7 @@ class etemplate extends boetemplate if ((int) $cell_options) $options .= ' size="'.(int)$cell_options.'"'; if (substr($name,-2) == '[]') { - $GLOBALS['egw_info']['etemplate']['form_options'] .= ' enctype="multipart/form-data"'; + self::$form_options .= ' enctype="multipart/form-data"'; if (strpos($options,'onChange="') !== false) { $options = preg_replace('/onChange="([^"]+)"/i','onChange="\\1; add_upload(this);"',$options); @@ -1540,11 +1502,10 @@ class etemplate extends boetemplate else { $html .= html::input_hidden($path_name = str_replace($name,$name.'_path',$form_name),'.'); - $GLOBALS['egw_info']['etemplate']['form_options'] .= - " enctype=\"multipart/form-data\" onsubmit=\"set_element2(this,'$path_name','$form_name')\""; + self::$form_options = " enctype=\"multipart/form-data\" onsubmit=\"set_element2(this,'$path_name','$form_name')\""; } $html .= html::input($form_name,'','file',$options); - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = $cell['type']; + self::$request->set_to_process($form_name,$cell['type']); } break; case 'vbox': @@ -1602,7 +1563,7 @@ class etemplate extends boetemplate } $cl = $cl['class']; } - $box_item_class = $this->expand_name(isset($this->class_conf[$cl]) ? $this->class_conf[$cl] : $cl, + $box_item_class = $this->expand_name(isset(self::$class_conf[$cl]) ? self::$class_conf[$cl] : $cl, $show_c,$show_row,$content['.c'],$content['.row'],$content); $rows[$box_row]['.'.$box_col] .= html::formatOptions($box_item_class,'class'); } @@ -1661,7 +1622,7 @@ class etemplate extends boetemplate $s_height = "height: $s_height".(substr($s_height,-1) != '%' ? 'px' : '').';'; } $html = html::input_hidden($form_name,$value); - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = $cell['type']; + self::$request->set_to_process($form_name,$cell['type']); for ($n = 1; $n <= $cell_options; ++$n) { @@ -1687,25 +1648,14 @@ class etemplate extends boetemplate // extension-processing need to be after all other and only with diff. name if ($ext_type && !$readonly && $this->haveExtension($ext_type,'post_process')) { // unset it first, if it is already set, to be after the other widgets of the ext. - $to_process = 'ext-'.$ext_type; - if (is_array($GLOBALS['egw_info']['etemplate']['to_process'][$form_name])) - { - $to_process = $GLOBALS['egw_info']['etemplate']['to_process'][$form_name]; - $to_process['type'] = 'ext-'.$ext_type; - } - unset($GLOBALS['egw_info']['etemplate']['to_process'][$form_name]); - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = $to_process; + $to_process = self::$request->get_to_process($form_name); + self::$request->unset_to_process($form_name); + self::$request->set_to_process($form_name,'ext-'.$ext_type,$to_process); } // save blur-value to strip it in process_exec - if (!empty($blur) && isset($GLOBALS['egw_info']['etemplate']['to_process'][$form_name])) + if (!empty($blur) && self::$request->isset_to_process($form_name)) { - if (!is_array($GLOBALS['egw_info']['etemplate']['to_process'][$form_name])) - { - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = array( - 'type' => $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] - ); - } - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name]['blur'] = $blur; + self::$request->set_to_process_attribute($form_name,'blur',$blur); } if ($extra_label && ($label != '' || $html == '')) { @@ -1722,7 +1672,7 @@ class etemplate extends boetemplate } if ($label && !$readonly && ($accesskey || $label_for || $type != 'label' && $cell['name'])) { - $label = html::label($label,$label_for ? $this->form_name($cname,$label_for) : + $label = html::label($label,$label_for ? self::form_name($cname,$label_for) : $form_name.($set_val?"[$set_val]":''),$accesskey); } if ($type == 'radio' || $type == 'checkbox' || $label && strpos($label,'%s')!==false) // default for radio is label after the button @@ -1755,9 +1705,9 @@ class etemplate extends boetemplate return html::a_href($html,$extra_link,'',$options); } // if necessary show validation-error behind field - if (isset($GLOBALS['egw_info']['etemplate']['validation_errors'][$form_name])) + if (isset(self::$validation_errors[$form_name])) { - $html .= ' '.$GLOBALS['egw_info']['etemplate']['validation_errors'][$form_name].''; + $html .= ' '.self::$validation_errors[$form_name].''; } // generate an extra div, if we have an onclick handler and NO children or it's an extension //echo "

$this->name($this->onclick_handler:$this->no_onclick:$this->onclick_proxy): $cell[type]/$cell[name]

\n"; @@ -1857,7 +1807,7 @@ class etemplate extends boetemplate if (preg_match_all("/form::name\\('([^']+)'\\)/",$on,$matches)) { foreach($matches[1] as $n => $matche_name) { - $matches[1][$n] = '\''.$this->form_name($cname,$matche_name).'\''; + $matches[1][$n] = '\''.self::form_name($cname,$matche_name).'\''; } $on = str_replace($matches[0],$matches[1],$on); } @@ -1905,7 +1855,7 @@ class etemplate extends boetemplate { return stripslashes($var); } - foreach($var as $key => $val) + foreach($public as $key => $val) { $var[$key] = is_array($val) ? self::array_stripslashes($val) : stripslashes($val); } @@ -1941,7 +1891,7 @@ class etemplate extends boetemplate { $content_in = etemplate::array_stripslashes($content_in); } - $GLOBALS['egw_info']['etemplate']['validation_errors'] = array(); + self::$validation_errors = array(); $this->canceled = $this->button_pressed = False; foreach($to_process as $form_name => $type) @@ -1964,7 +1914,7 @@ class etemplate extends boetemplate { $value = ''; // blur-values is equal to emtpy } - //echo "

process_show($this->name) loop was {$GLOBALS['egw_info']['etemplate']['loop']}, $type: $form_name = '$value'

\n"; + //echo "

process_show($this->name) loop was ".self::$loop.", $type: $form_name = '$value'

\n"; list($type,$sub) = explode('-',$type); switch ($type) { @@ -1982,15 +1932,15 @@ class etemplate extends boetemplate elseif (!etemplate::isset_array($content,$form_name)) { //echo "

setting content[$form_name]='$_cont' because is was unset !!!

\n"; - $this->set_array($content,$form_name,$_cont); + self::set_array($content,$form_name,$_cont); } if ($_cont === '' && $attr['needed'] && !$attr['blur']) { - $this->set_validation_error($form_name,lang('Field must not be empty !!!'),''); + self::set_validation_error($form_name,lang('Field must not be empty !!!'),''); } break; case 'htmlarea': - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); break; case 'int': case 'float': @@ -1999,7 +1949,7 @@ class etemplate extends boetemplate case 'textarea': if ($value === '' && $attr['needed'] && !$attr['blur']) { - $this->set_validation_error($form_name,lang('Field must not be empty !!!'),''); + self::set_validation_error($form_name,lang('Field must not be empty !!!'),''); } if ((int) $attr['maxlength'] > 0 && strlen($value) > (int) $attr['maxlength']) { @@ -2010,13 +1960,13 @@ class etemplate extends boetemplate switch($type) { case 'int': - $this->set_validation_error($form_name,lang("'%1' is not a valid integer !!!",$value),''); + self::set_validation_error($form_name,lang("'%1' is not a valid integer !!!",$value),''); break; case 'float': - $this->set_validation_error($form_name,lang("'%1' is not a valid floatingpoint number !!!",$value),''); + self::set_validation_error($form_name,lang("'%1' is not a valid floatingpoint number !!!",$value),''); break; default: - $this->set_validation_error($form_name,lang("'%1' has an invalid format !!!",$value),''); + self::set_validation_error($form_name,lang("'%1' has an invalid format !!!",$value),''); break; } } @@ -2028,30 +1978,30 @@ class etemplate extends boetemplate if (!empty($attr['min']) && $value < $attr['min']) { - $this->set_validation_error($form_name,lang("Value has to be at least '%1' !!!",$attr['min']),''); + self::set_validation_error($form_name,lang("Value has to be at least '%1' !!!",$attr['min']),''); $value = $type == 'int' ? (int) $attr['min'] : (float) $attr['min']; } if (!empty($attr['max']) && $value > $attr['max']) { - $this->set_validation_error($form_name,lang("Value has to be at maximum '%1' !!!",$attr['max']),''); + self::set_validation_error($form_name,lang("Value has to be at maximum '%1' !!!",$attr['max']),''); $value = $type == 'int' ? (int) $attr['max'] : (float) $attr['max']; } } } - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); break; case 'cancel': // cancel button ==> dont care for validation errors if ($value) { $this->canceled = True; - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); } break; case 'button': if ($value) { $this->button_pressed = True; - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); } break; case 'select': @@ -2061,7 +2011,7 @@ class etemplate extends boetemplate { if (!($attr['multiple'] && !$val) && !in_array($val,$attr['allowed'])) { - $this->set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",$attr['allowed'])),''); + self::set_validation_error($form_name,lang("'%1' is NOT allowed ('%2')!",$val,implode("','",$attr['allowed'])),''); $value = ''; break; } @@ -2070,19 +2020,19 @@ class etemplate extends boetemplate if (is_array($value)) $value = implode(',',$value); if ($value === '' && $attr['needed']) { - $this->set_validation_error($form_name,lang('Field must not be empty !!!',$value),''); + self::set_validation_error($form_name,lang('Field must not be empty !!!',$value),''); } - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); break; case 'checkbox': if ($value === false) // get_array() returns false for not set { - $this->set_array($content,$form_name,$attr['multiple'] ? array() : $attr['unset_value']); // need to be reported too + self::set_array($content,$form_name,$attr['multiple'] ? array() : $attr['unset_value']); // need to be reported too } else { $value = array_intersect(is_array($value) ? $value : array($value),$attr['values']); // return only allowed values - $this->set_array($content,$form_name,$attr['multiple'] ? $value : $value[0]); + self::set_array($content,$form_name,$attr['multiple'] ? $value : $value[0]); } break; case 'file': @@ -2121,7 +2071,7 @@ class etemplate extends boetemplate //echo $form_name; _debug_array($value); // fall-throught default: - $this->set_array($content,$form_name,$value); + self::set_array($content,$form_name,$value); break; } } @@ -2132,12 +2082,12 @@ class etemplate extends boetemplate if (is_int($this->debug) && $this->debug >= 2 || $this->debug == $this->name && $this->name) { echo "

process_show($this->name) end: content ="; _debug_array($content); - if (count($GLOBALS['egw_info']['etemplate']['validation_errors'])) + if (count(self::$validation_errors)) { - echo "

validation_errors = "; _debug_array($GLOBALS['egw_info']['etemplate']['validation_errors']); + echo "

validation_errors = "; _debug_array(self::$validation_errors); } } - return count($GLOBALS['egw_info']['etemplate']['validation_errors']); + return count(self::$validation_errors); } /** @@ -2145,19 +2095,19 @@ class etemplate extends boetemplate * * @param string $name (complete) name of the widget causing the error * @param string $error error-message already translated - * @param string $cname=null set it to '', if the name is already a form-name, defaults to $this->name_vars + * @param string $cname=null set it to '', if the name is already a form-name, defaults to self::$name_vars */ - function set_validation_error($name,$error,$cname=null) + static function set_validation_error($name,$error,$cname=null) { - if (is_null($cname)) $cname = $this->name_vars; + if (is_null($cname)) $cname = self::$name_vars; //echo "

etemplate::set_validation_error('$name','$error','$cname');

\n"; - if ($cname) $name = $this->form_name($cname,$name); + if ($cname) $name = self::form_name($cname,$name); - if ($GLOBALS['egw_info']['etemplate']['validation_errors'][$name]) + if (self::$validation_errors[$name]) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] .= ', '; + self::$validation_errors[$name] .= ', '; } - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] .= $error; + self::$validation_errors[$name] .= $error; } /** @@ -2169,15 +2119,11 @@ class etemplate extends boetemplate */ function java_script($consider_not_tested_as_enabled = True) { - $ret = !!$GLOBALS['egw_info']['etemplate']['java_script'] || - $consider_not_tested_as_enabled && !isset($GLOBALS['egw_info']['etemplate']['java_script']); - //echo "

java_script($consider_not_tested_as_enabled)='$ret', java_script='".$GLOBALS['egw_info']['etemplate']['java_script']."', isset(java_script)=".isset($GLOBALS['egw_info']['etemplate']['java_script'])."

\n"; + $ret = !!self::$java_script || + $consider_not_tested_as_enabled && !isset(self::$java_script); + //echo "

java_script($consider_not_tested_as_enabled)='$ret', java_script='".self::$java_script."', isset(java_script)=".isset(self::$java_script)."

\n"; return $ret; - return !!$GLOBALS['egw_info']['etemplate']['java_script'] || - $consider_not_tested_as_enabled && - (!isset($GLOBALS['egw_info']['etemplate']['java_script']) || - $GLOBALS['egw_info']['etemplate']['java_script'].'' == ''); } /** @@ -2190,13 +2136,10 @@ class etemplate extends boetemplate private function include_java_script($what = 3) { // this is to test if javascript is enabled - if ($what & 1 && !isset($GLOBALS['egw_info']['etemplate']['java_script'])) + if ($what & 1 && !isset(self::$java_script)) { $js = ' '; } @@ -2209,4 +2152,4 @@ if (document.getElementById) { } return $js; } -}; +} diff --git a/etemplate/inc/class.etemplate_request.inc.php b/etemplate/inc/class.etemplate_request.inc.php index c07eb69477..4221d3a8b5 100644 --- a/etemplate/inc/class.etemplate_request.inc.php +++ b/etemplate/inc/class.etemplate_request.inc.php @@ -1,36 +1,54 @@ - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-9 by Ralf Becker * @version $Id$ */ /** - * Class to represent the persitent information stored on the server for each eTemplate request - * - * The information is stored in the users session - * - * There are two ways to instanciate a request object: - * - * a) a new request: - * - * $request = new etemplate_request(); $id = $request->id(); - * - * b) open or modify an existing request: - * - * if (!($request = etemplate_request::read($id))) + * Class to represent the persitent information of an eTemplate request + * + * This class stores the request-data direct in a hidden var in the form. + * As this would allow an evil user to manipulate it and therefore compromise the security + * of an EGroupware instance, this class should only be used, if mcrypt is available + * to encrypt that data. The factory method etemplate_request::read() ensures that, + * by using etemplate_request_session instead. + * + * The key used to encrypt the request can be set in header.inc.php by setting + * + * $GLOBALS['egw_info']['server']['etemplate_form_key'] = 'something secret'; + * + * if this var is not set, the db_pass and EGW_SERVER_ROOT is used instead. + * + * The request object should be instancated only via the factory method etemplate::request($id=null) + * + * $request = etemplate::request(); + * + * // add request data + * + * $id = $request->id(); + * + * b) open or modify an existing request: + * + * if (!($request = etemplate::request($id))) * { * // request not found * } - * - * Ajax request can use this object to open the original request by using the id, they have to transmitt back, - * and register further variables, modify the registered ones or delete them. - * + * + * Ajax requests can use this object to open the original request by using the id, they have to transmitt back, + * and register further variables, modify the registered ones or delete them AND then update the id, if it changed: + * + * if (($new_id = $request->id()) != $id) + * { + * $response->addAssign('etemplate_exec_id','value',$new_id); + * } + * * For an example look in link_widget::ajax_search() */ class etemplate_request @@ -40,61 +58,132 @@ class etemplate_request * * @var array */ - private $data=array(); + protected $data=array(); /** * Flag if data has been modified and therefor need to be stored again in the session * * @var boolean */ - private $data_modified=false; + protected $data_modified=false; /** - * request id + * mcrypt resource + * + * @var resource + */ + static protected $mcrypt; + + /** + * See gzcompress, set it to 0 to not compress + * + * @var int + */ + static public $compression_level = 6; + + /** + * Name of request class used + * + * Can be set here to force a certain class, otherwise the factory method chooses one * * @var string */ - private $id; - + static public $request_class;// = 'etemplate_request_session'; + /** - * Enter description here... + * Factory method to get a new request object or the one for an existing request * - * @param array $id_data + * If mcrypt AND gzcompress is available this factory method chooses etemplate_request, + * which stores the request data encrypted in a hidden var directly in the form, + * over etemplate_request_session, which stores the data in the session (and causing + * the sesison to constantly grow). + * + * @param string $id=null * @return etemplate_request */ - function __construct($id=null) + public static function read($id=null) { - if (!$id) $id = self::request_id(); - - $this->id = $id; + if (is_null(self::$request_class)) + { + self::$request_class = extension_loaded('mcrypt') && function_exists('gzcompress') && + self::init_crypt() ? __CLASS__ : 'etemplate_request_session'; + } + if (self::$request_class != __CLASS__) + { + return call_user_method('read',self::$request_class,$id); + } + $request = new etemplate_request(); + + if (!is_null($id)) + { + $id = base64_decode($id); + + // decrypt the data if available + if (self::init_crypt()) + { + $id = trim(mdecrypt_generic(self::$mcrypt,$id)); + } + // uncompress the data if available + if (self::$compression_level && function_exists('gzcompress')) + { + //$len_compressed = bytes($id); + //$time = microtime(true); + $id = gzuncompress($id); + //$time = number_format(1000.0 * (microtime(true) - $time),1); + //$len_uncompressed = bytes($id); + //error_log(__METHOD__."() uncompressed from $len_compressed to $len_uncompressed bytes $time ms"); + } + $request->data = unserialize($id); + + if (!$request->data) + { + error_log(__METHOD__."() id not valid!"); + return false; + } + //error_log(__METHOD__."() size of request = ".bytes($id)); + } + return $request; } - + + /** + * Private constructor to force the instancation of this class only via it's static factory method read + * + * @param string $id=null + */ + private function __construct($id=null) + { + + } + /** * return the id of this request * * @return string */ - function id() + public function &id() { - return $this->id; - } - - /** - * Read the request via it's id, returns a request_object or false - * - * @param string $id - * @return etempalte_request|boolean the object or false if $id is not found - */ - static function read($id) - { - if (!($data = $GLOBALS['egw']->session->appsession($id,'etemplate'))) + $id = serialize($this->data); + + // compress the data if available + if (self::$compression_level && function_exists('gzcompress')) { - return false; // request not found + //$len_uncompressed = bytes($id); + //$time = microtime(true); + $id = gzcompress($id,self::$compression_level); + //$time = number_format(1000.0 * (microtime(true) - $time),1); + //$len_compressed = bytes($id); + //error_log(__METHOD__."() compressed from $len_uncompressed to $len_compressed bytes in $time ms"); } - $request = new etemplate_request($id); - $request->data = $data; - - return $request; + // encrypt the data if available + if (self::init_crypt()) + { + $id = mcrypt_generic(self::$mcrypt,$id); + } + $id = base64_encode($id); + + //error_log(__METHOD__."() #$this->id: size of request = ".bytes($id));//.", id='$id'"); + //self::debug(); + return $id; } - + /** * Register a form-variable to be processed * @@ -105,13 +194,36 @@ class etemplate_request public function set_to_process($form_name,$type,$data=array()) { if (!$form_name || !$type) return; - + $data['type'] = $type; - + $this->data['to_process'][$form_name] = $data; $this->data_modified = true; } - + + /** + * Set an attribute of a to-process record + * + * @param string $form_name form-name + * @param string $attribute etemplate type + * @param array $value + * @param boolean $add_to_array=false should $value be added to the attribute array + */ + public function set_to_process_attribute($form_name,$attribute,$value,$add_to_array=false) + { + if (!$form_name) return; + + if ($add_to_array) + { + $this->data['to_process'][$form_name][$attribute][] = $value; + } + else + { + $this->data['to_process'][$form_name][$attribute] = $value; + } + $this->data_modified = true; + } + /** * Unregister a form-variable to be no longer processed * @@ -122,7 +234,7 @@ class etemplate_request unset($this->data['to_process'][$form_name]); $this->data_modified = true; } - + /** * return the data of a form-var to process or the whole array * @@ -133,7 +245,7 @@ class etemplate_request { return $form_name ? $this->data['to_process'][$form_name] : $this->data['to_process']; } - + /** * check if something set for a given $form_name * @@ -144,14 +256,14 @@ class etemplate_request { return isset($this->data['to_process'][$form_name]); } - + /** * magic function to set all request-vars, used eg. as $request->method = 'app.class.method'; * * @param string $var * @param mixed $val */ - function __set($var,$val) + public function __set($var,$val) { if ($this->data[$var] !== $val) { @@ -159,90 +271,112 @@ class etemplate_request $this->data_modified = true; } } - + /** * magic function to access the request-vars, used eg. as $method = $request->method; * * @param string $var * @return mixed */ - function __get($var) + public function &__get($var) { return $this->data[$var]; } /** - * creates a new request-id via microtime() - * - * @return string + * Get the names / keys of existing variables + * + * @return array */ - static function request_id() + public function names() { - list($msec,$sec) = explode(' ',microtime()); - $time = 100 * $sec + (int)(100 * $msec); // gives precision of 1/100 sec - $id = $GLOBALS['egw_info']['flags']['currentapp'] .':'. $time; - - return $id; + return array_keys($this->data); } /** - * saves content,readonlys,template-keys, ... via eGW's appsession function + * Output the size-wise important parts of a request * - * As a user may open several windows with the same content/template wie generate a location-id from microtime - * which is used as location for request to descriminate between the different windows. This location-id - * is then saved as a hidden-var in the form. The above mentions session-id has nothing to do / is different - * from the session-id which is constant for all windows opened in one session. + * @param double $min_share minimum share to be reported (in percent of the whole request) + * @param double $dump_share minimum share from which on a variable get output */ - function __destruct() + public function debug($min_share=1.0,$dump_share=25.0) { - if ($this->data_modified) $GLOBALS['egw']->session->appsession($this->id,'etemplate',$this->data); - - if (substr($GLOBALS['egw_info']['server']['sessions_type'],0,4) == 'php4' && !$this->garbage_collection_done) + echo "

total size request data = ".($total=strlen(serialize($this->data)))."

\n"; + echo "

shares bigger then $min_share% percent of it:

\n"; + foreach($this->data as $key => $val) { - $this->_php4_request_garbage_collection(); - } - } - - /** - * a little bit of garbage collection for php4 sessions (their size is limited by memory_limit) - * - * With constant eTemplate use it can grow quite big and lead to unusable sessions (php terminates - * before any output with "Allowed memory size of ... exhausted"). - * We delete now sessions once used after 10min and sessions never or multiple used after 60min. - */ - private function _php4_request_garbage_collection() - { - // now we are on php4 sessions and do a bit of garbage collection - $appsessions =& $_SESSION[EGW_SESSION_VAR]['appsessions']['etemplate']; - $session_used =& $appsessions['session_used']; - - if ($this->id) - { - //echo "session_used[$id_used]='".$session_used[$id_used]."'
\n"; - ++$session_used[$this->id]; // count the number of times a session got used - } - $this->garbage_collection_done = true; - - if (count($appsessions) < 20) return; // we dont need to care - - list($msec,$sec) = explode(' ',microtime()); - $now = 100 * $sec + (int)(100 * $msec); // gives precision of 1/100 sec - - foreach(array_keys($appsessions) as $id) - { - list(,$time) = explode(':',$id); - - if (!$time) continue; // other data, no session - - //echo ++$n.') '.$id.': '.(($now-$time)/100.0)."secs old, used=".$session_used[$id].", size=".strlen($appsessions[$id])."
\n"; - - if ($session_used[$id] == 1 && $time < $now - 10*6000 || // session used and older then 10min - $time < $now - 60*6000) // session not used and older then 1h + $len = strlen(is_array($val) ? serialize($val) : $val); + $len .= ' ('.sprintf('%2.1lf',($percent = 100.0 * $len / $total)).'%)'; + if ($percent < $min_share) continue; + echo "

$key: strlen(\$val)=$len

\n"; + if ($percent >= $dump_share) _debug_array($val); + if (is_array($val) && $len > 2000) { - //echo "

boetemplate::php4_session_garbage_collection('$id_used'): unsetting session '$id' (now=$now)

\n"; - unset($appsessions[$id]); - unset($session_used[$id]); + foreach($val as $k => $v) + { + $l = strlen(is_array($v) ? serialize($v) : $v); + $l .= ' ('.sprintf('%2.1lf',($p = 100.0 * $l / $total)).'%)'; + if ($p < $min_share) continue; + echo "

 - {$key}[$k]: strlen(\$v)=$l

\n"; + } } } } + + /** + * Check if session encryption is configured, possible and initialise it + * + * @param string $algo='tripledes' + * @param string $mode='ecb' + * @return boolean true if encryption is used, false otherwise + */ + static public function init_crypt($algo='tripledes',$mode='ecb') + { + if (is_null(self::$mcrypt)) + { + if (isset($GLOBALS['egw_info']['server']['etemplate_form_key'])) + { + $key = $GLOBALS['egw_info']['server']['etemplate_form_key']; + } + else + { + $key = $GLOBALS['egw_info']['server']['db_pass'].EGW_SERVER_ROOT; + } + if (!extension_loaded('mcrypt') && (!function_exists('dl') || !@dl(PHP_SHLIB_PREFIX.'mcrypt'.'.'.PHP_SHLIB_SUFFIX))) + { + error_log(__METHOD__."() required PHP extension mcrypt not loaded and can not be loaded, eTemplate requests get NOT encrypted!"); + return false; + } + if (!(self::$mcrypt = mcrypt_module_open($algo, '', $mode, ''))) + { + error_log(__METHOD__."() could not mcrypt_module_open(algo='$algo','',mode='$mode',''), eTemplate requests get NOT encrypted!"); + return false; + } + $iv_size = mcrypt_enc_get_iv_size(self::$mcrypt); + $iv = !isset($GLOBALS['egw_info']['server']['mcrypt_iv']) || strlen($GLOBALS['egw_info']['server']['mcrypt_iv']) < $iv_size ? + mcrypt_create_iv ($iv_size, MCRYPT_RAND) : substr($GLOBALS['egw_info']['server']['mcrypt_iv'],0,$iv_size); + + $key_size = mcrypt_enc_get_key_size(self::$mcrypt); + if (strlen($key) > $key_size) $key = substr($key,0,$key_size); + + if (mcrypt_generic_init(self::$mcrypt,$key, $iv) < 0) + { + error_log(__METHOD__."() could not initialise mcrypt, sessions get NOT encrypted!"); + return self::$mcrypt = false; + } + } + return is_resource(self::$mcrypt); + } + + /** + * Destructor + */ + function __destruct() + { + if (self::$mcrypt) + { + mcrypt_generic_deinit(self::$mcrypt); + self::$mcrypt = null; + } + } } \ No newline at end of file diff --git a/etemplate/inc/class.etemplate_request_session.inc.php b/etemplate/inc/class.etemplate_request_session.inc.php new file mode 100644 index 0000000000..2db57d3cc6 --- /dev/null +++ b/etemplate/inc/class.etemplate_request_session.inc.php @@ -0,0 +1,171 @@ + + * @copyright (c) 2007-9 by Ralf Becker + * @version $Id$ + */ + +/** + * Class to represent the persitent information stored on the server for each eTemplate request + * + * The information is stored in the users session, which causes the session to constantly grow. + * We implement here some garbadge collection to remove old requests. + * + * The request object should be instancated only via the factory method etemplate_request::read($id=null) + * + * $request = etemplate_request::read(); + * + * // add request data + * + * $id = $request->id(); + * + * b) open or modify an existing request: + * + * if (!($request = etemplate_request::read($id))) + * { + * // request not found + * } + * + * Ajax requests can use this object to open the original request by using the id, they have to transmitt back, + * and register further variables, modify the registered ones or delete them AND then update the id, if it changed: + * + * if (($new_id = $request->id()) != $id) + * { + * $response->addAssign('etemplate_exec_id','value',$new_id); + * } + * + * For an example look in link_widget::ajax_search() + */ +class etemplate_request_session extends etemplate_request +{ + /** + * request id + * + * @var string + */ + protected $id; + + /** + * Private constructor to force the instancation of this class only via it's static factory method read + * + * @param array $id + */ + private function __construct($id=null) + { + if (!$id) $id = self::request_id(); + + $this->id = $id; + } + + /** + * return the id of this request + * + * @return string + */ + public function id() + { + //error_log(__METHOD__."() id=$this->id"); + return $this->id; + } + + /** + * Factory method to get a new request object or the one for an existing request + * + * @param string $id=null + * @return etemplate_request|boolean the object or false if $id is not found + */ + static function read($id=null) + { + $request = new etemplate_request_session($id); + + if (!is_null($id)) + { + if (!($data = $GLOBALS['egw']->session->appsession($id,'etemplate'))) + { + return false; // request not found + } + $request->data = $data; + } + //error_log(__METHOD__."(id=$id"); + return $request; + } + + /** + * creates a new request-id via microtime() + * + * @return string + */ + static function request_id() + { + $time = (int) (100 * microtime(true)); // gives precision of 1/100 sec + $id = $GLOBALS['egw_info']['flags']['currentapp'] .':'. $time; + + return $id; + } + + /** + * saves content,readonlys,template-keys, ... via eGW's appsession function + * + * As a user may open several windows with the same content/template wie generate a location-id from microtime + * which is used as location for request to descriminate between the different windows. This location-id + * is then saved as a hidden-var in the form. The above mentions session-id has nothing to do / is different + * from the session-id which is constant for all windows opened in one session. + */ + function __destruct() + { + if ($this->data_modified) $GLOBALS['egw']->session->appsession($this->id,'etemplate',$this->data); + + if (!$this->garbage_collection_done) + { + $this->_php4_request_garbage_collection(); + } + } + + /** + * a little bit of garbage collection for php4 sessions (their size is limited by memory_limit) + * + * With constant eTemplate use it can grow quite big and lead to unusable sessions (php terminates + * before any output with "Allowed memory size of ... exhausted"). + * We delete now sessions once used after 10min and sessions never or multiple used after 60min. + */ + protected function _php4_request_garbage_collection() + { + // now we are on php4 sessions and do a bit of garbage collection + $appsessions =& $_SESSION[egw_session::EGW_APPSESSION_VAR]['etemplate']; + $session_used =& $appsessions['session_used']; + + if ($this->id) + { + //echo "session_used[$id_used]='".$session_used[$id_used]."'
\n"; + ++$session_used[$this->id]; // count the number of times a session got used + } + $this->garbage_collection_done = true; + + if (count($appsessions) < 20) return; // we dont need to care + + $now = (int) (100 * microtime(true)); // gives precision of 1/100 sec + + foreach(array_keys($appsessions) as $id) + { + list(,$time) = explode(':',$id); + + if (!$time) continue; // other data, no session + + //echo ++$n.') '.$id.': '.(($now-$time)/100.0)."secs old, used=".$session_used[$id].", size=".strlen($appsessions[$id])."
\n"; + + if ($session_used[$id] == 1 && $time < $now - 10*6000 || // session used and older then 10min + $time < $now - 30*6000) // session not used and older then 30min + { + //echo "

boetemplate::php4_session_garbage_collection('$id_used'): unsetting session '$id' (now=$now)

\n"; + unset($appsessions[$id]); + unset($session_used[$id]); + } + } + } +} \ No newline at end of file diff --git a/etemplate/inc/class.link_widget.inc.php b/etemplate/inc/class.link_widget.inc.php index 02e69e0979..f9bb0919c9 100644 --- a/etemplate/inc/class.link_widget.inc.php +++ b/etemplate/inc/class.link_widget.inc.php @@ -7,6 +7,7 @@ * @subpackage extensions * @link http://www.egroupware.org * @author Ralf Becker + * @copyright 2002-9 by RalfBecker@outdoor-training.de * @version $Id$ */ @@ -114,6 +115,7 @@ class link_widget // readonly ==> omit the whole widget $value = ''; $cell = $tmpl->empty_cell(); + $extension_data = null; return; } if (!is_array($value) && in_array($type,array('link-to','link-list','link-add'))) @@ -133,6 +135,7 @@ class link_widget { case 'link': $cell['readonly'] = True; // set it readonly to NOT call our post_process function + $extension_data = null; $cell['no_lang'] = 1; $link = $target = $popup = ''; if (!is_array($value) && $value && isset($GLOBALS['egw_info']['apps'][$cell['size']])) @@ -152,7 +155,7 @@ class link_widget $link .= '&'.$var.'='.$val; } if (!($popup = egw_link::is_popup($value['app'],'view')) && - $GLOBALS['egw_info']['etemplate']['output_mode'] == 2) // we are in a popup + etemplate::$request->output_mode == 2) // we are in a popup { $target = '_blank'; } @@ -166,6 +169,7 @@ class link_widget { $cell = $tmpl->empty_cell(); $cell['readonly'] = True; // set it readonly to NOT call our post_process function + $extension_data = null; return; } $cell['type'] = 'label'; @@ -208,7 +212,7 @@ class link_widget list($w,$h) = explode('x',$popup); $options = ' onclick="window.open(this,this.target,\'width='.(int)$w.',height='.(int)$h.',location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes\'); return false;"'; } - elseif ($GLOBALS['egw_info']['etemplate']['output_mode'] == 2 || // we are in a popup + elseif (etemplate::$request->output_mode == 2 || // we are in a popup $link['app'] == egw_link::VFS_APPNAME) // or it's a link to an attachment { $options = ' target="_blank"'; @@ -220,6 +224,7 @@ class link_widget } $cell['type'] = 'html'; $cell['readonly'] = True; // set it readonly to NOT call our post_process function + $extension_data = null; $value = $str; return True; @@ -292,7 +297,7 @@ class link_widget { $value[$row]['view'] = egw_link::view($link['app'],$link['id'],$link); if (!($value[$row]['popup'] = egw_link::is_popup($link['app'],'view')) && - $GLOBALS['egw_info']['etemplate']['output_mode'] == 2) // we are in a popup + etemplate::$request->output_mode == 2) // we are in a popup { $value[$row]['target'] = '_blank'; // we create a new window as the linked page is no popup } @@ -656,12 +661,17 @@ class link_widget $response->addScript($script); } // store new allowed id's in the eT request - if ($etemplate_exec_id && ($et_request = etemplate_request::read($etemplate_exec_id))) + if ($etemplate_exec_id && ($request = etemplate_request::read($etemplate_exec_id))) { - $data = $et_request->get_to_process($id_res); + $data = $request->get_to_process($id_res); //error_log($id_res.'='.array2string($data)); $data['allowed'] = $found ? array_keys($found) : array(); - $et_request->set_to_process($id_res,$data); + $request->set_to_process($id_res,$data); + // update id, if request changed it (happens if the request data is stored direct in the form) + if ($etemplate_exec_id != ($new_id = $request->id())) + { + $response->addAssign('etemplate_exec_id','value',$new_id); + } } return $response->getXML(); } diff --git a/etemplate/inc/class.nextmatch_widget.inc.php b/etemplate/inc/class.nextmatch_widget.inc.php index 488f18cac1..440b0632e8 100644 --- a/etemplate/inc/class.nextmatch_widget.inc.php +++ b/etemplate/inc/class.nextmatch_widget.inc.php @@ -3,7 +3,7 @@ * eGroupWare eTemplate Extension - Nextmatch Widget * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @copyright 2002-8 by RalfBecker@outdoor-training.de + * @copyright 2002-9 by RalfBecker@outdoor-training.de * @package etemplate * @subpackage extensions * @link http://www.egroupware.org @@ -11,8 +11,6 @@ * @version $Id$ */ -require_once(EGW_INCLUDE_ROOT. '/etemplate/inc/class.etemplate.inc.php'); - /** * eTemplate Extension: Widget that show only a certain number of data-rows and allows to modifiy the rows shown (scroll). * @@ -313,7 +311,7 @@ class nextmatch_widget $rows = array(); if (!is_object($obj) || !method_exists($obj,$method)) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = "nextmatch_widget::pre_process($cell[name]): '$value[get_rows]' is no valid method !!!"; + etemplate::set_validation_error($name,"nextmatch_widget::pre_process($cell[name]): '$value[get_rows]' is no valid method !!!"); } else { @@ -393,7 +391,7 @@ class nextmatch_widget { // make each letter internally behave like a button $form_name = $name.'[searchletter]['.($key === 'all' ? $key : $letter).']'; - $GLOBALS['egw_info']['etemplate']['to_process'][$form_name] = 'button'; + etemplate::$request->set_to_process($form_name,'button'); if (!$key) $letterbox =& $lettersearch[1]; // to re-use the first child $letterbox = etemplate::empty_cell('label',$letter,array( @@ -402,13 +400,13 @@ class nextmatch_widget $key === 'all' && !$value['searchletter'] ? '_active' : ''), 'no_lang' => 2, 'align' => $key == 'all' ? 'right' : '', - 'onclick' => "return submitit($tmpl->name_form,'$form_name');", + 'onclick' => 'return submitit('.etemplate::$name_form.",'$form_name');", )); // if not the first (re-used) child, add it to the parent if ($key) etemplate::add_child($lettersearch,$letterbox); unset($letterbox); } - //_debug_array($GLOBALS['egw_info']['etemplate']['to_process']); + //_debug_array(etemplate::$request->to_process); } if(isset($value['no_search'])) $value['no_start_search'] = $value['no_search']; foreach(array('no_cat'=>'cat_id','no_filter'=>'filter','no_filter2'=>'filter2', 'no_search' => 'search', 'no_start_search' => 'start_search' ) as $val_name => $cell_name) @@ -896,7 +894,7 @@ class nextmatch_widget } if (!is_object($obj) || !method_exists($obj,$method)) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = "nextmatch_widget::pre_process($cell[name]): '$value[get_rows]' is no valid method !!!"; + etemplate::set_validation_error($name,"nextmatch_widget::pre_process($cell[name]): '$value[get_rows]' is no valid method !!!"); return false; } $this->charset = $this->charset_out = $GLOBALS['egw']->translation->charset(); @@ -922,7 +920,7 @@ class nextmatch_widget } if ($export_limit && (!is_numeric($export_limit) || $export_limit < $total)) { - $GLOBALS['egw_info']['etemplate']['validation_errors'][$name] = lang('You are not allowed to export more then %1 entries!',$export_limit); + etemplate::set_validation_error($name,lang('You are not allowed to export more then %1 entries!',$export_limit)); return false; } if (!isset($value['no_csv_support'])) $value['no_csv_support'] = !is_array($value['csv_fields']); diff --git a/etemplate/inc/class.select_widget.inc.php b/etemplate/inc/class.select_widget.inc.php index 3671948869..8113c0a8e1 100644 --- a/etemplate/inc/class.select_widget.inc.php +++ b/etemplate/inc/class.select_widget.inc.php @@ -1,555 +1,552 @@ + * @copyright 2002-9 by RalfBecker@outdoor-training.de + * @version $Id$ + */ + +/** + * eTemplate Extension: several select-boxes with predefined eGW specific content + * + * This widgets replaces the not longer exiting phpgwapi.sbox class. The widgets are independent of the UI, + * as they only uses etemplate-widgets and therefore have no render-function. + */ +class select_widget +{ /** - * eGroupWare eTemplate Extension - Select Widgets - * - * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License - * @package etemplate - * @link http://www.egroupware.org - * @author Ralf Becker - * @version $Id$ + * 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( + 'select-percent' => 'Select Percentage', + 'select-priority' => 'Select Priority', + 'select-access' => 'Select Access', + 'select-country' => 'Select Country', + 'select-state' => 'Select State', // US-states + 'select-cat' => 'Select Category', // Category-Selection, size: -1=Single+All, 0=Single, >0=Multiple with size lines + 'select-account' => 'Select Account', // label=accounts(default),groups,both + // size: -1=Single+not assigned, 0=Single, >0=Multiple + 'select-year' => 'Select Year', + 'select-month' => 'Select Month', + 'select-day' => 'Select Day', + 'select-dow' => 'Select Day of week', + 'select-hour' => 'Select Hour', // either 0-23 or 12am,1am-11am,12pm,1pm-11pm + 'select-number' => 'Select Number', + 'select-app' => 'Select Application', + 'select-lang' => 'Select Language', + ); + /** + * @var array + */ + var $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' + ); /** - * eTemplate Extension: several select-boxes with predefined eGW specific content + * Constructor of the extension * - * This widgets replaces the not longer exiting phpgwapi.sbox class. The widgets are independent of the UI, - * as they only uses etemplate-widgets and therefor have no render-function. - * - * @package etemplate - * @subpackage extensions - * @author RalfBecker-AT-outdoor-training.de - * @license GPL + * @param string $ui '' for html */ - class select_widget + function select_widget($ui) { - /** - * 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( - 'select-percent' => 'Select Percentage', - 'select-priority' => 'Select Priority', - 'select-access' => 'Select Access', - 'select-country' => 'Select Country', - 'select-state' => 'Select State', // US-states - 'select-cat' => 'Select Category', // Category-Selection, size: -1=Single+All, 0=Single, >0=Multiple with size lines - 'select-account' => 'Select Account', // label=accounts(default),groups,both - // size: -1=Single+not assigned, 0=Single, >0=Multiple - 'select-year' => 'Select Year', - 'select-month' => 'Select Month', - 'select-day' => 'Select Day', - 'select-dow' => 'Select Day of week', - 'select-hour' => 'Select Hour', // either 0-23 or 12am,1am-11am,12pm,1pm-11pm - 'select-number' => 'Select Number', - 'select-app' => 'Select Application', - 'select-lang' => 'Select Language', - ); - /** - * @var array - */ - var $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' - ); - - /** - * Constructor of the extension - * - * @param string $ui '' for html - */ - function select_widget($ui) + foreach($this->monthnames as $k => $name) { - foreach($this->monthnames as $k => $name) + if ($name) { - if ($name) - { - $this->monthnames[$k] = lang($name); - } + $this->monthnames[$k] = lang($name); } - $this->ui = $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 object &$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) + /** + * 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 object &$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,$type4) = explode(',',$cell['size']); + + $extension_data['type'] = $cell['type']; + + $readonly = $cell['readonly'] || $readonlys; + switch ($cell['type']) { - list($rows,$type,$type2,$type3,$type4) = explode(',',$cell['size']); + case 'select-percent': // options: #row,decrement(default=10) + $decr = $type > 0 ? $type : 10; + for ($i=0; $i <= 100; $i += $decr) + { + $cell['sel_options'][intval($i)] = intval($i).'%'; + } + $cell['sel_options'][100] = '100%'; + if (!$rows || !empty($value)) + { + $value = intval(($value+($decr/2)) / $decr) * $decr; + } + $cell['no_lang'] = True; + break; - $extension_data['type'] = $cell['type']; + case 'select-priority': + $cell['sel_options'] = array('','low','normal','high'); + break; - $readonly = $cell['readonly'] || $readonlys; - switch ($cell['type']) - { - case 'select-percent': // options: #row,decrement(default=10) - $decr = $type > 0 ? $type : 10; - for ($i=0; $i <= 100; $i += $decr) + case 'select-access': + $cell['sel_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 + $cell['sel_options'] = $GLOBALS['egw']->country->countries(); + + if (($extension_data['country_use_name'] = $type) && $value) + { + $value = $GLOBALS['egw']->country->country_code($value); + if (!isset($cell['sel_options'][$value])) { - $cell['sel_options'][intval($i)] = intval($i).'%'; - } - $cell['sel_options'][100] = '100%'; - if (!$rows || !empty($value)) - { - $value = intval(($value+($decr/2)) / $decr) * $decr; + $cell['sel_options'][$value] = $value; } + } + $cell['no_lang'] = True; + break; + + case 'select-state': + $cell['sel_options'] = $GLOBALS['egw']->country->us_states(); + $cell['no_lang'] = True; + break; + + case 'select-cat': // !$type == globals cats too, $type2: extraStyleMultiselect, $type3: application, if not current-app + 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; - break; - - case 'select-priority': - $cell['sel_options'] = array('','low','normal','high'); - break; - - case 'select-access': - $cell['sel_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 - $cell['sel_options'] = $GLOBALS['egw']->country->countries(); - - if (($extension_data['country_use_name'] = $type) && $value) + if ($value) { - $value = $GLOBALS['egw']->country->country_code($value); - if (!isset($cell['sel_options'][$value])) + if (!is_array($value)) $value = explode(',',$value); + foreach($value as $key => $id) { - $cell['sel_options'][$value] = $value; - } - } - $cell['no_lang'] = True; - break; - - case 'select-state': - $cell['sel_options'] = $GLOBALS['egw']->country->us_states(); - $cell['no_lang'] = True; - break; - - case 'select-cat': // !$type == globals cats too, $type2: extraStyleMultiselect, $type3: application, if not current-app - 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; - if ($value) - { - if (!is_array($value)) $value = explode(',',$value); - foreach($value as $key => $id) + if ($id && ($name = stripslashes($GLOBALS['egw']->categories->id2name($id))) && $name != '--') { - if ($id && ($name = stripslashes($GLOBALS['egw']->categories->id2name($id))) && $name != '--') - { - $cell['sel_options'][$id] = $name; - } - else - { - unset($value[$key]); // remove not (longer) existing or inaccessible cats - } + $cell['sel_options'][$id] = $name; + } + else + { + unset($value[$key]); // remove not (longer) existing or inaccessible cats } } - 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); - } - foreach((array)$categories->return_sorted_array(0,False,'','','',!$type) as $cat) - { - $s = str_repeat(' ',$cat['level']) . stripslashes($cat['name']); - - if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1') - { - $s .= ' ♦'; - } - if (!$tmpl->xslt) - { - $cell['sel_options'][$cat['id']] = $s; // 0.9.14 only - } - else - { - $cell['sel_options'][$cat['cat_id']] = $s; - } - } - // preserv unavailible cats (eg. private user-cats) - if ($value && ($unavailible = array_diff(is_array($value) ? $value : explode(',',$value),array_keys((array)$cell['sel_options'])))) - { - $extension_data['unavailible'] = $unavailible; - } - $cell['size'] = $rows.($type2 ? ','.$type2 : ''); - $cell['no_lang'] = True; 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); + } + foreach((array)$categories->return_sorted_array(0,False,'','','',!$type) as $cat) + { + $s = str_repeat(' ',$cat['level']) . stripslashes($cat['name']); - 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"; - if($type == 'owngroups') + if ($cat['app_name'] == 'phpgw' || $cat['owner'] == '-1') { - $type = 'groups'; - $owngroups = true; - foreach($GLOBALS['egw']->accounts->membership() as $group) $mygroups[] = $group['account_id']; + $s .= ' ♦'; } - // in case of readonly, we read/create only the needed entries, as reading accounts is expensive - if ($readonly) + if (!$tmpl->xslt) + { + $cell['sel_options'][$cat['id']] = $s; // 0.9.14 only + } + else + { + $cell['sel_options'][$cat['cat_id']] = $s; + } + } + // preserv unavailible cats (eg. private user-cats) + if ($value && ($unavailible = array_diff(is_array($value) ? $value : explode(',',$value),array_keys((array)$cell['sel_options'])))) + { + $extension_data['unavailible'] = $unavailible; + } + $cell['size'] = $rows.($type2 ? ','.$type2 : ''); + $cell['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"; + if($type == 'owngroups') + { + $type = 'groups'; + $owngroups = true; + foreach($GLOBALS['egw']->accounts->membership() as $group) $mygroups[] = $group['account_id']; + } + // in case of readonly, we read/create only the needed entries, as reading accounts is expensive + if ($readonly) + { + $cell['no_lang'] = True; + foreach(is_array($value) ? $value : (strpos($value,',') !== false ? explode(',',$value) : array($value)) as $id) + { + $cell['sel_options'][$id] = $this->accountInfo($id,$acc,$type2,$type=='both'); + } + break; + } + if ($this->ui == 'html' && $type != 'groups') // use eGW's new account-selection (html only) + { + $not = array_slice(explode(',',$cell['size']),4); + $help = (int)$cell['no_lang'] < 2 ? lang($cell['help']) : $cell['help']; + $onFocus = "self.status='".addslashes(htmlspecialchars($help))."'; return true;"; + $onBlur = "self.status=''; return true;"; + if ($cell['noprint']) { - $cell['no_lang'] = True; foreach(is_array($value) ? $value : (strpos($value,',') !== false ? explode(',',$value) : array($value)) as $id) { - $cell['sel_options'][$id] = $this->accountInfo($id,$acc,$type2,$type=='both'); + if ($id) $onlyPrint[] = $this->accountInfo($id,$acc,$type2,$type=='both'); } - break; + $onlyPrint = $onlyPrint ? implode('
',$onlyPrint) : lang((int)$rows < 0 ? 'all' : $rows); + $noPrint_class = ' class="noPrint"'; } - if ($this->ui == 'html' && $type != 'groups') // use eGW's new account-selection (html only) + if (($rows > 0 || $type3) && substr($name,-2) != '[]') $name .= '[]'; + $value = $GLOBALS['egw']->uiaccountsel->selection($name,'eT_accountsel_'.str_replace(array('[','][',']'),array('_','_',''),$name), + $value,$type,$rows > 0 ? $rows : ($type3 ? -$type3 : 0),$not,' onfocus="'.$onFocus.'" onblur="'.$onBlur.'"'.$noPrint_class, + $cell['onchange'] == '1' ? 'this.form.submit();' : $cell['onchange'], + !empty($rows) && 0+$rows <= 0 ? lang($rows < 0 ? 'all' : $rows) : False); + if ($cell['noprint']) { - $not = array_slice(explode(',',$cell['size']),4); - $help = (int)$cell['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[] = $this->accountInfo($id,$acc,$type2,$type=='both'); - } - $onlyPrint = $onlyPrint ? implode('
',$onlyPrint) : lang((int)$rows < 0 ? 'all' : $rows); - $noPrint_class = ' class="noPrint"'; - } - if (($rows > 0 || $type3) && substr($name,-2) != '[]') $name .= '[]'; - $value = $GLOBALS['egw']->uiaccountsel->selection($name,'eT_accountsel_'.str_replace(array('[','][',']'),array('_','_',''),$name), - $value,$type,$rows > 0 ? $rows : ($type3 ? -$type3 : 0),$not,' onfocus="'.$onFocus.'" onblur="'.$onBlur.'"'.$noPrint_class, - $cell['onchange'] == '1' ? 'this.form.submit();' : $cell['onchange'], - !empty($rows) && 0+$rows <= 0 ? lang($rows < 0 ? 'all' : $rows) : False); - if ($cell['noprint']) - { - $value = ''.$onlyPrint.''.$value; - } - $cell['type'] = 'html'; - $cell['size'] = ''; // is interpreted as link otherwise - $GLOBALS['egw_info']['etemplate']['to_process'][$name] = 'select'; - break; - } - $cell['no_lang'] = True; - $accs = $GLOBALS['egw']->accounts->get_list(empty($type) ? 'accounts' : $type); // default is accounts - foreach($accs as $acc) - { - if ($acc['account_type'] == 'u') - { - $cell['sel_options'][$acc['account_id']] = $this->accountInfo($acc['account_id'],$acc,$type2,$type=='both'); - } - } - foreach($accs as $acc) - { - if ($acc['account_type'] == 'g' && (!$owngroups || ($owngroups && in_array($acc['account_id'],(array)$mygroups)))) - { - $cell['sel_options'][$acc['account_id']] = $this->accountInfo($acc['account_id'],$acc,$type2,$type=='both'); - } + $value = ''.$onlyPrint.''.$value; } + $cell['type'] = 'html'; + $cell['size'] = ''; // is interpreted as link otherwise + etemplate::$request->set_to_process($name,'select'); break; - - case 'select-year': // options: #rows,#before(default=3),#after(default=2) - $cell['sel_options'][''] = ''; - if ($type <= 0) $type = 3; - if ($type2 <= 0) $type2 = 2; - if ($type > 100 && $type2 > 100 && $type > $type) { $y = $type; $type=$type2; $type2=$y; } - $y = date('Y')-$type; - if ($value && $value-$type < $y || $type > 100) $y = $type > 100 ? $type : $value-$type; - $to = date('Y')+$type2; - if ($value && $value+$type2 > $to || $type2 > 100) $to = $type2 > 100 ? $type2 : $value+$type2; - for ($n = 0; $y <= $to && $n < 200; ++$n) - { - $cell['sel_options'][$y] = $y++; - } - $cell['no_lang'] = True; - break; - - case 'select-month': - $cell['sel_options'] = $this->monthnames; - $value = intval($value); - break; - - case 'select-dow': // options: rows[,0=summaries befor days, 1=summaries after days, 2=no summaries[,extraStyleMultiselect]] - if (!defined('MCAL_M_SUNDAY')) - { - define('MCAL_M_SUNDAY',1); - define('MCAL_M_MONDAY',2); - define('MCAL_M_TUESDAY',4); - define('MCAL_M_WEDNESDAY',8); - define('MCAL_M_THURSDAY',16); - define('MCAL_M_FRIDAY',32); - define('MCAL_M_SATURDAY',64); - - define('MCAL_M_WEEKDAYS',62); - define('MCAL_M_WEEKEND',65); - define('MCAL_M_ALLDAYS',127); - } - $weekstart = $GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts']; - $cell['sel_options'] = array(); - if ($rows >= 2 && !$type) - { - $cell['sel_options'] = array( - MCAL_M_ALLDAYS => 'all days', - MCAL_M_WEEKDAYS => 'working days', - MCAL_M_WEEKEND => 'weekend', - ); - } - if ($weekstart == 'Saturday') $cell['sel_options'][MCAL_M_SATURDAY] = 'saturday'; - if ($weekstart != 'Monday') $cell['sel_options'][MCAL_M_SUNDAY] = 'sunday'; - $cell['sel_options'] += array( - MCAL_M_MONDAY => 'monday', - MCAL_M_TUESDAY => 'tuesday', - MCAL_M_WEDNESDAY=> 'wednesday', - MCAL_M_THURSDAY => 'thursday', - MCAL_M_FRIDAY => 'friday', - ); - if ($weekstart != 'Saturday') $cell['sel_options'][MCAL_M_SATURDAY] = 'saturday'; - if ($weekstart == 'Monday') $cell['sel_options'][MCAL_M_SUNDAY] = 'sunday'; - if ($rows >= 2 && $type == 1) - { - $cell['sel_options'] += array( - MCAL_M_ALLDAYS => 'all days', - MCAL_M_WEEKDAYS => 'working days', - MCAL_M_WEEKEND => 'weekend', - ); - } - $value_in = $value; - $value = array(); - foreach($cell['sel_options'] as $val => $lable) - { - if (($value_in & $val) == $val) - { - $value[] = $val; - - if ($val == MCAL_M_ALLDAYS || - $val == MCAL_M_WEEKDAYS && $value_in == MCAL_M_WEEKDAYS || - $val == MCAL_M_WEEKEND && $value_in == MCAL_M_WEEKEND) - { - break; // dont set the others - } - } - } - if (!$readonly) - { - $GLOBALS['egw_info']['etemplate']['to_process'][$name] = 'ext-select-dow'; - } - $cell['size'] = $rows.($type2 ? ','.$type2 : ''); - break; - - case 'select-day': - $type = 1; - $type2 = 31; - $type3 = 1; - // fall-through - - case 'select-number': // options: rows,min,max,decrement,suffix - $type = $type === '' ? 1 : intval($type); // min - $type2 = $type2 === '' ? 10 : intval($type2); // max - $format = '%d'; - if (!empty($type3) && $type3[0] == '0') // leading zero - { - $format = '%0'.strlen($type3).'d'; - } - $type3 = !$type3 ? 1 : intval($type3); // decrement - if (($type <= $type2) != ($type3 > 0)) - { - $type3 = -$type3; // void infinite loop - } - if (!empty($type4)) $format .= lang($type4); - for ($i=0,$n=$type; $n <= $type2 && $i <= 100; $n += $type3,++$i) - { - $cell['sel_options'][$n] = sprintf($format,$n); - } - $cell['no_lang'] = True; - break; - - case 'select-hour': - for ($h = 0; $h <= 23; ++$h) - { - $cell['sel_options'][$h] = $GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] == 12 ? - (($h % 12 ? $h % 12 : 12).' '.($h < 12 ? lang('am') : lang('pm'))) : - sprintf('%02d',$h); - } - $cell['no_lang'] = True; - break; - - case 'select-app': // type2: ''=users enabled apps, 'installed', 'all' = not installed ones too - $apps = array(); - foreach ($GLOBALS['egw_info']['apps'] as $app => $data) - { - if (!$type2 || $GLOBALS['egw_info']['user']['apps'][$app]) - { - $apps[$app] = $data['title'] ? $data['title'] : lang($app); - } - } - if ($type2 == 'all') - { - $dir = opendir(EGW_SERVER_ROOT); - while ($file = readdir($dir)) - { - if (@is_dir(EGW_SERVER_ROOT."/$file/setup") && $file[0] != '.' && - !isset($apps[$app = basename($file)])) - { - $apps[$app] = $app . ' (*)'; - } - } - closedir($dir); - } - $apps_lower = $apps; // case-in-sensitve sort - foreach ($apps_lower as $app => $title) - { - $apps_lower[$app] = strtolower($title); - } - asort($apps_lower); - foreach ($apps_lower as $app => $title) - { - $cell['sel_options'][$app] = $apps[$app]; - } - break; - case 'select-lang': - $cell['sel_options'] = $GLOBALS['egw']->translation->list_langs(); - $cell['no_lang'] = True; - break; - } - if ($rows > 1) - { - unset($cell['sel_options']['']); - } - return True; // extra Label Ok - } - - /** - * internal function to format account-data - */ - function accountInfo($id,$acc=0,$longnames=0,$show_type=0) - { - if (!$id) - { - return ' '; - } - - if (!is_array($acc)) - { - $data = $GLOBALS['egw']->accounts->get_account_data($id); - if (!isset($data[$id])) return '#'.$id; - foreach(array('type','lid','firstname','lastname') as $name) - { - $acc['account_'.$name] = $data[$id][$name]; } - } - $info = $show_type ? '('.$acc['account_type'].') ' : ''; - - if ($acc['account_type'] == 'g') - { - $longnames = 1; - } - switch ($longnames) - { - case 2: - $info .= '<'.$acc['account_lid'].'> '; - // fall-through - case 1: - $info .= $acc['account_type'] == 'g' ? lang('group').' '.$acc['account_lid'] : - $acc['account_firstname'].' '.$acc['account_lastname']; - break; - case '0': - $info .= $acc['account_lid']; - break; - default: // use the phpgw default - $info = $GLOBALS['egw']->common->display_fullname($acc['account_lid'], - $acc['account_firstname'],$acc['account_lastname']); - break; - } - return $info; - } - - /** - * 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) - { - switch ($extension_data['type']) - { - case 'select-cat': - $value = $value_in; - // check if we have some unavailible cats and add them again - if (is_array($extension_data['unavailible']) && $extension_data['unavailible']) + $cell['no_lang'] = True; + $accs = $GLOBALS['egw']->accounts->get_list(empty($type) ? 'accounts' : $type); // default is accounts + foreach($accs as $acc) + { + if ($acc['account_type'] == 'u') { - if (is_array($value)) // multiselection - { - $value = array_merge($value,$extension_data['unavailible']); - } - elseif (!$value) // non-multiselection and nothing selected by the user - { - $value = $extension_data['unavailible'][0]; - } + $cell['sel_options'][$acc['account_id']] = $this->accountInfo($acc['account_id'],$acc,$type2,$type=='both'); } - break; + } + foreach($accs as $acc) + { + if ($acc['account_type'] == 'g' && (!$owngroups || ($owngroups && in_array($acc['account_id'],(array)$mygroups)))) + { + $cell['sel_options'][$acc['account_id']] = $this->accountInfo($acc['account_id'],$acc,$type2,$type=='both'); + } + } + break; - case 'select-dow': - $value = 0; - if (!is_array($value_in)) $value_in = explode(',',$value_in); - foreach($value_in as $val) + case 'select-year': // options: #rows,#before(default=3),#after(default=2) + $cell['sel_options'][''] = ''; + if ($type <= 0) $type = 3; + if ($type2 <= 0) $type2 = 2; + if ($type > 100 && $type2 > 100 && $type > $type) { $y = $type; $type=$type2; $type2=$y; } + $y = date('Y')-$type; + if ($value && $value-$type < $y || $type > 100) $y = $type > 100 ? $type : $value-$type; + $to = date('Y')+$type2; + if ($value && $value+$type2 > $to || $type2 > 100) $to = $type2 > 100 ? $type2 : $value+$type2; + for ($n = 0; $y <= $to && $n < 200; ++$n) + { + $cell['sel_options'][$y] = $y++; + } + $cell['no_lang'] = True; + break; + + case 'select-month': + $cell['sel_options'] = $this->monthnames; + $value = intval($value); + break; + + case 'select-dow': // options: rows[,0=summaries befor days, 1=summaries after days, 2=no summaries[,extraStyleMultiselect]] + if (!defined('MCAL_M_SUNDAY')) + { + define('MCAL_M_SUNDAY',1); + define('MCAL_M_MONDAY',2); + define('MCAL_M_TUESDAY',4); + define('MCAL_M_WEDNESDAY',8); + define('MCAL_M_THURSDAY',16); + define('MCAL_M_FRIDAY',32); + define('MCAL_M_SATURDAY',64); + + define('MCAL_M_WEEKDAYS',62); + define('MCAL_M_WEEKEND',65); + define('MCAL_M_ALLDAYS',127); + } + $weekstart = $GLOBALS['egw_info']['user']['preferences']['calendar']['weekdaystarts']; + $cell['sel_options'] = array(); + if ($rows >= 2 && !$type) + { + $cell['sel_options'] = array( + MCAL_M_ALLDAYS => 'all days', + MCAL_M_WEEKDAYS => 'working days', + MCAL_M_WEEKEND => 'weekend', + ); + } + if ($weekstart == 'Saturday') $cell['sel_options'][MCAL_M_SATURDAY] = 'saturday'; + if ($weekstart != 'Monday') $cell['sel_options'][MCAL_M_SUNDAY] = 'sunday'; + $cell['sel_options'] += array( + MCAL_M_MONDAY => 'monday', + MCAL_M_TUESDAY => 'tuesday', + MCAL_M_WEDNESDAY=> 'wednesday', + MCAL_M_THURSDAY => 'thursday', + MCAL_M_FRIDAY => 'friday', + ); + if ($weekstart != 'Saturday') $cell['sel_options'][MCAL_M_SATURDAY] = 'saturday'; + if ($weekstart == 'Monday') $cell['sel_options'][MCAL_M_SUNDAY] = 'sunday'; + if ($rows >= 2 && $type == 1) + { + $cell['sel_options'] += array( + MCAL_M_ALLDAYS => 'all days', + MCAL_M_WEEKDAYS => 'working days', + MCAL_M_WEEKEND => 'weekend', + ); + } + $value_in = $value; + $value = array(); + foreach($cell['sel_options'] as $val => $lable) + { + if (($value_in & $val) == $val) { - $value |= $val; - } - //echo "

select_widget::post_process('$name',...,'$value_in'): value='$value'

\n"; - break; - case 'select-country': - if ($extension_data['country_use_name'] && $value_in) - { - if (($value = $GLOBALS['egw']->country->get_full_name($value_in))) + $value[] = $val; + + if ($val == MCAL_M_ALLDAYS || + $val == MCAL_M_WEEKDAYS && $value_in == MCAL_M_WEEKDAYS || + $val == MCAL_M_WEEKEND && $value_in == MCAL_M_WEEKEND) { - break; + break; // dont set the others } } - // fall through - default: - $value = $value_in; - break; - } - //echo "

select_widget::post_process('$name',,'$extension_data',,,'$value_in'): value='$value', is_null(value)=".(int)is_null($value)."

\n"; - return true; + } + if (!$readonly) + { + etemplate::$request->set_to_process($name,'ext-select-dow'); + } + $cell['size'] = $rows.($type2 ? ','.$type2 : ''); + break; + + case 'select-day': + $type = 1; + $type2 = 31; + $type3 = 1; + // fall-through + + case 'select-number': // options: rows,min,max,decrement,suffix + $type = $type === '' ? 1 : intval($type); // min + $type2 = $type2 === '' ? 10 : intval($type2); // max + $format = '%d'; + if (!empty($type3) && $type3[0] == '0') // leading zero + { + $format = '%0'.strlen($type3).'d'; + } + $type3 = !$type3 ? 1 : intval($type3); // decrement + if (($type <= $type2) != ($type3 > 0)) + { + $type3 = -$type3; // void infinite loop + } + if (!empty($type4)) $format .= lang($type4); + for ($i=0,$n=$type; $n <= $type2 && $i <= 100; $n += $type3,++$i) + { + $cell['sel_options'][$n] = sprintf($format,$n); + } + $cell['no_lang'] = True; + break; + + case 'select-hour': + for ($h = 0; $h <= 23; ++$h) + { + $cell['sel_options'][$h] = $GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] == 12 ? + (($h % 12 ? $h % 12 : 12).' '.($h < 12 ? lang('am') : lang('pm'))) : + sprintf('%02d',$h); + } + $cell['no_lang'] = True; + break; + + case 'select-app': // type2: ''=users enabled apps, 'installed', 'all' = not installed ones too + $apps = array(); + foreach ($GLOBALS['egw_info']['apps'] as $app => $data) + { + if (!$type2 || $GLOBALS['egw_info']['user']['apps'][$app]) + { + $apps[$app] = $data['title'] ? $data['title'] : lang($app); + } + } + if ($type2 == 'all') + { + $dir = opendir(EGW_SERVER_ROOT); + while ($file = readdir($dir)) + { + if (@is_dir(EGW_SERVER_ROOT."/$file/setup") && $file[0] != '.' && + !isset($apps[$app = basename($file)])) + { + $apps[$app] = $app . ' (*)'; + } + } + closedir($dir); + } + $apps_lower = $apps; // case-in-sensitve sort + foreach ($apps_lower as $app => $title) + { + $apps_lower[$app] = strtolower($title); + } + asort($apps_lower); + foreach ($apps_lower as $app => $title) + { + $cell['sel_options'][$app] = $apps[$app]; + } + break; + case 'select-lang': + $cell['sel_options'] = $GLOBALS['egw']->translation->list_langs(); + $cell['no_lang'] = True; + break; } + if ($rows > 1) + { + unset($cell['sel_options']['']); + } + return True; // extra Label Ok } + + /** + * internal function to format account-data + */ + function accountInfo($id,$acc=0,$longnames=0,$show_type=0) + { + if (!$id) + { + return ' '; + } + + if (!is_array($acc)) + { + $data = $GLOBALS['egw']->accounts->get_account_data($id); + if (!isset($data[$id])) return '#'.$id; + foreach(array('type','lid','firstname','lastname') as $name) + { + $acc['account_'.$name] = $data[$id][$name]; + } + } + $info = $show_type ? '('.$acc['account_type'].') ' : ''; + + if ($acc['account_type'] == 'g') + { + $longnames = 1; + } + switch ($longnames) + { + case 2: + $info .= '<'.$acc['account_lid'].'> '; + // fall-through + case 1: + $info .= $acc['account_type'] == 'g' ? lang('group').' '.$acc['account_lid'] : + $acc['account_firstname'].' '.$acc['account_lastname']; + break; + case '0': + $info .= $acc['account_lid']; + break; + default: // use the phpgw default + $info = $GLOBALS['egw']->common->display_fullname($acc['account_lid'], + $acc['account_firstname'],$acc['account_lastname']); + break; + } + return $info; + } + + /** + * 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) + { + switch ($extension_data['type']) + { + case 'select-cat': + $value = $value_in; + // check if we have some unavailible cats and add them again + if (is_array($extension_data['unavailible']) && $extension_data['unavailible']) + { + if (is_array($value)) // multiselection + { + $value = array_merge($value,$extension_data['unavailible']); + } + elseif (!$value) // non-multiselection and nothing selected by the user + { + $value = $extension_data['unavailible'][0]; + } + } + break; + + case 'select-dow': + $value = 0; + if (!is_array($value_in)) $value_in = explode(',',$value_in); + foreach($value_in as $val) + { + $value |= $val; + } + //echo "

select_widget::post_process('$name',...,'$value_in'): value='$value'

\n"; + break; + case 'select-country': + if ($extension_data['country_use_name'] && $value_in) + { + if (($value = $GLOBALS['egw']->country->get_full_name($value_in))) + { + break; + } + } + // fall through + default: + $value = $value_in; + break; + } + //echo "

select_widget::post_process('$name',,'$extension_data',,,'$value_in'): value='$value', is_null(value)=".(int)is_null($value)."

\n"; + return true; + } +} diff --git a/etemplate/inc/class.soetemplate.inc.php b/etemplate/inc/class.soetemplate.inc.php index d215158350..9c107a575f 100644 --- a/etemplate/inc/class.soetemplate.inc.php +++ b/etemplate/inc/class.soetemplate.inc.php @@ -4,7 +4,7 @@ * * @link http://www.egroupware.org * @author Ralf Becker - * @copyright 2002-8 by RalfBecker@outdoor-training.de + * @copyright 2002-9 by RalfBecker@outdoor-training.de * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package etemplate * @subpackage api @@ -30,20 +30,20 @@ */ class soetemplate { - var $debug; // =1 show some debug-messages, = 'app.name' show messages only for eTemplate 'app.name' - var $name; // name of the template, e.g. 'infolog.edit' - var $template; // '' = default (not 'default') - var $lang; // '' if general template else language short, e.g. 'de' - var $group; // 0 = not specific else groupId or if < 0 userId - var $version; // like 0.9.13.001 - var $style; // embeded CSS style-sheet - var $children; // array with children - var $data; // depricated: first grid of the children - var $size; // depricated: witdh,height,border of first grid + public $debug; // =1 show some debug-messages, = 'app.name' show messages only for eTemplate 'app.name' + public $name; // name of the template, e.g. 'infolog.edit' + public $template; // '' = default (not 'default') + public $lang; // '' if general template else language short, e.g. 'de' + public $group; // 0 = not specific else groupId or if < 0 userId + public $version; // like 0.9.13.001 + public $style; // embeded CSS style-sheet + public $children; // array with children + public $data; // depricated: first grid of the children + public $size; // depricated: witdh,height,border of first grid /** * private reference to the global db-object * - * @var egw_db + * @public egw_db */ private $db; /** @@ -68,7 +68,7 @@ class soetemplate * widgets that contain other widgets, eg. for tree_walk method * widget-type is the key, the value specifys how the children are stored. * - * @var array + * @public array */ static $widgets_with_children = array( 'template' => 'template', @@ -94,9 +94,9 @@ class soetemplate * @param int $cols initial size of the template, default 1, only used if no name given !!! * @return soetemplate */ - function soetemplate($name='',$template='',$lang='',$group=0,$version='',$rows=1,$cols=1) + function __construct($name='',$template='',$lang='',$group=0,$version='',$rows=1,$cols=1) { - if (is_object($GLOBALS['egw']->db)) + if (isset($GLOBALS['egw']->db)) { $this->db = $GLOBALS['egw']->db; } @@ -674,6 +674,8 @@ class soetemplate $this->set_rows_cols(); } + static private $compress_array_recursion = array(); + /** * all empty values and objects in the array got unset (to save space in the db ) * @@ -686,6 +688,8 @@ class soetemplate */ function compress_array($arr,$remove_objs=false) { + static $recursion = array(); + if (!is_array($arr)) { return $arr; @@ -702,7 +706,7 @@ class soetemplate } elseif (!$remove_objs && $key == 'obj' && is_object($val) && method_exists($val,'as_array') && // this test prevents an infinit recursion of templates calling itself, atm. etemplate.editor.new - $GLOBALS['egw_info']['etemplate']['as_array'][$this->name]++ < 2) + self::$compress_array_recursion[$this->name]++ < 2) { $arr['obj'] = $val->as_array(2); } @@ -1077,6 +1081,8 @@ class soetemplate return lang("%1 new eTemplates imported for Application '%2'",$n,$app); } + static private $import_tested = array(); + /** * test if new template-import necessary for app and does the import * @@ -1090,11 +1096,11 @@ class soetemplate { list($app) = explode('.',$app); - if (!$app || $GLOBALS['egw_info']['etemplate']['import_tested'][$app]) + if (!$app || self::$import_tested[$app]) { return ''; // ensure test is done only once per call and app } - $GLOBALS['egw_info']['etemplate']['import_tested'][$app] = True; // need to be done before new ... + self::$import_tested[$app] = True; // need to be done before new ... $path = EGW_SERVER_ROOT."/$app/setup/etemplates.inc.php"; diff --git a/etemplate/inc/class.tab_widget.inc.php b/etemplate/inc/class.tab_widget.inc.php index 17acea5225..2d24624263 100644 --- a/etemplate/inc/class.tab_widget.inc.php +++ b/etemplate/inc/class.tab_widget.inc.php @@ -9,7 +9,7 @@ * @author Ralf Becker * @version $Id$ */ - + /** * eTemplate Extension: widget that shows one row of tabs and an other row with the eTemplate of the selected tab * @@ -19,7 +19,7 @@ */ class tab_widget { - /** + /** * exported methods of this class * @var array */ @@ -32,7 +32,7 @@ * @var string */ var $human_name = 'Tabs'; // this is the name for the editor - + /** * Constructor of the extension * @@ -41,7 +41,7 @@ function tab_widget($ui) { } - + /** * pre-processing of the extension * @@ -49,7 +49,7 @@ * * @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 &$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 object &$tmpl reference to the template we belong too @@ -58,10 +58,10 @@ function pre_process($form_name,&$value,&$cell,&$readonlys,&$extension_data,&$tmpl) { //echo "

tab_widget::pre_process('$form_name',$value,,$extension_data)

\n"; - + if (!$cell['onchange']) // onchange allows to use the old behavior (submit for each new tab) { - $dom_enabled = isset($GLOBALS['egw_info']['etemplate']['dom_enabled']) ? $GLOBALS['egw_info']['etemplate']['dom_enabled'] : true; + $dom_enabled = true; } $labels = explode('|',$cell['label']); $helps = explode('|',$cell['help']); @@ -85,10 +85,10 @@ } } $all_names = implode('|',$names); - + $tab_widget =& new etemplate('etemplate.tab_widget'); $tab_widget->no_onclick = true; - + if ($value && strpos($value,'.') === false) { $value = $tmpl->name . '.' . $value; @@ -109,7 +109,7 @@ $value = $selected_tab = $names[0]; } $extension_data = $value; // remember the active tab in the extension_data - + foreach($names as $k => $name) { if (strpos($name,'.') === false) @@ -138,13 +138,13 @@ } $tcell['label'] = $labels[$k]; $tcell['help'] = $helps[$k]; - + $tab_widget->set_cell_attribute('tabs',1+$k,$tcell); } $tab_widget->set_cell_attribute('tabs','type','hbox'); $tab_widget->set_cell_attribute('tabs','size',count($names)); $tab_widget->set_cell_attribute('tabs','name',''); - + if ($dom_enabled) { foreach($names as $n => $name) @@ -166,14 +166,14 @@ $tab_widget->set_cell_attribute('body','obj',$stab); } $tab_widget->set_cell_attribute('body','name',$selected_tab); - + $cell['type'] = 'template'; $cell['obj'] = &$tab_widget; $cell['label'] = $cell['help'] = ''; - + return False; // NO extra Label } - + /** * postprocessing method, called after the submission of the form * @@ -194,7 +194,7 @@ function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in) { //echo "

tab_widget::post_process($name): value_in = "; _debug_array($value_in); - + if (is_array($value_in)) { foreach ($value_in as $tab => $button_pressed) @@ -213,7 +213,7 @@ // if value not set (other button pressed), set the value we remembered in the extension_data if (!$value) { - $value = $extension_data; + $value = $extension_data; } return True; } diff --git a/etemplate/inc/class.uietemplate_gtk.inc.php b/etemplate/inc/class.uietemplate_gtk.inc.php index 5555b2dc3b..7a5e1128f4 100644 --- a/etemplate/inc/class.uietemplate_gtk.inc.php +++ b/etemplate/inc/class.uietemplate_gtk.inc.php @@ -24,7 +24,7 @@ * @param $debug enables debug messages: 0=no, 1=calls to show and process_show, 2=content of process_show * @param 3=calls to show_cell OR template- or cell-type name */ - class etemplate extends boetemplate + class gtk_etemplate extends boetemplate { var $debug;//='etemplate.editor.edit'; // 1=calls to show and process_show, 2=content after process_show, // 3=calls to show_cell and process_show_cell, or template-name or cell-type diff --git a/etemplate/inc/class.xul_io.inc.php b/etemplate/inc/class.xul_io.inc.php index 374367194b..91532518b6 100644 --- a/etemplate/inc/class.xul_io.inc.php +++ b/etemplate/inc/class.xul_io.inc.php @@ -508,7 +508,7 @@ // save tmpl to the cache, as the file may contain more then one tmpl $cname = ($etempl->template == '' ? 'default' : $etempl->template).'/'.$etempl->name. ($etempl->lang == '' ? '' : '.'.$etempl->lang); - $GLOBALS['egw_info']['etemplate']['cache'][$cname] = $etempl->as_array(1); + boetemplate::$template_cache[$cname] = $etempl->as_array(1); if ($this->debug) { $etempl->echo_tmpl();