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();