From 06ba0970820bf645c6cf75e97cd900bd3afb0a7c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Tue, 27 Jul 2010 19:38:01 +0000 Subject: [PATCH 01/88] Fix unknown qualifier issue for contact fields --- addressbook/inc/class.addressbook_vcal.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/addressbook/inc/class.addressbook_vcal.inc.php b/addressbook/inc/class.addressbook_vcal.inc.php index 24eb931972..0850b97bb4 100644 --- a/addressbook/inc/class.addressbook_vcal.inc.php +++ b/addressbook/inc/class.addressbook_vcal.inc.php @@ -696,6 +696,7 @@ class addressbook_vcal extends addressbook_bo case 'VOICE': case 'OTHER': case 'CELL': + if ($rowName != 'TEL') break; case 'WORK': case 'HOME': $rowName .= ';' . $pname; From 77982bb6462acc358a9229667b6d9ee10e6b339c Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 07:49:38 +0000 Subject: [PATCH 02/88] fixed typo --- setup/inc/class.setup_translation.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/inc/class.setup_translation.inc.php b/setup/inc/class.setup_translation.inc.php index dd1d6ba3ef..1e407be57d 100644 --- a/setup/inc/class.setup_translation.inc.php +++ b/setup/inc/class.setup_translation.inc.php @@ -103,17 +103,17 @@ class setup_translation function get_langs($DEBUG=False) { - return translaton::get_langs($DEBUG); + return translation::get_langs($DEBUG); } function drop_langs($appname,$DEBUG=False) { - return translaton::drop_langs($appname,$DEBUG); + return translation::drop_langs($appname,$DEBUG); } function add_langs($appname,$DEBUG=False,$force_langs=False) { - return translaton::add_langs($appname,$DEBUG,$force_langs); + return translation::add_langs($appname,$DEBUG,$force_langs); } function drop_add_all_langs($langs=False) From b71035b529cb7a826ed55b84a62411f0483ab104 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 08:14:15 +0000 Subject: [PATCH 03/88] marking all static methods static and moving method to query supported languages into setup_translation class --- setup/inc/class.setup_translation.inc.php | 57 +++++++++++++++++++++-- setup/lang.php | 3 +- 2 files changed, 53 insertions(+), 7 deletions(-) diff --git a/setup/inc/class.setup_translation.inc.php b/setup/inc/class.setup_translation.inc.php index 1e407be57d..8bf02b0135 100644 --- a/setup/inc/class.setup_translation.inc.php +++ b/setup/inc/class.setup_translation.inc.php @@ -101,22 +101,22 @@ class setup_translation return $ret; } - function get_langs($DEBUG=False) + static function get_langs($DEBUG=False) { return translation::get_langs($DEBUG); } - function drop_langs($appname,$DEBUG=False) + static function drop_langs($appname,$DEBUG=False) { return translation::drop_langs($appname,$DEBUG); } - function add_langs($appname,$DEBUG=False,$force_langs=False) + static function add_langs($appname,$DEBUG=False,$force_langs=False) { return translation::add_langs($appname,$DEBUG,$force_langs); } - function drop_add_all_langs($langs=False) + static function drop_add_all_langs($langs=False) { if (!$langs && !count($langs = translation::get_langs())) { @@ -125,13 +125,60 @@ class setup_translation return translation::install_langs($langs,'dumpold'); } + /** + * Languages we support (alphabetically sorted) + * + * @param boolean $array_values=true true: values are an array, false values are just the descriptiong + * @return array + */ + static function get_supported_langs($array_values=true) + { + $f = fopen(EGW_SERVER_ROOT.'/setup/lang/languages','rb'); + while(($line = fgets($f))) + { + list($lang,$descr) = explode("\t",$line,2); + $lang = trim($lang); + if ($array_values) + { + $languages[$lang]['lang'] = $lang; + $languages[$lang]['descr'] = trim($descr); + $languages[$lang]['available'] = False; + } + else + { + $languages[$lang] = trim($descr); + } + } + fclose($f); + + if ($array_values) + { + $d = dir(EGW_SERVER_ROOT.'/setup/lang'); + while(($file = $d->read())) + { + if(preg_match('/^(php|e)gw_([-a-z]+).lang$/i',$file,$matches)) + { + $languages[$matches[2]]['available'] = True; + } + } + $d->close(); + uasort($languages,create_function('$a,$b','return strcmp(@$a[\'descr\'],@$b[\'descr\']);')); + } + else + { + asort($languages); + } + //_debug_array($languages); + return $languages; + } + /** * List availible charsets and it's supported languages * @param boolean/string $name=false name for selectbox or false to return an array * @param string $selected selected charset * @return string/array html for a selectbox or array with charset / languages pairs */ - function get_charsets($name=false,$selected='') + static function get_charsets($name=false,$selected='') { $charsets = array( 'utf-8' => 'utf-8: '.lang('all languages (incl. not listed ones)'), diff --git a/setup/lang.php b/setup/lang.php index 67a3dfc328..d021d40dfd 100644 --- a/setup/lang.php +++ b/setup/lang.php @@ -55,8 +55,7 @@ if (!@$newinstall && !isset($GLOBALS['egw_info']['setup']['installed_langs'])) } $select_box_desc = lang('Select which languages you would like to use'); $select_box = ''; -$languages = get_langs(); -uasort($languages,create_function('$a,$b','return strcmp(@$a[\'descr\'],@$b[\'descr\']);')); +$languages = setup_translation::get_supported_langs(); foreach($languages as $id => $data) { $select_box_langs = From d02badfed5f8896e6da2c6392307709665032794 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 08:29:54 +0000 Subject: [PATCH 04/88] using functions moved to setup_translation class and modernising setup_html --- setup/inc/class.setup_html.inc.php | 574 +++++++++++++++-------------- setup/inc/functions.inc.php | 59 --- setup/index.php | 2 +- 3 files changed, 302 insertions(+), 333 deletions(-) diff --git a/setup/inc/class.setup_html.inc.php b/setup/inc/class.setup_html.inc.php index fc714df20f..6fd00707fe 100644 --- a/setup/inc/class.setup_html.inc.php +++ b/setup/inc/class.setup_html.inc.php @@ -1,322 +1,350 @@ * - * and Miles Lott * - * -------------------------------------------- * - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the * - * Free Software Foundation; either version 2 of the License, or (at your * - * option) any later version. * - \**************************************************************************/ +/** + * eGroupWare Setup + * + * @link http://www.egroupware.org + * @package setup + * @author Tony Puglisi (Angles) + * @author Miles Lott + * @author Ralf Becker + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ - /* $Id$ */ - - class setup_html +/** + * Some static helper functions to generate html stuff in setup + */ +class setup_html +{ + /** + * generate header.inc.php file output - NOT a generic html header function + * + */ + static function generate_header() { - /** - * generate header.inc.php file output - NOT a generic html header function - * - */ - function generate_header() + // PHP will automatically replace any dots in incoming + // variable names with underscores. + + $GLOBALS['header_template']->set_file(array('header' => 'header.inc.php.template')); + $GLOBALS['header_template']->set_block('header','domain','domain'); + $var = Array(); + + $deletedomain = get_var('deletedomain',Array('POST')); + $domains = get_var('domains',Array('POST')); + + foreach($domains as $k => $v) { - // PHP will automatically replace any dots in incoming - // variable names with underscores. - - $GLOBALS['header_template']->set_file(array('header' => 'header.inc.php.template')); - $GLOBALS['header_template']->set_block('header','domain','domain'); - $var = Array(); - - $deletedomain = get_var('deletedomain',Array('POST')); - $domains = get_var('domains',Array('POST')); - - foreach($domains as $k => $v) + if(is_array($deletedomain) && isset($deletedomain[$k])) { - if(is_array($deletedomain) && isset($deletedomain[$k])) - { - continue; - } - $variableName = str_replace('.','_',$k); - $dom = get_var('setting_'.$variableName,Array('POST')); - $GLOBALS['header_template']->set_var('DB_DOMAIN',$v); - foreach($dom as $x => $y) - { - if(strtoupper($x) == 'CONFIG_PASS') - { - $GLOBALS['header_template']->set_var(strtoupper($x),md5($y)); - } - else - { - $GLOBALS['header_template']->set_var(strtoupper($x),$y); - } - } - /* Admin did not type a new password, so use the old one from the hidden field, - * which is already md5 encoded. - */ - if($dom['config_password'] && !$dom['config_pass']) - { - /* Real == hidden */ - $GLOBALS['header_template']->set_var('CONFIG_PASS',$dom['config_password']); - } - /* If the admin didn't select a db_port, set to the default */ - if(!$dom['db_port']) - { - $GLOBALS['header_template']->set_var('DB_PORT',$GLOBALS['default_db_ports'][$dom['db_type']]); - } - $GLOBALS['header_template']->parse('domains','domain',True); + continue; } - - $GLOBALS['header_template']->set_var('domain',''); - - $setting = get_var('setting',Array('POST')); - while($setting && list($k,$v) = @each($setting)) + $variableName = str_replace('.','_',$k); + $dom = get_var('setting_'.$variableName,Array('POST')); + $GLOBALS['header_template']->set_var('DB_DOMAIN',$v); + foreach($dom as $x => $y) { - if(strtoupper($k) == 'HEADER_ADMIN_PASSWORD') + if(strtoupper($x) == 'CONFIG_PASS') { - $var[strtoupper($k)] = md5($v); + $GLOBALS['header_template']->set_var(strtoupper($x),md5($y)); } else { - $var[strtoupper($k)] = $v; + $GLOBALS['header_template']->set_var(strtoupper($x),$y); } } - /* Admin did not type a new header password, so use the old one from the hidden field, + /* Admin did not type a new password, so use the old one from the hidden field, * which is already md5 encoded. */ - if($var['HEADER_ADMIN_PASS'] && empty($setting['HEADER_ADMIN_PASSWORD'])) + if($dom['config_password'] && !$dom['config_pass']) { /* Real == hidden */ - $var['HEADER_ADMIN_PASSWORD'] = $var['HEADER_ADMIN_PASS']; + $GLOBALS['header_template']->set_var('CONFIG_PASS',$dom['config_password']); } - $GLOBALS['header_template']->set_var($var); - return $GLOBALS['header_template']->parse('out','header'); + /* If the admin didn't select a db_port, set to the default */ + if(!$dom['db_port']) + { + $GLOBALS['header_template']->set_var('DB_PORT',$GLOBALS['default_db_ports'][$dom['db_type']]); + } + $GLOBALS['header_template']->parse('domains','domain',True); } - function setup_tpl_dir($app_name='setup') + $GLOBALS['header_template']->set_var('domain',''); + + $setting = get_var('setting',Array('POST')); + while($setting && list($k,$v) = @each($setting)) { - /* hack to get tpl dir */ - if (is_dir(EGW_SERVER_ROOT)) + if(strtoupper($k) == 'HEADER_ADMIN_PASSWORD') { - $srv_root = EGW_SERVER_ROOT . SEP . "$app_name" . SEP; + $var[strtoupper($k)] = md5($v); } else { - $srv_root = ''; + $var[strtoupper($k)] = $v; } + } + /* Admin did not type a new header password, so use the old one from the hidden field, + * which is already md5 encoded. + */ + if($var['HEADER_ADMIN_PASS'] && empty($setting['HEADER_ADMIN_PASSWORD'])) + { + /* Real == hidden */ + $var['HEADER_ADMIN_PASSWORD'] = $var['HEADER_ADMIN_PASS']; + } + $GLOBALS['header_template']->set_var($var); + return $GLOBALS['header_template']->parse('out','header'); + } - $tpl_typical = 'templates' . SEP . 'default'; - $tpl_root = "$srv_root" ."$tpl_typical"; - return $tpl_root; + static function setup_tpl_dir($app_name='setup') + { + /* hack to get tpl dir */ + if (is_dir(EGW_SERVER_ROOT)) + { + $srv_root = EGW_SERVER_ROOT . SEP . "$app_name" . SEP; + } + else + { + $srv_root = ''; } - function show_header($title='',$nologoutbutton=False, $logoutfrom='config', $configdomain='') + $tpl_typical = 'templates' . SEP . 'default'; + $tpl_root = "$srv_root" ."$tpl_typical"; + return $tpl_root; + } + + static function show_header($title='',$nologoutbutton=False, $logoutfrom='config', $configdomain='') + { + // add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv) + header('Content-type: text/html; charset='.$GLOBALS['egw_setup']->system_charset); + + $GLOBALS['setup_tpl']->set_var('charset',$GLOBALS['egw_setup']->system_charset); + $style = array( + 'th_bg' => '#486591', + 'th_text' => '#FFFFFF', + 'row_on' => '#DDDDDD', + 'row_off' => '#EEEEEE', + 'banner_bg' => '#4865F1', + 'msg' => '#FF0000', + ); + $GLOBALS['setup_tpl']->set_var($style); + if ($nologoutbutton) { - // add a content-type header to overwrite an existing default charset in apache (AddDefaultCharset directiv) - header('Content-type: text/html; charset='.$GLOBALS['egw_setup']->system_charset); - - $GLOBALS['setup_tpl']->set_var('charset',$GLOBALS['egw_setup']->system_charset); - $style = array( - 'th_bg' => '#486591', - 'th_text' => '#FFFFFF', - 'row_on' => '#DDDDDD', - 'row_off' => '#EEEEEE', - 'banner_bg' => '#4865F1', - 'msg' => '#FF0000', - ); - $GLOBALS['setup_tpl']->set_var($style); - if ($nologoutbutton) - { - $GLOBALS['setup_tpl']->set_block('T_head','loged_in'); - $GLOBALS['setup_tpl']->set_var('loged_in',''); - } - else - { - $btn_logout = '' . lang('Logout').''; - $check_install = ''.lang('Check installation').''; - } - - $GLOBALS['setup_tpl']->set_var('lang_setup', lang('setup')); - $GLOBALS['setup_tpl']->set_var('page_title',$title); - if ($configdomain == '') - { - $GLOBALS['setup_tpl']->set_var('configdomain',''); - } - else - { - $GLOBALS['setup_tpl']->set_var('configdomain',' - ' . lang('Domain') . ': ' . $configdomain); - } - - if(basename($_SERVER['SCRIPT_FILENAME']) != 'index.php') - { - $index_btn = '' . lang('Setup Main Menu') . ''; - $index_img = 'ball'; - } - - $GLOBALS['setup_tpl']->set_var('lang_version',lang('version')); - $GLOBALS['setup_tpl']->set_var('pgw_ver',@$GLOBALS['egw_info']['server']['versions']['phpgwapi']); - $GLOBALS['setup_tpl']->set_var(array( - 'logoutbutton' => $btn_logout, - 'indexbutton' => $index_btn, - 'indeximg' => $index_img, - 'check_install' => $check_install, - 'main_menu' => lang('Setup Main Menu'), - 'user_login' => lang('Back to user login') - )); - - // manual / help link to the install manual on egroupware.org - $manual_remote_egw_url = 'http://manual.egroupware.org/egroupware'; - $url_parts = explode('/',$_SERVER['PHP_SELF']); - $script = array_pop($url_parts); - $lang = setup::get_lang(); - $url = $manual_remote_egw_url.'/manual/index.php?referer='.urlencode($manual_remote_egw_url.'/setup/'.$script). - ($lang ? '&lang='.urlencode($lang) : ''); - $GLOBALS['setup_tpl']->set_var('manual',''. - htmlspecialchars(lang('Manual / help')).''); - - $GLOBALS['setup_tpl']->pparse('out','T_head'); - /* $setup_tpl->set_var('T_head',''); */ + $GLOBALS['setup_tpl']->set_block('T_head','loged_in'); + $GLOBALS['setup_tpl']->set_var('loged_in',''); + } + else + { + $btn_logout = '' . lang('Logout').''; + $check_install = ''.lang('Check installation').''; } - function show_footer() + $GLOBALS['setup_tpl']->set_var('lang_setup', lang('setup')); + $GLOBALS['setup_tpl']->set_var('page_title',$title); + if ($configdomain == '') { - $GLOBALS['setup_tpl']->pparse('out','T_footer'); - unset($GLOBALS['setup_tpl']); + $GLOBALS['setup_tpl']->set_var('configdomain',''); + } + else + { + $GLOBALS['setup_tpl']->set_var('configdomain',' - ' . lang('Domain') . ': ' . $configdomain); } - function show_alert_msg($alert_word='Setup alert',$alert_msg='setup alert (generic)') + if(basename($_SERVER['SCRIPT_FILENAME']) != 'index.php') { - $GLOBALS['setup_tpl']->set_var('V_alert_word',$alert_word); - $GLOBALS['setup_tpl']->set_var('V_alert_msg',$alert_msg); - $GLOBALS['setup_tpl']->pparse('out','T_alert_msg'); + $index_btn = '' . lang('Setup Main Menu') . ''; + $index_img = 'ball'; } - function make_frm_btn_simple($pre_frm_blurb='',$frm_method='post',$frm_action='',$input_type='submit',$input_value='',$post_frm_blurb='') + $GLOBALS['setup_tpl']->set_var('lang_version',lang('version')); + $GLOBALS['setup_tpl']->set_var('pgw_ver',@$GLOBALS['egw_info']['server']['versions']['phpgwapi']); + $GLOBALS['setup_tpl']->set_var(array( + 'logoutbutton' => $btn_logout, + 'indexbutton' => $index_btn, + 'indeximg' => $index_img, + 'check_install' => $check_install, + 'main_menu' => lang('Setup Main Menu'), + 'user_login' => lang('Back to user login') + )); + + // manual / help link to the install manual on egroupware.org + $manual_remote_egw_url = 'http://manual.egroupware.org/egroupware'; + $url_parts = explode('/',$_SERVER['PHP_SELF']); + $script = array_pop($url_parts); + $lang = setup::get_lang(); + $url = $manual_remote_egw_url.'/manual/index.php?referer='.urlencode($manual_remote_egw_url.'/setup/'.$script). + ($lang ? '&lang='.urlencode($lang) : ''); + $GLOBALS['setup_tpl']->set_var('manual',''. + htmlspecialchars(lang('Manual / help')).''); + + $GLOBALS['setup_tpl']->pparse('out','T_head'); + /* $setup_tpl->set_var('T_head',''); */ + } + + static function show_footer() + { + $GLOBALS['setup_tpl']->pparse('out','T_footer'); + unset($GLOBALS['setup_tpl']); + } + + static function show_alert_msg($alert_word='Setup alert',$alert_msg='setup alert (generic)') + { + $GLOBALS['setup_tpl']->set_var('V_alert_word',$alert_word); + $GLOBALS['setup_tpl']->set_var('V_alert_msg',$alert_msg); + $GLOBALS['setup_tpl']->pparse('out','T_alert_msg'); + } + + static function make_frm_btn_simple($pre_frm_blurb='',$frm_method='post',$frm_action='',$input_type='submit',$input_value='',$post_frm_blurb='') + { + /* a simple form has simple components */ + $simple_form = $pre_frm_blurb ."\n" + . '
' . "\n" + . '' . "\n" + . '
' . "\n" + . $post_frm_blurb . "\n"; + return $simple_form; + } + + static function make_href_link_simple($pre_link_blurb='',$href_link='',$href_text='default text',$post_link_blurb='') + { + /* a simple href link has simple components */ + $simple_link = $pre_link_blurb + . '' . $href_text . ' ' + . $post_link_blurb . "\n"; + return $simple_link; + } + + static function login_form() + { + /* begin use TEMPLATE login_main.tpl */ + $GLOBALS['setup_tpl']->set_var('ConfigLoginMSG',@$GLOBALS['egw_info']['setup']['ConfigLoginMSG']); + $GLOBALS['setup_tpl']->set_var('HeaderLoginMSG',@$GLOBALS['egw_info']['setup']['HeaderLoginMSG']); + $GLOBALS['setup_tpl']->set_var('lang_header_username',lang('Header Username')); + $GLOBALS['setup_tpl']->set_var('lang_header_password',lang('Header Password')); + $GLOBALS['setup_tpl']->set_var('lang_header_login',lang('Header Admin Login')); + $GLOBALS['setup_tpl']->set_var('lang_config_login',lang('Setup/Config Admin Login')); + $GLOBALS['setup_tpl']->set_var('lang_config_username',lang('Config Username')); + $GLOBALS['setup_tpl']->set_var('lang_config_password',lang('Config Password')); + $GLOBALS['setup_tpl']->set_var('lang_domain',lang('Domain')); + + $GLOBALS['setup_tpl']->set_var('lang_select',self::lang_select()); + + if ($GLOBALS['egw_info']['setup']['stage']['header'] == '10') { - /* a simple form has simple components */ - $simple_form = $pre_frm_blurb ."\n" - . '
' . "\n" - . '' . "\n" - . '
' . "\n" - . $post_frm_blurb . "\n"; - return $simple_form; - } - - function make_href_link_simple($pre_link_blurb='',$href_link='',$href_text='default text',$post_link_blurb='') - { - /* a simple href link has simple components */ - $simple_link = $pre_link_blurb - . '' . $href_text . ' ' - . $post_link_blurb . "\n"; - return $simple_link; - } - - function login_form() - { - /* begin use TEMPLATE login_main.tpl */ - $GLOBALS['setup_tpl']->set_var('ConfigLoginMSG',@$GLOBALS['egw_info']['setup']['ConfigLoginMSG']); - $GLOBALS['setup_tpl']->set_var('HeaderLoginMSG',@$GLOBALS['egw_info']['setup']['HeaderLoginMSG']); - $GLOBALS['setup_tpl']->set_var('lang_header_username',lang('Header Username')); - $GLOBALS['setup_tpl']->set_var('lang_header_password',lang('Header Password')); - $GLOBALS['setup_tpl']->set_var('lang_header_login',lang('Header Admin Login')); - $GLOBALS['setup_tpl']->set_var('lang_config_login',lang('Setup/Config Admin Login')); - $GLOBALS['setup_tpl']->set_var('lang_config_username',lang('Config Username')); - $GLOBALS['setup_tpl']->set_var('lang_config_password',lang('Config Password')); - $GLOBALS['setup_tpl']->set_var('lang_domain',lang('Domain')); - - $GLOBALS['setup_tpl']->set_var('lang_select',lang_select()); - - if ($GLOBALS['egw_info']['setup']['stage']['header'] == '10') + /* + Begin use SUB-TEMPLATE login_stage_header, + fills V_login_stage_header used inside of login_main.tpl + */ + if (count($GLOBALS['egw_domain']) > 1) { - /* - Begin use SUB-TEMPLATE login_stage_header, - fills V_login_stage_header used inside of login_main.tpl - */ - if (count($GLOBALS['egw_domain']) > 1) + foreach($GLOBALS['egw_domain'] as $domain => $data) { - foreach($GLOBALS['egw_domain'] as $domain => $data) - { - $domains .= "\n"; - } - $GLOBALS['setup_tpl']->set_var('domains',$domains); + $domains .= "\n"; + } + $GLOBALS['setup_tpl']->set_var('domains',$domains); - // use BLOCK B_multi_domain inside of login_stage_header - $GLOBALS['setup_tpl']->parse('V_multi_domain','B_multi_domain'); - // in this case, the single domain block needs to be nothing - $GLOBALS['setup_tpl']->set_var('V_single_domain',''); + // use BLOCK B_multi_domain inside of login_stage_header + $GLOBALS['setup_tpl']->parse('V_multi_domain','B_multi_domain'); + // in this case, the single domain block needs to be nothing + $GLOBALS['setup_tpl']->set_var('V_single_domain',''); + } + else + { + reset($GLOBALS['egw_domain']); + $default_domain = each($GLOBALS['egw_domain']); + $GLOBALS['setup_tpl']->set_var('default_domain_zero',$default_domain[0]); + + /* Use BLOCK B_single_domain inside of login_stage_header */ + $GLOBALS['setup_tpl']->parse('V_single_domain','B_single_domain'); + /* in this case, the multi domain block needs to be nothing */ + $GLOBALS['setup_tpl']->set_var('V_multi_domain',''); + } + /* + End use SUB-TEMPLATE login_stage_header + put all this into V_login_stage_header for use inside login_main + */ + $GLOBALS['setup_tpl']->parse('V_login_stage_header','T_login_stage_header'); + } + else + { + /* begin SKIP SUB-TEMPLATE login_stage_header */ + $GLOBALS['setup_tpl']->set_var('V_multi_domain',''); + $GLOBALS['setup_tpl']->set_var('V_single_domain',''); + $GLOBALS['setup_tpl']->set_var('V_login_stage_header',''); + } + /* + end use TEMPLATE login_main.tpl + now out the login_main template + */ + $GLOBALS['setup_tpl']->pparse('out','T_login_main'); + } + + static function lang_select($onChange=False,$ConfigLang='') + { + if (!$ConfigLang) + { + $ConfigLang = setup::get_lang(); + } + $select = '' . "\n"; + + return $select; } -?> + + static function get_template_list() + { + $d = dir(EGW_SERVER_ROOT . '/phpgwapi/templates'); + + while($entry = $d->read()) + { + if ($entry != 'CVS' && $entry != '.' && $entry != '..') + { + $list[$entry]['name'] = $entry; + $f = EGW_SERVER_ROOT . '/phpgwapi/templates/' . $entry . '/details.inc.php'; + if (file_exists ($f)) + { + include($f); + $list[$entry]['title'] = 'Use ' . $GLOBALS['egw_info']['template'][$entry]['title'] . 'interface'; + } + else + { + $list[$entry]['title'] = $entry; + } + } + } + $d->close(); + reset ($list); + return $list; + } + + static function list_themes() + { + $dh = dir(EGW_SERVER_ROOT . '/phpgwapi/themes'); + while ($file = $dh->read()) + { + if (preg_match('/'."\.theme$".'/i', $file)) + { + $list[] = substr($file,0,strpos($file,'.')); + } + } + $dh->close(); + reset ($list); + return $list; + } +} diff --git a/setup/inc/functions.inc.php b/setup/inc/functions.inc.php index d398c0ac8c..62aad93fa8 100644 --- a/setup/inc/functions.inc.php +++ b/setup/inc/functions.inc.php @@ -75,65 +75,6 @@ function lang($key,$vars=null) return $GLOBALS['egw_setup']->translation->translate("$key", $vars); } -/** - * returns array of languages we support, with enabled set to True if the lang file exists - */ -function get_langs() -{ - $f = fopen('./lang/languages','rb'); - while($line = fgets($f,200)) - { - list($x,$y) = explode("\t",$line); - $languages[$x]['lang'] = trim($x); - $languages[$x]['descr'] = trim($y); - $languages[$x]['available'] = False; - } - fclose($f); - - $d = dir('./lang'); - while($file=$d->read()) - { - if(preg_match('/^(php|e)gw_([-a-z]+).lang$/i',$file,$matches)) - { - $languages[$matches[2]]['available'] = True; - } - } - $d->close(); - - //print_r($languages); - return $languages; -} - -function lang_select($onChange=False,$ConfigLang='') -{ - if (!$ConfigLang) - { - $ConfigLang = setup::get_lang(); - } - $select = '' . "\n"; - - return $select; -} - if(file_exists(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php')) { include(EGW_SERVER_ROOT.'/phpgwapi/setup/setup.inc.php'); /* To set the current core version */ diff --git a/setup/index.php b/setup/index.php index cb68b723a5..de0d9e5bcb 100644 --- a/setup/index.php +++ b/setup/index.php @@ -480,7 +480,7 @@ switch($GLOBALS['egw_info']['setup']['stage']['lang']) break; case 10: $langs_list = array(); - $languages = get_langs(); + $languages = setup_translation::get_supported_langs(); foreach ($GLOBALS['egw_info']['setup']['installed_langs'] as $key => $value) { $langs_list[] = isset($languages[$key]) ? $languages[$key]['descr'] : $value; From 307294c3b0d191df1d7d60909fb5a5a2db9a7481 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 12:50:26 +0000 Subject: [PATCH 05/88] commiting the session, before redirecting might fix racecondition in session creation --- login.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/login.php b/login.php index 5cb49b05db..97a7aad5e2 100755 --- a/login.php +++ b/login.php @@ -359,6 +359,8 @@ else } else { + // commiting the session, before redirecting might fix racecondition in session creation + $GLOBALS['egw']->session->commit_session(); egw::redirect_link($forward,$extra_vars); } } From fcec62794e278ae8728d7c54596d04dd31412774 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 13:02:05 +0000 Subject: [PATCH 06/88] some improvments for easier extention of the contact form --- .../inc/class.addressbook_contactform.inc.php | 22 ++++++++++++++----- 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/addressbook/inc/class.addressbook_contactform.inc.php b/addressbook/inc/class.addressbook_contactform.inc.php index 09d7ea4440..36106b7f00 100644 --- a/addressbook/inc/class.addressbook_contactform.inc.php +++ b/addressbook/inc/class.addressbook_contactform.inc.php @@ -16,6 +16,13 @@ */ class addressbook_contactform { + /** + * Callback as variable for easier extending + * + * @var string + */ + var $callback = 'addressbook.addressbook_contactform.display'; + /** * Shows the contactform and stores the submitted data * @@ -29,7 +36,7 @@ class addressbook_contactform * @param string $copytoreceiver=false send a copy of notification to receiver * @return string html content */ - function display($content=null,$addressbook=null,$fields=null,$msg=null,$email=null,$tpl_name=null,$subject=null,$copytoreceiver=false) + function display(array &$content=null,$addressbook=null,$fields=null,$msg=null,$email=null,$tpl_name=null,$subject=null,$copytoreceiver=false,$sel_options=array()) { #error_log( "

addressbook_contactform::display(".print_r($content,true).",$addressbook,".print_r($fields,true).",$msg,$tpl_name)

\n"); if (empty($tpl_name) && !empty($content['tpl_form_name'])) $tpl_name =$content['tpl_form_name']; @@ -53,6 +60,7 @@ class addressbook_contactform $contact = new addressbook_bo(); if ($content['owner']) // save the contact in the addressbook { + $content['private'] = 0; // in case default_private is set if (($id = $contact->save($content))) { // check for fileuploads and attach the found files @@ -70,7 +78,7 @@ class addressbook_contactform } } - return '

'.$content['msg'].'

'; + return '

'.($msg ? $msg : $content['msg']).'

'; } else { @@ -110,7 +118,7 @@ class addressbook_contactform $custom = 1; foreach($fields as $name) { - if ($name{0} == '#') // custom field + if ($name[0] == '#') // custom field { static $contact; if (is_null($contact)) @@ -135,15 +143,17 @@ class addressbook_contactform } } $preserv['start_time'] = time(); + $content['lang'] = $GLOBALS['egw_info']['user']['preferences']['common']['lang']; } - if (is_array($content) && $submitted == 'truebutfalse') { + elseif ($submitted == 'truebutfalse') + { $preserv['tpl_form_name'] = $tpl_name; unset($content['submitit']); $custom = 1; // fieldnames are "defined" by the commit attempt, that way, we do not have to remember them foreach($content as $name => $value) { $preserv[$name]=$value; - if ($name{0} == '#') // custom field + if ($name[0] == '#') // custom field { static $contact; if (is_null($contact)) $contact = new addressbook_bo(); @@ -184,6 +194,6 @@ class addressbook_contactform $content['captcha_task'] = sprintf('%d - %d =',$num1,$num2); $preserv['captcha_result'] = $num1-$num2; } - return $tpl->exec('addressbook.addressbook_contactform.display',$content,$sel_options,$readonlys,$preserv); + return $tpl->exec($this->callback,$content,$sel_options,$readonlys,$preserv); } } From cff42abd0738d3c63dc5b5620e6a84f7147b2bd4 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 17:02:20 +0000 Subject: [PATCH 07/88] modernising uiconfig a little bit and fixed mulitiple levels for framework for framed templates --- admin/inc/class.uiconfig.inc.php | 472 +++++++++++++++---------------- 1 file changed, 221 insertions(+), 251 deletions(-) diff --git a/admin/inc/class.uiconfig.inc.php b/admin/inc/class.uiconfig.inc.php index 9e958182e8..423dd866e5 100644 --- a/admin/inc/class.uiconfig.inc.php +++ b/admin/inc/class.uiconfig.inc.php @@ -1,270 +1,240 @@ * - * http://www.egroupware.org * - * -------------------------------------------- * - * This program is free software; you can redistribute it and/or modify it * - * under the terms of the GNU General Public License as published by the * - * Free Software Foundation; either version 2 of the License, or (at your * - * option) any later version. * - \**************************************************************************/ +/** + * eGgroupWare admin - site configuration + * + * @link http://www.egroupware.org + * @author Miles Lott + * @package admin + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @version $Id$ + */ - /* $Id$ */ +/** + * Site configuration for all apps using an $app/templates/default/config.tpl + */ +class uiconfig +{ + var $public_functions = array('index' => True); - class uiconfig + function index() { - var $public_functions = array('index' => True); - - function index() + if ($GLOBALS['egw']->acl->check('site_config_access',1,'admin')) { - if ($GLOBALS['egw']->acl->check('site_config_access',1,'admin')) - { - $GLOBALS['egw']->redirect_link('/index.php'); - } - $referer = $_POST['submit'] || $_POST['cancel'] ? $_POST['referer'] : $_SERVER['HTTP_REFERER']; - if (!$referer) $referer = $GLOBALS['egw']->link('/admin/index.php'); - if ($GLOBALS['egw_info']['server']['webserver_url']) - { - list(,$show_app) = explode($GLOBALS['egw_info']['server']['webserver_url'],$referer); - } - else - { - $parts = parse_url($referer); - $show_app = $parts['path']; - unset($parts); - } - list(,$show_app) = explode('/',$show_app); - if (!$show_app) $show_app = 'admin'; + egw::redirect_link('/index.php'); + } + $referer = $_POST['submit'] || $_POST['cancel'] ? $_POST['referer'] : + common::get_referer('/admin/index.php',$_POST['referer']); + list(,$show_app) = explode('/',$referer); + if (!$show_app) $show_app = 'admin'; - // load the translations of the app we show too, so they dont need to be in admin! - if ($_GET['appname'] != 'admin') + // load the translations of the app we show too, so they dont need to be in admin! + if ($_GET['appname'] != 'admin') + { + translation::add_app($_GET['appname']); + } + + if(get_magic_quotes_gpc() && is_array($_POST['newsettings'])) + { + $_POST['newsettings'] = array_stripslashes($_POST['newsettings']); + } + + switch($_GET['appname']) + { + case 'admin': + case 'addressbook': + case 'calendar': + case 'email': + case 'nntp': + /* + Other special apps can go here for now, e.g.: + case 'bogusappname': + */ + $appname = $_GET['appname']; + $config_appname = 'phpgwapi'; + break; + case 'phpgwapi': + case '': + /* This keeps the admin from getting into what is a setup-only config */ + egw::redirect_link('/admin/index.php'); + break; + default: + $appname = $_GET['appname']; + $config_appname = $appname; + break; + } + $t =& CreateObject('phpgwapi.Template',$GLOBALS['egw']->common->get_tpl_dir($appname)); + $t->set_unknowns('keep'); + $t->set_file(array('config' => 'config.tpl')); + $t->set_block('config','header','header'); + $t->set_block('config','body','body'); + $t->set_block('config','footer','footer'); + + $c =& CreateObject('phpgwapi.config',$config_appname); + $c->read_repository(); + + if ($_POST['cancel'] || $_POST['submit'] && $GLOBALS['egw']->acl->check('site_config_access',2,'admin')) + { + egw::redirect_link($referer); + } + + if ($_POST['submit']) + { + /* Load hook file with functions to validate each config (one/none/all) */ + $GLOBALS['egw']->hooks->single('config_validate',$appname); + + foreach($_POST['newsettings'] as $key => $config) { - $GLOBALS['egw']->translation->add_app($_GET['appname']); + if ($config) + { + $c->config_data[$key] = $config; + if($GLOBALS['egw_info']['server']['found_validation_hook'] && function_exists($key)) + { + call_user_func($key,$config); + if($GLOBALS['config_error']) + { + $errors .= lang($GLOBALS['config_error']) . ' '; + $GLOBALS['config_error'] = False; + } + } + } + /* don't erase passwords, since we also don't print them */ + elseif(strpos($key,'passwd') === false && strpos($key,'password') === false && strpos($key,'root_pw') === false) + { + unset($c->config_data[$key]); + } + } + if($GLOBALS['egw_info']['server']['found_validation_hook'] && function_exists('final_validation')) + { + final_validation($_POST['newsettings']); + if($GLOBALS['config_error']) + { + $errors .= lang($GLOBALS['config_error']) . ' '; + $GLOBALS['config_error'] = False; + } + unset($GLOBALS['egw_info']['server']['found_validation_hook']); } - if(get_magic_quotes_gpc() && is_array($_POST['newsettings'])) - { - $_POST['newsettings'] = $this->array_stripslashes($_POST['newsettings']); - } + $c->save_repository(); - switch($_GET['appname']) + if(!$errors) { - case 'admin': - case 'addressbook': - case 'calendar': - case 'email': - case 'nntp': - /* - Other special apps can go here for now, e.g.: - case 'bogusappname': - */ - $appname = $_GET['appname']; - $config_appname = 'phpgwapi'; + egw::redirect_link($referer); + } + } + + if($errors) + { + $t->set_var('error',lang('Error') . ': ' . $errors); + $t->set_var('th_err','#FF8888'); + unset($errors); + unset($GLOBALS['config_error']); + } + else + { + $t->set_var('error',''); + $t->set_var('th_err',$GLOBALS['egw_info']['theme']['th_bg']); + } + // set currentapp to our calling app, to show the right sidebox-menu + $GLOBALS['egw_info']['flags']['currentapp'] = $show_app; + $GLOBALS['egw']->common->egw_header(); + echo parse_navbar(); + + $t->set_var('title',lang('Site Configuration')); + $t->set_var('action_url',$GLOBALS['egw']->link('/index.php','menuaction=admin.uiconfig.index&appname=' . $appname)); + $t->set_var('th_bg', $GLOBALS['egw_info']['theme']['th_bg']); + $t->set_var('th_text', $GLOBALS['egw_info']['theme']['th_text']); + $t->set_var('row_on', $GLOBALS['egw_info']['theme']['row_on']); + $t->set_var('row_off', $GLOBALS['egw_info']['theme']['row_off']); + $t->set_var('hidden_vars',''); + $t->pparse('out','header'); + + $vars = $t->get_undefined('body'); + + if ($GLOBALS['egw']->hooks->single('config',$appname)) // reload the config-values, they might have changed + { + $c->read_repository(); + } + foreach($vars as $value) + { + $valarray = explode('_',$value); + $type = array_shift($valarray); + $newval = implode(' ',$valarray); + + switch ($type) + { + case 'lang': + $t->set_var($value,lang($newval)); break; - case 'phpgwapi': - case '': - /* This keeps the admin from getting into what is a setup-only config */ - $GLOBALS['egw']->redirect_link('/admin/index.php'); + case 'value': + $newval = str_replace(' ','_',$newval); + /* Don't show passwords in the form */ + if(strpos($value,'passwd') !== false || strpos($value,'password') !== false || strpos($value,'root_pw') !== false) + { + $t->set_var($value,''); + } + else + { + $t->set_var($value,$c->config_data[$newval]); + } + break; + /* + case 'checked': + $newval = str_replace(' ','_',$newval); + if ($c->config_data[$newval]) + { + $t->set_var($value,' checked'); + } + else + { + $t->set_var($value,''); + } + break; + */ + case 'selected': + $configs = array(); + $config = ''; + $newvals = explode(' ',$newval); + $setting = end($newvals); + for ($i=0;$i<(count($newvals) - 1); $i++) + { + $configs[] = $newvals[$i]; + } + $config = implode('_',$configs); + /* echo $config . '=' . $c->config_data[$config]; */ + if ($c->config_data[$config] == $setting) + { + $t->set_var($value,' selected'); + } + else + { + $t->set_var($value,''); + } + break; + case 'hook': + $newval = str_replace(' ','_',$newval); + if(function_exists($newval)) + { + $t->set_var($value,$newval($c->config_data)); + } + else + { + $t->set_var($value,''); + } + break; + case 'call': // eg. call_class::method or call_app.class.method + $newval = str_replace(' ','_',$newval); + $t->set_var($value,ExecMethod($newval,$c->config_data)); break; default: - $appname = $_GET['appname']; - $config_appname = $appname; + $t->set_var($value,''); break; } - $t =& CreateObject('phpgwapi.Template',$GLOBALS['egw']->common->get_tpl_dir($appname)); - $t->set_unknowns('keep'); - $t->set_file(array('config' => 'config.tpl')); - $t->set_block('config','header','header'); - $t->set_block('config','body','body'); - $t->set_block('config','footer','footer'); - - $c =& CreateObject('phpgwapi.config',$config_appname); - $c->read_repository(); - - if ($_POST['cancel'] || $_POST['submit'] && $GLOBALS['egw']->acl->check('site_config_access',2,'admin')) - { - $GLOBALS['egw']->redirect($referer); - } - - if ($_POST['submit']) - { - /* Load hook file with functions to validate each config (one/none/all) */ - $GLOBALS['egw']->hooks->single('config_validate',$appname); - - foreach($_POST['newsettings'] as $key => $config) - { - if ($config) - { - $c->config_data[$key] = $config; - if($GLOBALS['egw_info']['server']['found_validation_hook'] && function_exists($key)) - { - call_user_func($key,$config); - if($GLOBALS['config_error']) - { - $errors .= lang($GLOBALS['config_error']) . ' '; - $GLOBALS['config_error'] = False; - } - } - } - /* don't erase passwords, since we also don't print them */ - elseif(strpos($key,'passwd') === false && strpos($key,'password') === false && strpos($key,'root_pw') === false) - { - unset($c->config_data[$key]); - } - } - if($GLOBALS['egw_info']['server']['found_validation_hook'] && function_exists('final_validation')) - { - final_validation($_POST['newsettings']); - if($GLOBALS['config_error']) - { - $errors .= lang($GLOBALS['config_error']) . ' '; - $GLOBALS['config_error'] = False; - } - unset($GLOBALS['egw_info']['server']['found_validation_hook']); - } - - $c->save_repository(); - - if(!$errors) - { - $GLOBALS['egw']->redirect($referer); - } - } - - if($errors) - { - $t->set_var('error',lang('Error') . ': ' . $errors); - $t->set_var('th_err','#FF8888'); - unset($errors); - unset($GLOBALS['config_error']); - } - else - { - $t->set_var('error',''); - $t->set_var('th_err',$GLOBALS['egw_info']['theme']['th_bg']); - } - // set currentapp to our calling app, to show the right sidebox-menu - $GLOBALS['egw_info']['flags']['currentapp'] = $show_app; - $GLOBALS['egw']->common->egw_header(); - echo parse_navbar(); - - $t->set_var('title',lang('Site Configuration')); - $t->set_var('action_url',$GLOBALS['egw']->link('/index.php','menuaction=admin.uiconfig.index&appname=' . $appname)); - $t->set_var('th_bg', $GLOBALS['egw_info']['theme']['th_bg']); - $t->set_var('th_text', $GLOBALS['egw_info']['theme']['th_text']); - $t->set_var('row_on', $GLOBALS['egw_info']['theme']['row_on']); - $t->set_var('row_off', $GLOBALS['egw_info']['theme']['row_off']); - $t->set_var('hidden_vars',''); - $t->pparse('out','header'); - - $vars = $t->get_undefined('body'); - - if ($GLOBALS['egw']->hooks->single('config',$appname)) // reload the config-values, they might have changed - { - $c->read_repository(); - } - foreach($vars as $value) - { - $valarray = explode('_',$value); - $type = array_shift($valarray); - $newval = implode(' ',$valarray); - - switch ($type) - { - case 'lang': - $t->set_var($value,lang($newval)); - break; - case 'value': - $newval = str_replace(' ','_',$newval); - /* Don't show passwords in the form */ - if(strpos($value,'passwd') !== false || strpos($value,'password') !== false || strpos($value,'root_pw') !== false) - { - $t->set_var($value,''); - } - else - { - $t->set_var($value,$c->config_data[$newval]); - } - break; - /* - case 'checked': - $newval = str_replace(' ','_',$newval); - if ($c->config_data[$newval]) - { - $t->set_var($value,' checked'); - } - else - { - $t->set_var($value,''); - } - break; - */ - case 'selected': - $configs = array(); - $config = ''; - $newvals = explode(' ',$newval); - $setting = end($newvals); - for ($i=0;$i<(count($newvals) - 1); $i++) - { - $configs[] = $newvals[$i]; - } - $config = implode('_',$configs); - /* echo $config . '=' . $c->config_data[$config]; */ - if ($c->config_data[$config] == $setting) - { - $t->set_var($value,' selected'); - } - else - { - $t->set_var($value,''); - } - break; - case 'hook': - $newval = str_replace(' ','_',$newval); - if(function_exists($newval)) - { - $t->set_var($value,$newval($c->config_data)); - } - else - { - $t->set_var($value,''); - } - break; - case 'call': // eg. call_class::method or call_app.class.method - $newval = str_replace(' ','_',$newval); - $t->set_var($value,ExecMethod($newval,$c->config_data)); - break; - default: - $t->set_var($value,''); - break; - } - } - - $t->pfp('out','body'); - - $t->set_var('lang_submit', $GLOBALS['egw']->acl->check('site_config_access',2,'admin') ? lang('Cancel') : lang('Save')); - $t->set_var('lang_cancel', lang('Cancel')); - $t->pfp('out','footer'); } - /** - * applies stripslashes recursivly on each element of an array - * - * @param array &$var - * @return array - */ - function array_stripslashes($var) - { - if (!is_array($var)) - { - return stripslashes($var); - } - foreach($var as $key => $val) - { - $var[$key] = is_array($val) ? $this->array_stripslashes($val) : stripslashes($val); - } - return $var; - } + $t->pfp('out','body'); + + $t->set_var('lang_submit', $GLOBALS['egw']->acl->check('site_config_access',2,'admin') ? lang('Cancel') : lang('Save')); + $t->set_var('lang_cancel', lang('Cancel')); + $t->pfp('out','footer'); } -?> +} From f185382b0ac2d4de79b1c737840727ef9b12a313 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 29 Jul 2010 19:09:55 +0000 Subject: [PATCH 08/88] fixed not working link_query call to static callback eg. "someclass::somemethod" --- phpgwapi/inc/class.egw_link.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.egw_link.inc.php b/phpgwapi/inc/class.egw_link.inc.php index 0369ec457b..973fda4373 100644 --- a/phpgwapi/inc/class.egw_link.inc.php +++ b/phpgwapi/inc/class.egw_link.inc.php @@ -595,7 +595,7 @@ class egw_link extends solink } if(is_callable($method)) // php5.3+ call { - $result = $method($pattern,$options); + $result = call_user_func($method,$pattern,$options); } elseif(is_object($obj) && method_exists($obj,$method)) { @@ -910,7 +910,7 @@ class egw_link extends solink } else { - error_log(__METHOD__."($app,$id,$file,$comment) Can't mkdir $entry_dir!"); + error_log(__METHOD__."($app,$id,".array2string($file).",$comment) Can't mkdir $entry_dir!"); } return $Ok ? -$stat['ino'] : false; } From 55463e493b331b08a7f7778bf84835d38d389f7b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 30 Jul 2010 08:36:41 +0000 Subject: [PATCH 09/88] displaying label of link-entry widget as blur text --- etemplate/inc/class.link_widget.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etemplate/inc/class.link_widget.inc.php b/etemplate/inc/class.link_widget.inc.php index 6368dacc3e..32cb5f30ce 100644 --- a/etemplate/inc/class.link_widget.inc.php +++ b/etemplate/inc/class.link_widget.inc.php @@ -420,9 +420,11 @@ class link_widget 'no_app_sel' => !!$extension_data['app'], 'id' => is_array($value) ? $value['current'] : $id, 'query' => is_array($value) ? $value['query'] : '', - 'blur' => count($options) == 1 ? lang($app) : lang('Search'), + 'blur' => $cell['label'] ? $cell['label'] : + (count($options) == 1 ? lang($app) : lang('Search')), 'extra' => $cell['onchange'] ? ','.self::AJAX_NEED_ONCHANGE : null, // store flang for ajax_search, to display extra_line required by onchange ); + $cell['label'] = ''; // displayed as blur text if ($options) // limit the app-selectbox to the given apps { From 02d2ca5157169e768def2c4c386c8e9990038281 Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Fri, 30 Jul 2010 10:19:06 +0000 Subject: [PATCH 10/88] improve the handling of overlong words, fix a bug regarding the check of infolog_status existing in known status for the type selected --- infolog/inc/class.infolog_ui.inc.php | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/infolog/inc/class.infolog_ui.inc.php b/infolog/inc/class.infolog_ui.inc.php index d9f081c740..6fcf638514 100644 --- a/infolog/inc/class.infolog_ui.inc.php +++ b/infolog/inc/class.infolog_ui.inc.php @@ -1013,7 +1013,7 @@ class infolog_ui if ($content['js']) $content['js'] = ''; } // on a type-change, set the status to the default status of that type, if the actual status is not supported by the new type - if (!in_array($content['info_status'],$this->bo->status[$content['info_type']])) + if (!array_key_exists($content['info_status'],$this->bo->status[$content['info_type']])) { $content['info_status'] = $this->bo->status['defaults'][$content['info_type']]; if ($content['info_status'] != 'done') $content['info_datecompleted'] = ''; @@ -1041,14 +1041,21 @@ class infolog_ui { continue; } - foreach(array(',' => ', ', '.' => '. ') as $pattern => $replace) // set blank behind all , and . + $cont = split(' ', $content[$key]); + $ckarray = array(); + foreach($cont as &$word) { - if(strpos($content[$key], $replace) === false) + // set blank behind all , and . if words are too long, apply wordwrap afterwards to make sure we get + if (strlen($word)>75) { - $content[$key] = str_replace($pattern, $replace, $content[$key]); + if (!(strpos($word,',')===false) && strpos($word,', ')===false) $word = str_replace(',',', ',$word); + if (!(strpos($word,'.')===false) && strpos($word,'. ')===false) $word = str_replace('.','. ',$word); + $word = wordwrap($word, 75, ' ', true); } + $ckarray[] =$word; } - $content[$key] = wordwrap($content[$key], 75, ' ', true); + $content[$key] = join(' ',$ckarray); + unset($ckarray); } if (is_numeric($_REQUEST['cat_id'])) { From 671313b3ea5088f60fb28b107615c5312c0b5391 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 30 Jul 2010 13:38:49 +0000 Subject: [PATCH 11/88] quiten error_log if no CSS file for an app --- phpgwapi/inc/class.egw_framework.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpgwapi/inc/class.egw_framework.inc.php b/phpgwapi/inc/class.egw_framework.inc.php index ed6cac8dea..604ac382eb 100644 --- a/phpgwapi/inc/class.egw_framework.inc.php +++ b/phpgwapi/inc/class.egw_framework.inc.php @@ -1166,7 +1166,7 @@ abstract class egw_framework } if (!file_exists(EGW_SERVER_ROOT.$path)) { - error_log(__METHOD__."($app,$name) $path NOT found!"); + //error_log(__METHOD__."($app,$name) $path NOT found!"); return false; } if (!in_array($path,self::$css_include_files)) From 4fc00c03aef1bc5afa3376ef50807401d7fd0bec Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 30 Jul 2010 14:02:19 +0000 Subject: [PATCH 12/88] forgot to translate the label --- etemplate/inc/class.link_widget.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etemplate/inc/class.link_widget.inc.php b/etemplate/inc/class.link_widget.inc.php index 32cb5f30ce..726040b406 100644 --- a/etemplate/inc/class.link_widget.inc.php +++ b/etemplate/inc/class.link_widget.inc.php @@ -420,7 +420,7 @@ class link_widget 'no_app_sel' => !!$extension_data['app'], 'id' => is_array($value) ? $value['current'] : $id, 'query' => is_array($value) ? $value['query'] : '', - 'blur' => $cell['label'] ? $cell['label'] : + 'blur' => $cell['label'] ? lang($cell['label']) : (count($options) == 1 ? lang($app) : lang('Search')), 'extra' => $cell['onchange'] ? ','.self::AJAX_NEED_ONCHANGE : null, // store flang for ajax_search, to display extra_line required by onchange ); From 3dc949d08e09518fdca5a3f70596cd16314af294 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 30 Jul 2010 15:27:51 +0000 Subject: [PATCH 13/88] display readonly link-entry widgets as links to the linked entry --- etemplate/inc/class.link_widget.inc.php | 56 ++++++++++++++++++------- 1 file changed, 40 insertions(+), 16 deletions(-) diff --git a/etemplate/inc/class.link_widget.inc.php b/etemplate/inc/class.link_widget.inc.php index 726040b406..7d46c2d415 100644 --- a/etemplate/inc/class.link_widget.inc.php +++ b/etemplate/inc/class.link_widget.inc.php @@ -219,22 +219,7 @@ class link_widget { foreach ($value as $link) { - $options .= " onMouseOver=\"self.status='".addslashes(html::htmlspecialchars($help))."'; return true;\""; - $options .= " onMouseOut=\"self.status=''; return true;\""; - - if (($popup = egw_link::is_popup($link['app'],'view'))) - { - 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 (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"'; - } - $str .= ($str !== '' ? ', ' : '') . html::a_href( - html::htmlspecialchars(egw_link::title($link['app'],$link['id'])), - egw_link::view($link['app'],$link['id'],$link),'',$options); + $str .= ($str !== '' ? ', ' : '') . self::link2a_href($link,$help); } } $cell['type'] = 'html'; @@ -341,6 +326,15 @@ class link_widget break; case 'link-entry': + if ($cell['readonly'] || $readonlys) + { + if(!is_array($value)) $value = array('app' => $cell['size'],'id' => $value); + $value = self::link2a_href($value,$help); + $cell['type'] = 'html'; + $cell['readonly'] = true; + $extension_data = null; + return true; + } $GLOBALS['egw_info']['flags']['include_xajax'] = true; $tpl = new etemplate('etemplate.link_widget.entry'); $options = $cell['size'] ? explode(',',$cell['size']) : array(); @@ -470,6 +464,36 @@ class link_widget return True; // extra Label is ok } + /** + * return a_href to view a linked entry + * + * @param array $link array with values for keys 'id' and 'app' + * @param string $help='' + * @return string + */ + static function link2a_href(array $link,$help='') + { + if (($popup = egw_link::is_popup($link['app'],'view'))) + { + 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 (etemplate::$request->output_mode == 2 || // we are in a popup + $link['app'] == egw_link::VFS_APPNAME || // or it's a link to an attachment + ($target = egw_link::get_registry($link['app'],'view_target'))) // or explicit target set + { + $options = ' target="'.($target ? $target : '_blank').'"'; + } + if ($help) + { + $options .= " onMouseOver=\"self.status='".addslashes(html::htmlspecialchars($help))."'; return true;\""; + $options .= " onMouseOut=\"self.status=''; return true;\""; + } + return html::a_href( + html::htmlspecialchars(egw_link::title($link['app'],$link['id'])), + egw_link::view($link['app'],$link['id'],$link),'',$options); + } + /** * postprocessing method, called after the submission of the form * From 49db0c8838eb559bf45183befcd66118f4cc2883 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 30 Jul 2010 15:30:46 +0000 Subject: [PATCH 14/88] allow to optional specify a database object (to access addressbooks in other databases) --- addressbook/inc/class.addressbook_bo.inc.php | 13 ++++++++++--- addressbook/inc/class.addressbook_so.inc.php | 16 +++++++++++----- addressbook/inc/class.addressbook_sql.inc.php | 12 +++++++++--- 3 files changed, 30 insertions(+), 11 deletions(-) diff --git a/addressbook/inc/class.addressbook_bo.inc.php b/addressbook/inc/class.addressbook_bo.inc.php index a91c199aa8..7be00f21d6 100755 --- a/addressbook/inc/class.addressbook_bo.inc.php +++ b/addressbook/inc/class.addressbook_bo.inc.php @@ -7,7 +7,7 @@ * @author Ralf Becker * @author Joerg Lehrke * @package addressbook - * @copyright (c) 2005-8 by Ralf Becker + * @copyright (c) 2005-10 by Ralf Becker * @copyright (c) 2005/6 by Cornelius Weiss * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ @@ -144,9 +144,15 @@ class addressbook_bo extends addressbook_so */ protected $delete_history = ''; - function __construct($contact_app='addressbook') + /** + * Constructor + * + * @param string $contact_app='addressbook' used for acl->get_grants() + * @param egw_db $db=null + */ + function __construct($contact_app='addressbook',egw_db $db=null) { - parent::__construct($contact_app); + parent::__construct($contact_app,$db); if ($this->log) { $this->logfile = $GLOBALS['egw_info']['server']['temp_dir'].'/log-addressbook_bo'; @@ -1207,6 +1213,7 @@ class addressbook_bo extends addressbook_so if($options['start'] || $options['num_rows']) { $limit = array($options['start'], $options['num_rows']); } + $filter = (array)$options['filter']; if ($GLOBALS['egw_info']['user']['preferences']['addressbook']['hide_accounts']) $filter['account_id'] = null; if (($contacts = parent::search($criteria,false,'org_name,n_family,n_given,cat_id','','%',false,'OR', $limit, $filter))) { diff --git a/addressbook/inc/class.addressbook_so.inc.php b/addressbook/inc/class.addressbook_so.inc.php index d76d1fff02..60223ea617 100755 --- a/addressbook/inc/class.addressbook_so.inc.php +++ b/addressbook/inc/class.addressbook_so.inc.php @@ -6,7 +6,7 @@ * @author Cornelius Weiss * @author Ralf Becker * @package addressbook - * @copyright (c) 2005-8 by Ralf Becker + * @copyright (c) 2005-10 by Ralf Becker * @copyright (c) 2005/6 by Cornelius Weiss * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ @@ -205,9 +205,15 @@ class addressbook_so var $sodistrib_list; var $backend; - function __construct($contact_app='addressbook') + /** + * Constructor + * + * @param string $contact_app='addressbook' used for acl->get_grants() + * @param egw_db $db=null + */ + function __construct($contact_app='addressbook',egw_db $db=null) { - $this->db = $GLOBALS['egw']->db; + $this->db = is_null($db) ? $GLOBALS['egw']->db : $db; $this->user = $GLOBALS['egw_info']['user']['account_id']; $this->memberships = $GLOBALS['egw']->accounts->memberships($this->user,true); @@ -244,7 +250,7 @@ class addressbook_so { $this->contact_repository = 'sql-ldap'; } - $this->somain = new addressbook_sql(); + $this->somain = new addressbook_sql($db); if ($this->user) // not set eg. in setup { @@ -303,7 +309,7 @@ class addressbook_so } else { - $this->soextra = new addressbook_sql(); + $this->soextra = new addressbook_sql($db); } $this->customfields = config::get_customfields('addressbook'); diff --git a/addressbook/inc/class.addressbook_sql.inc.php b/addressbook/inc/class.addressbook_sql.inc.php index 56d8840235..4a8cb91622 100644 --- a/addressbook/inc/class.addressbook_sql.inc.php +++ b/addressbook/inc/class.addressbook_sql.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package addressbook - * @copyright (c) 2006-8 by Ralf Becker + * @copyright (c) 2006-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -54,9 +54,15 @@ class addressbook_sql extends so_sql_cf */ var $ab2list_table = 'egw_addressbook2list'; - function __construct() + /** + * Constructor + * + * @param egw_db $db=null + */ + function __construct(egw_db $db=null) { - parent::__construct('phpgwapi','egw_addressbook','egw_addressbook_extra','contact_'); + parent::__construct('phpgwapi','egw_addressbook','egw_addressbook_extra','contact_', + $extra_key='_name',$extra_value='_value',$extra_id='_id',$db); // Get custom fields from addressbook instead of phpgwapi $this->customfields = config::get_customfields('addressbook'); From 4325167239e4834b6b3ee66cd95f1a02da691261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 31 Jul 2010 06:11:05 +0000 Subject: [PATCH 15/88] Fix an invalid link definition in the adodb documentation. --- phpgwapi/inc/adodb/docs/docs-adodb.htm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpgwapi/inc/adodb/docs/docs-adodb.htm b/phpgwapi/inc/adodb/docs/docs-adodb.htm index b4b4f000c0..8b09359c45 100644 --- a/phpgwapi/inc/adodb/docs/docs-adodb.htm +++ b/phpgwapi/inc/adodb/docs/docs-adodb.htm @@ -1266,7 +1266,7 @@ else { the last error or use the PEAR_ERROR_DIE technique.

MetaError and MetaErrMsg

-

If you need error messages that work across multiple databases, then use MetaError(), which returns a virtualized error number, based on PEAR DB's error number system, and MetaErrMsg(). +

If you need error messages that work across multiple databases, then use MetaError(), which returns a virtualized error number, based on PEAR DB's error number system, and MetaErrMsg().

Error Messages

Error messages are outputted using the static method ADOConnnection::outp($msg,$newline=true). From c6a4c409349205ad462b3edc16ffdba7f5effb16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 31 Jul 2010 07:03:15 +0000 Subject: [PATCH 16/88] Fix HTML code in etemplate documentation: - Close 'li' tags - Close 'lt' tags by 'gt' tags - Convert ampersands to 'amp' tags. --- etemplate/doc/reference.html | 458 +++++++++++++++++------------------ 1 file changed, 229 insertions(+), 229 deletions(-) diff --git a/etemplate/doc/reference.html b/etemplate/doc/reference.html index 5435a5871a..4800a1fd98 100644 --- a/etemplate/doc/reference.html +++ b/etemplate/doc/reference.html @@ -25,9 +25,9 @@ there if your are not familiar with the overal concept.

There are two possibilities now to create an eTemplate:

  1. Use the eTemplate-Editor (as descript in the Tutorial) to interactivly - design your template. + design your template.
  2. Write a xml-file in a Syntax similar to XUL (the mozilla UI-interface definition language) and import - it into the database with the eTemplate-Editor + it into the database with the eTemplate-Editor

The xml-interface to the eTemplates

@@ -36,58 +36,58 @@ an eTemplate from the example app in the Tutorial (here are screenshots of the template in the editor and the show-function):

-<?xml version="1.0"?>
-<!-- $Id$ -->
-<overlay>
-  <template id="et_media.edit" template="" lang="" group="" version="">
-    <grid width="100%">
-      <columns>
-        <column/>
-        <column/>
-        <column/>
-        <column/>
-      </columns>
-      <rows>
-        <row>
-          <description options="ib" span="all" value="eTemplates MediaDB" no_lang="1" id="msg"/>
-        </row>
-        <row>
-          <hrule span="all"/>
-        </row>
-        <row>
-          <description span="all"/>
-        </row>
-        <row>
-          <description value="Name"/>
-          <textbox size="100" maxlength="100" span="all" id="name" statustext="here goes the name of the publication / record"/>
-        </row>
-        <row>
-          <description value="Author"/>
-          <textbox size="100" maxlength="100" span="all" id="author" statustext="please use Name, First Name"/>
-        </row>
-        <row>
-          <description value="Type"/>
-          <menulist span="all" statustext="select the type fitting most">
-            <menupopup id="type"/>
-          </menulist>
-        </row>
-        <row>
-          <description value="Description"/>
-          <textbox ="" cols="3" rows="100" span="all" id="descr" statustext="we have a fulltext search using that description"/>
-        </row>
-        <row>
-        <description span="all"/>
-        </row>
-        <row>
-          <button label="Read" id="read" statustext="reads or searches for entries matching the criteria above"/>
-          <button label="Save" id="save" statustext="saves the change to the db"/>
-          <button label="Cancel" id="cancel" statustext="clears the form, without changing anything"/>
-          <button label="Delete" id="delete" statustext="deletes an entry"/>
-        </row>
-      </rows>
-    </grid>
-  </template>
-</overlay>
+<?xml version="1.0"?>
+<!-- $Id$ -->
+<overlay>
+  <template id="et_media.edit" template="" lang="" group="" version="">
+    <grid width="100%">
+      <columns>
+        <column/>
+        <column/>
+        <column/>
+        <column/>
+      </columns>
+      <rows>
+        <row>
+          <description options="ib" span="all" value="eTemplates MediaDB" no_lang="1" id="msg"/>
+        </row>
+        <row>
+          <hrule span="all"/>
+        </row>
+        <row>
+          <description span="all"/>
+        </row>
+        <row>
+          <description value="Name"/>
+          <textbox size="100" maxlength="100" span="all" id="name" statustext="here goes the name of the publication / record"/>
+        </row>
+        <row>
+          <description value="Author"/>
+          <textbox size="100" maxlength="100" span="all" id="author" statustext="please use Name, First Name"/>
+        </row>
+        <row>
+          <description value="Type"/>
+          <menulist span="all" statustext="select the type fitting most">
+            <menupopup id="type"/>
+          </menulist>
+        </row>
+        <row>
+          <description value="Description"/>
+          <textbox ="" cols="3" rows="100" span="all" id="descr" statustext="we have a fulltext search using that description"/>
+        </row>
+        <row>
+        <description span="all"/>
+        </row>
+        <row>
+          <button label="Read" id="read" statustext="reads or searches for entries matching the criteria above"/>
+          <button label="Save" id="save" statustext="saves the change to the db"/>
+          <button label="Cancel" id="cancel" statustext="clears the form, without changing anything"/>
+          <button label="Delete" id="delete" statustext="deletes an entry"/>
+        </row>
+      </rows>
+    </grid>
+  </template>
+</overlay>
 

The tags / widget-names and attributes / parameters used are as close as possible to XUL. For more information about XUL refer to www.xulplanet.com or the Mozilla docs @@ -95,22 +95,22 @@ information about XUL refer to www.xulplanet.

Please keep in mind that the xml-files used to store the eTemplates are only similar to XUL and implement only a subset of XUL. Here are the main differences:

    -
  • only certain widgets and widget attributes are implemented +
  • only certain widgets and widget attributes are implemented
  • xul-files can contain the actual content or refer to it via a datasources (RDF's) and use a - different template syntax to fill in content from a variable: <label value="?label"/>
    + different template syntax to fill in content from a variable: <label value="?label"/>
    eTemplates get there content from an array passed to the exec or show-function of the template-object - and reference to the content by the id / name-field of each widget. + and reference to the content by the id / name-field of each widget.
  • xul-files can contain an unlimited number of nested elements, the xml-root-node of an eTemplates has to - be an overlay, containing multiple (non-xul) <template>'s. That templates can contain now (HEAD) - a tree of other widgets, but not other templates direct. You can use <template id="app.template_name" /> - to load an other template by its name. + be an overlay, containing multiple (non-xul) <template>'s. That templates can contain now (HEAD) + a tree of other widgets, but not other templates direct. You can use <template id="app.template_name" /> + to load an other template by its name.

Like XUL the eTemplate-xml-files are quite strict with the xml-syntax:

    -
  • All tags and attributes must be written in lowercase -
  • All strings must be double quoted, like id="string" -
  • Every XUL widget must use close tags (either <tag></tag> or <tag/>) to be well-formed -
  • All attributes must have a value, no <tag attr> it has to be <tag attr="1"> +
  • All tags and attributes must be written in lowercase
  • +
  • All strings must be double quoted, like id="string"
  • +
  • Every XUL widget must use close tags (either <tag></tag> or <tag/>) to be well-formed
  • +
  • All attributes must have a value, no <tag attr> it has to be <tag attr="1">

@@ -161,12 +161,12 @@ implement only a subset of XUL. Here are the main differences:

$cont the content-array the (sub-)template, on auto-repeated row's this could eg. be used to generate button-names with id-values in it: "del[$cont[id]]" expands to "del[123]", - if $cont = array('id' => 123) + if $cont = array('id' => 123) $row_cont the sub-array indexed by $row of the content-array, on auto-repeated row's this could eg. be used to generate button-names with id-values in it: "del[$row_cont[id]]" expands to "del[123]", - if $cont = array('1' => array('id' => 123),'2' => array('id' => 456)) and $row = 1 + if $cont = array('1' => array('id' => 123),'2' => array('id' => 456)) and $row = 1 $c_
$col_
$row_
are the respective values of the previous template-inclusion, @@ -375,7 +375,7 @@ implement only a subset of XUL. Here are the main differences:

Label - <description /> + <description /> yes label @@ -385,7 +385,7 @@ implement only a subset of XUL. Here are the main differences:

Options has 5 comma-separated fields:
1. if it contains a 'i' and/or a 'b' the content (not the label) is rendered in italic and/or bold.
- 2. link: if set to a menuaction string or an array with get-params (via the content-arry), + 2. link: if set to a menuaction string or an array with get-params (via the content-arry), a link to that methode = app.class.method is put around the label
3. if set URLs in the content get activated
4. name of form-element the label is for: gives focus to that element if the label gets clicked
@@ -396,12 +396,12 @@ implement only a subset of XUL. Here are the main differences:

Text - <textbox /> + <textbox /> yes text a single-line input field for text
- In the html-UI this is rendered as <input ...>

+ In the html-UI this is rendered as <input ...>

Options has 3 comma-separated fields:
xml: size: the length in chars of the input-field
xml: maxlength: the maximum length of the input
@@ -410,12 +410,12 @@ implement only a subset of XUL. Here are the main differences:

Integer - <textbox
type="int" /> + <textbox
type="int" /> ? int a input-field to enter an integer
- In the html-UI this is rendered as <input ...>. The input-validation is done at the moment only on server-side, + In the html-UI this is rendered as <input ...>. The input-validation is done at the moment only on server-side, clientside validation and input-restriction to only numbers is planed.

Options has 3 comma-separated fields:
xml: min: minimum value, default none, empty values are Ok, as long as needed is not set
@@ -425,12 +425,12 @@ implement only a subset of XUL. Here are the main differences:

Float - <textbox
type="float"/> + <textbox
type="float"/> ? float a input-field to enter a float
- In the html-UI this is rendered as <input ...>. The input-validation is done at the moment only on server-side, + In the html-UI this is rendered as <input ...>. The input-validation is done at the moment only on server-side, clientside validation and input-restriction to only numbers is planed.

Options has 4 comma-separated fields:
xml: min: minimum value, default none, empty values are Ok, as long as needed is not set
@@ -441,12 +441,12 @@ implement only a subset of XUL. Here are the main differences:

Textarea - <textbox
multiline="true" /> + <textbox
multiline="true" /> yes textarea a multiline input-field for text
- In the html-UI this is rendered as <textarea ...>.

+ In the html-UI this is rendered as <textarea ...>.

Options has 2 comma-separated fields:
xml: cols: the width of the field in chars
xml: rows: the number of rows @@ -454,12 +454,12 @@ implement only a subset of XUL. Here are the main differences:

Formatted Text
(HTML) - <htmlarea /> + <htmlarea /> no htmlarea a multiline input-field for formatted (HTML) text
- In the html-UI this is rendered as <textarea ...> and the HTMLarea javascript editor is used.

+ In the html-UI this is rendered as <textarea ...> and the HTMLarea javascript editor is used.

Options has 5 comma-separated fields:
xml: mode: {ascii|simple|extended|advanced}
xml: height: height of htmlarea
@@ -470,15 +470,15 @@ implement only a subset of XUL. Here are the main differences:

Checkbox - <checkbox /> + <checkbox /> yes checkbox a widget that can be checked or unchecked
- In the html-UI this is rendered as <input type="checkbox" ...>. -

Multiple checkboxes can have an identical name ending with [], in that case the value will be an array with the set_value's - of the checked boxes. You can use a button with a custom javascript onclick action of eg. - "toggle_all(this.form,form::name('nm[rows][checkbox][]')); return false;" and a set_value of "$row_cont[id]" to toggle + In the html-UI this is rendered as <input type="checkbox" ...>. +

Multiple checkboxes can have an identical name ending with [], in that case the value will be an array with the set_value's + of the checked boxes. You can use a button with a custom javascript onclick action of eg. + "toggle_all(this.form,form::name('nm[rows][checkbox][]')); return false;" and a set_value of "$row_cont[id]" to toggle all checkboxes in the lines of a nextmatch widget. The form::name( ) function translate the name used in the template into the name used in the form. If the button is an image-button, check needed to render it as button and not as image with link, which has no this.form property!

@@ -491,12 +491,12 @@ implement only a subset of XUL. Here are the main differences:

Radiobutton - <radio /> + <radio /> ? radio a widget in a group of which only one can be checked
- In the html-UI this is rendered as <input type="radio" ...>
+ In the html-UI this is rendered as <input type="radio" ...>
Unlike XUL (and like html) the radio-buttons are grouped by giving them the same name / id.
Options: [set_value][,ro_true[,ro_false]]
set_value: which value in the content represents the checked state, default=1
@@ -507,15 +507,15 @@ implement only a subset of XUL. Here are the main differences:

Submitbutton - <button image="img.gif" ro_image="img-grey.gif" /> + <button image="img.gif" ro_image="img-grey.gif" /> yes button a button to submit the form / end the dialog
- In the html-UI this is rendered as <input type="submit" ...>.
+ In the html-UI this is rendered as <input type="submit" ...>.
If a button is set readonly (via seting its id in the $readonlys array passed to exec) it is not rendered at all (if no ro_image is given), like it would be disabled.

- needed: if set and the user has JavaScript enabled the button is renderd as a link around the label + needed: if set and the user has JavaScript enabled the button is renderd as a link around the label and a hidden input to set id if the link is clicked.
Options xml: image, ro_image: Image to use instead of a Button with a label. There will be no button around the image. If a ro_image is given (separated by a comma in the editors options) @@ -523,35 +523,35 @@ implement only a subset of XUL. Here are the main differences:

onclick: specify some java-script to be called if the button gets pressed/clicked:
a) general javascript: "window.close();"
b) confirmation: "return window.confirm('');" (message get run through lang()!)
- c) popup: app.class.func&id=$cont[id],target(default _blank),width (default 600),height (default 450) - You can use $cont[] or $row_cont[] (note no quotes!) to pass further information to the popup + c) popup: app.class.func&id=$cont[id],target(default _blank),width (default 600),height (default 450) + You can use $cont[] or $row_cont[] (note no quotes!) to pass further information to the popup via the content array.) Button - <buttononly image="img.gif" ro_image="img-grey.gif" /> + <buttononly image="img.gif" ro_image="img-grey.gif" /> no buttononly a button
- Same as Submitbutton but it is rendered as <input type="button" ...> in the html-UI + Same as Submitbutton but it is rendered as <input type="button" ...> in the html-UI Horizonatal Rule - <hrule /> + <hrule /> no hrule a horizontal rule / line
- In the html-UI this is rendered as <hr ...>
+ In the html-UI this is rendered as <hr ...>
Options can contain a width of the rule, default is 100% Template - <template id="app.name" content="subarr" /> + <template id="app.name" content="subarr" /> yes template @@ -565,7 +565,7 @@ implement only a subset of XUL. Here are the main differences:

Image - <image src="foo.gif" label="Get a foo" options="app.class.method" /> + <image src="foo.gif" label="Get a foo" options="app.class.method" /> yes image @@ -582,16 +582,16 @@ implement only a subset of XUL. Here are the main differences:

Selectbox - <menulist>
-   <menupopup id="name" options="Select one" />
- </menulist>

+ <menulist>
+   <menupopup id="name" options="Select one" />
+ </menulist>

multiselect: options > 1
- <listbox rows="#"/>

+ <listbox rows="#"/>

Examples for predefined selectboxes:

- <listbox type="select-cat" rows="5"/>

- <menulist>
-   <menupopup type="select-account" options="All,both,2"/>
- </menulist>

+ <listbox type="select-cat" rows="5"/>

+ <menulist>
+   <menupopup type="select-account" options="All,both,2"/>
+ </menulist>

@@ -601,11 +601,11 @@ implement only a subset of XUL. Here are the main differences:

shows a selectbox
The content of the selectbox / the options have to be in an array which can be in 2 locations:
    -
  1. in $content["options-$name"] +
  2. in $content["options-$name"]
  3. or in an separate array only for select-box-options under the index name, this array is passed - to the exec or show function of the etemplate-class + to the exec or show function of the etemplate-class
- Options in the editor: if set and > 1 the selectbox is a multiselection with options number of lines

+ Options in the editor: if set and > 1 the selectbox is a multiselection with options number of lines

xml: rows: only for <listbox>: number of rows to show

xml options: only for <menupopup/>: textual label for a first Row, e.g. 'All' or 'None' (id will be ''), additional attr see sub-types

@@ -621,7 +621,7 @@ implement only a subset of XUL. Here are the main differences:

as you expect by the name
select-year, select-month, select-day:
options for year: ,start,end (start and end can be a number of - years from now or if > 100 a absolut year)
+ years from now or if > 100 a absolut year)
select-number:
Select a number out of a range specified by the options-field:
,{start (default=1)},{end (incl., default=10)},{decrement (default={padding zeros}1)},{suffix}.
@@ -636,7 +636,7 @@ implement only a subset of XUL. Here are the main differences:

FileUpload - <file id="name"/>
+ <file id="name"/>
no file @@ -649,33 +649,33 @@ implement only a subset of XUL. Here are the main differences:

Date - <date options="Y-m-d,1"/>

- <date type="date-time"/>

- <date type="date-timeonly" options="H:i"/>

- <date type="date-houronly"/>

- <date type="date-duration"/> + <date options="Y-m-d,1"/>

+ <date type="date-time"/>

+ <date type="date-timeonly" options="H:i"/>

+ <date type="date-houronly"/>

+ <date type="date-duration"/> no date Date-/Time-input via selectboxes or a field for the year
The order of the input-fields is determined by the prefs of the user.
- Options: [datetime-storage-format] [,&1=year-no-selectbox|&2=today-button|&4=one-min-steps|&8=ro-suppress-0h0]
- datetime-storage-format is the format, in which the date is stored in the variable: - empty means an unix-timestamp (in GMT), or a string containing the letters Y, m, - d, H, i plus separators, eg. 'Y-m-d': 2002-12-31. The storage format + Options: [datetime-storage-format] [,&1=year-no-selectbox|&2=today-button|&4=one-min-steps|&8=ro-suppress-0h0]
+ datetime-storage-format is the format, in which the date is stored in the variable: + empty means an unix-timestamp (in GMT), or a string containing the letters Y, m, + d, H, i plus separators, eg. 'Y-m-d': 2002-12-31. The storage format for times is always 24h or timestamp with date 1.1.1970 (if no date used). (This has nothing to do with the format of the display, which is only determined by the users preferences.)
- year-no-selectbox if set (&1) an int-widget (input-field) is used instead of a + year-no-selectbox if set (&1) an int-widget (input-field) is used instead of a select-year widget.
- today-button: if set (&2) a [Today] button is displayed which sets the fields to + today-button: if set (&2) a [Today] button is displayed which sets the fields to the up-to-date date (via javascript)
- one-min-steps: if set (&4) the minute-selectbox uses one minutes steps, default 5min steps
- ro-suppress-0h0: if set (&8) the time is suppressed for readonly and a time of 0h0
- day-of-week-prefix: if set (&16) readonly dates get prefixed with the day of week
- week-number-prefix: if set (&32) readonly dates get prefixed with lang('Wk') & weeknumber
+ one-min-steps: if set (&4) the minute-selectbox uses one minutes steps, default 5min steps
+ ro-suppress-0h0: if set (&8) the time is suppressed for readonly and a time of 0h0
+ day-of-week-prefix: if set (&16) readonly dates get prefixed with the day of week
+ week-number-prefix: if set (&32) readonly dates get prefixed with lang('Wk') & weeknumber
Sub-widgets: date-time: a date and a time and date-timeonly or date-houronly: only a time / hour
- These widgets allow the input of times too or only, they use 12h am/pm or 24h format as + These widgets allow the input of times too or only, they use 12h am/pm or 24h format as specified in the user prefs.
If readonly is set, this widget can be used to display a date, without the need to convert it.

Duration a floating point input with an optional selectbox for the unit (hours or days)
@@ -689,18 +689,18 @@ implement only a subset of XUL. Here are the main differences:

VBox, HBox, Box - <vbox>
-   <widget ...>
-   <widget ...>
- </vbox>

- <hbox span="all">
-   <widget ...>
-   <widget ...>
- </hbox>

- <box orient="horizontal">
-   <widget ...>
-   <widget ...>
- </box> + <vbox>
+   <widget ...>
+   <widget ...>
+ </vbox>

+ <hbox span="all">
+   <widget ...>
+   <widget ...>
+ </hbox>

+ <box orient="horizontal">
+   <widget ...>
+   <widget ...>
+ </box> yes vbox, hbox, box @@ -718,11 +718,11 @@ implement only a subset of XUL. Here are the main differences:

GroupBox - <groupbox>
-   <caption label="Legend"/>
-   <widget ...>
-   <widget ...>
- </groupbox>

+ <groupbox>
+   <caption label="Legend"/>
+   <widget ...>
+   <widget ...>
+ </groupbox>

yes groupbox @@ -738,16 +738,16 @@ implement only a subset of XUL. Here are the main differences:

Tabs - <tabbox id="name">
-   <tabs>
-     <tab label="Tab 1" statustext="Help"/>
+ <tabbox id="name">
+   <tabs>
+     <tab label="Tab 1" statustext="Help"/>
    ...
-   </tabs>
-   <tabpanels>
-     <grid id="app.name.tab1"/>
+   </tabs>
+   <tabpanels>
+     <grid id="app.name.tab1"/>
    ...
-   </tabpanels>
- </tabbox> +   </tabpanels>
+ </tabbox> yes tab @@ -765,7 +765,7 @@ implement only a subset of XUL. Here are the main differences:

Manual - <manual> + <manual> no manual @@ -778,19 +778,19 @@ implement only a subset of XUL. Here are the main differences:

Custom fields - <custom_fields> + <custom_fields> no custom_fields - display custom fields: the fields can be configured with admin.customfields.edit&appname={app}

+ display custom fields: the fields can be configured with admin.customfields.edit&appname={app}

The indexes of the custom fields in content are prefixed with a hash (#). NextMatch - <nextmatch options="notes.index.rows" id="nm"/> + <nextmatch options="notes.index.rows" id="nm"/> yes tab @@ -805,57 +805,57 @@ implement only a subset of XUL. Here are the main differences:

 $content[$id] = array(		// I = value set by the app, 0 = value on return / output
-	'get_rows'       =>	// I  method/callback to request the data for the rows eg. 'notes.bo.get_rows'
-	'filter_label'   =>	// I  label for filter    (optional)
-	'filter_help'    =>	// I  help-msg for filter (optional)
-	'no_filter'      => True// I  disable the 1. filter
-	'no_filter2'     => True// I  disable the 2. filter (params are the same as for filter)
-	'no_cat'         => True// I  disable the cat-selectbox
-	'cat_app'        =>     // I  application the cat's should be from, default app in get_rows
-	'template'       =>	// I  template to use for the rows, if not set via options
-	'header_left'    =>	// I  template to show left of the range-value, left-aligned (optional)
-	'header_right'   =>	// I  template to show right of the range-value, right-aligned (optional)
-	'bottom_too'     => True// I  show the nextmatch-line (arrows, filters, search, ...) again after the rows
-	'never_hide'     => True// I  never hide the nextmatch-line if less then maxmatch entrie
-	'lettersearch'   => True// I  show a lettersearch
-	'searchletter'   =>	// I0 active letter of the lettersearch or false for [all]
-	'start'          =>	// IO position in list
-	'num_rows'       =>	// IO number of rows to show, defaults to maxmatches from the general prefs
-	'cat_id'         =>	// IO category, if not 'no_cat' => True
-	'search'         =>	// IO search pattern
-	'order'          =>	// IO name of the column to sort after (optional for the sortheaders)
-	'sort'           =>	// IO direction of the sort: 'ASC' or 'DESC'
-	'col_filter'     =>	// IO array of column-name value pairs (optional for the filterheaders)
-	'filter'         =>	// IO filter, if not 'no_filter' => True
-	'filter_no_lang' => True// I  set no_lang for filter (=dont translate the options)
-	'filter_onchange'=> 'this.form.submit();'// I onChange action for filter, default: this.form.submit();
-	'filter2'        =>	// IO filter2, if not 'no_filter2' => True
-	'filter2_no_lang'=> True// I  set no_lang for filter2 (=dont translate the options)
-	'filter2_onchange'=> 'this.form.submit();'// I onChange action for filter, default: this.form.submit();
-	'rows'           =>	//  O content set by callback
-	'total'          =>	//  O the total number of entries
-	'sel_options'    =>	//  O additional or changed sel_options set by the callback and merged into $tmpl->sel_options
-	'no_columnselection' => // I  turns off the columnselection completly, turned on by default
-	'columnselection-pref' => // I  name of the preference (plus 'nextmatch-' prefix), default = template-name
-	'default_cols'   => 	// I  columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
-	'options-selectcols' => // I  array with name/label pairs for the column-selection, this gets autodetected by default. 
-					A name => false hides the column from the column-selection.  
+	'get_rows'       =>	// I  method/callback to request the data for the rows eg. 'notes.bo.get_rows'
+	'filter_label'   =>	// I  label for filter    (optional)
+	'filter_help'    =>	// I  help-msg for filter (optional)
+	'no_filter'      => True// I  disable the 1. filter
+	'no_filter2'     => True// I  disable the 2. filter (params are the same as for filter)
+	'no_cat'         => True// I  disable the cat-selectbox
+	'cat_app'        =>     // I  application the cat's should be from, default app in get_rows
+	'template'       =>	// I  template to use for the rows, if not set via options
+	'header_left'    =>	// I  template to show left of the range-value, left-aligned (optional)
+	'header_right'   =>	// I  template to show right of the range-value, right-aligned (optional)
+	'bottom_too'     => True// I  show the nextmatch-line (arrows, filters, search, ...) again after the rows
+	'never_hide'     => True// I  never hide the nextmatch-line if less then maxmatch entrie
+	'lettersearch'   => True// I  show a lettersearch
+	'searchletter'   =>	// I0 active letter of the lettersearch or false for [all]
+	'start'          =>	// IO position in list
+	'num_rows'       =>	// IO number of rows to show, defaults to maxmatches from the general prefs
+	'cat_id'         =>	// IO category, if not 'no_cat' => True
+	'search'         =>	// IO search pattern
+	'order'          =>	// IO name of the column to sort after (optional for the sortheaders)
+	'sort'           =>	// IO direction of the sort: 'ASC' or 'DESC'
+	'col_filter'     =>	// IO array of column-name value pairs (optional for the filterheaders)
+	'filter'         =>	// IO filter, if not 'no_filter' => True
+	'filter_no_lang' => True// I  set no_lang for filter (=dont translate the options)
+	'filter_onchange'=> 'this.form.submit();'// I onChange action for filter, default: this.form.submit();
+	'filter2'        =>	// IO filter2, if not 'no_filter2' => True
+	'filter2_no_lang'=> True// I  set no_lang for filter2 (=dont translate the options)
+	'filter2_onchange'=> 'this.form.submit();'// I onChange action for filter, default: this.form.submit();
+	'rows'           =>	//  O content set by callback
+	'total'          =>	//  O the total number of entries
+	'sel_options'    =>	//  O additional or changed sel_options set by the callback and merged into $tmpl->sel_options
+	'no_columnselection' => // I  turns off the columnselection completly, turned on by default
+	'columnselection-pref' => // I  name of the preference (plus 'nextmatch-' prefix), default = template-name
+	'default_cols'   => 	// I  columns to use if there's no user or default pref (! as first char uses all but the named columns), default all columns
+	'options-selectcols' => // I  array with name/label pairs for the column-selection, this gets autodetected by default.
+					A name => false hides the column from the column-selection.
 					To completely hide a column, you need to use the Grid column attributes - disabled in eTemplate, and have your get_rows function
-					set a key that matches.  
-					For example, if your column name is private_phone, use the eTemplate editor to set column disabled to @no_private_phone, 
+					set a key that matches.
+					For example, if your column name is private_phone, use the eTemplate editor to set column disabled to @no_private_phone,
 					and have your get_rows function set $rows['no_private_phone'] = true (or some calculated condition).
 					
-	'return'         =>     // IO allows to return something from the get_rows function if $query is a var-param!
-	'csv_fields'     =>	// I  false=disable csv export, true or unset=enable it with auto-detected fieldnames, 
-					or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type)
+	'return'         =>     // IO allows to return something from the get_rows function if $query is a var-param!
+	'csv_fields'     =>	// I  false=disable csv export, true or unset=enable it with auto-detected fieldnames,
+					or array with name=>label or name=>array('label'=>label,'type'=>type) pairs (type is a eT widget-type)
 );
 
 /*
  * example: the get_rows function from notes.bo.get_rows (has to be in public_functions !)
  */
-function get_rows($query,&$rows,&$readonlys)
+function get_rows($query,&$rows,&$readonlys)
 {
-	$rows = $this->read($query['start'],$query['search'],$query['filter'],$query['cat_id']);
+	$rows = $this->read($query['start'],$query['search'],$query['filter'],$query['cat_id']);
 	if (!is_array($rows))
 	{
 		$rows = array( );
@@ -863,16 +863,16 @@ function get_rows($query,&$rows,&$readonlys)
 	$readonlys = array( );	// set readonlys to enable/disable our edit/delete-buttons
 	while (list($n,$note) = each($rows))
 	{
-		if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_EDIT))
+		if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_EDIT))
 		{
 			$readonlys["edit[$note[id]]"] = True;
 		}
-		if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_DELETE))
+		if (!$this->check_perms($this->grants[$note['owner_id']],PHPGW_ACL_DELETE))
 		{
 			$readonlys["delete[$note[id]]"] = True;
 		}
 	}
-	return $this->total_records;
+	return $this->total_records;
 }
 
 /*
@@ -882,35 +882,35 @@ function index($content = 0)
 {
 	if (!is_array($content))
 	{
-		$content = array('nm' => $this->session_data); // restore settings from the session
+		$content = array('nm' => $this->session_data); // restore settings from the session
 	}
 	if (isset($content['nm']['rows']))		// one of the buttons in the rows is pressed
 	{
-		$this->session_data = $values['nm'];	// save the settings in the session
-		unset($this->session_data['rows']);	// we dont want to save the content of the rows
-		$this->save_sessiondata();
+		$this->session_data = $values['nm'];	// save the settings in the session
+		unset($this->session_data['rows']);	// we dont want to save the content of the rows
+		$this->save_sessiondata();
 
 		if (isset($values['nm']['rows']['edit']))
 		{
 			list($id) = each($values['nm']['rows']['edit']);
-			return $this->edit($id);
+			return $this->edit($id);
 		}
 		elseif (isset($values['nm']['rows']['delete']))
 		{
 			list($id) = each($values['nm']['rows']['delete']);
-			return $this->delete($id);
+			return $this->delete($id);
 		}
 	}
 	$values['nm']['options-filter'] = array (	// set up the data for our filter
-		'all'			=> 'Show all',
-		'public'		=> 'Only yours',
-		'private'	=> 'Private'
+		'all'			=> 'Show all',
+		'public'		=> 'Only yours',
+		'private'	=> 'Private'
 	);
 	$values['nm']['get_rows'] = 'notes.bo.get_rows';
 	$values['nm']['no_filter2'] = True;		// disable the 2. filter
 
-	$this->tpl->read('notes.index');
-	$this->tpl->exec('notes.ui.index',$values);
+	$this->tpl->read('notes.index');
+	$this->tpl->exec('notes.ui.index',$values);
 }
 
@@ -918,10 +918,10 @@ function index($content = 0) Nextmatch-
SortHeader

Nextmatch-
FilterHeader

Nextmatch-
Custom FilterHeader

Nextmatch-
AccountFilter
- <nextmatch type="nextmatch-sortheader" id="col-name" options="DESC" label="ColLabel"/>

- <nextmatch type="nextmatch-filterheader" id="col-name"/>

- <nextmatch type="nextmatch-customfilter" id="col-name" options="select-precent"/>

- <nextmatch type="nextmatch-accountfilter" id="col-name"/> + <nextmatch type="nextmatch-sortheader" id="col-name" options="DESC" label="ColLabel"/>

+ <nextmatch type="nextmatch-filterheader" id="col-name"/>

+ <nextmatch type="nextmatch-customfilter" id="col-name" options="select-precent"/>

+ <nextmatch type="nextmatch-accountfilter" id="col-name"/> no nextmatch-
sortheader

nextmatch-
filterheader

nextmatch-
customfilter

nextmatch-
accountfilter

nextmatch-
header @@ -930,7 +930,7 @@ function index($content = 0) nextmatch-sortheader
Widget to be placed as a colum-header in the headerline of a nextmatch-template. It allows, by clicking on it, to order the lines of the nextmatch after a certain column. The column-name is given as name (xml:id) the - label is show as a link of button (no javascript). One can specify a default sorting: options={DESC|ASC} (default=ASC), + label is show as a link of button (no javascript). One can specify a default sorting: options={DESC|ASC} (default=ASC), to be used when the header is clicked for the first time. Consecutive click on the header change the sorting direction, indicated by a little up- or down-arrow. As a second comma-separated parameter one can specify an extra label for the column-selection.

@@ -943,7 +943,7 @@ function index($content = 0) The custom filterheader allows to use other (select-)widgets to filter by them. They have to be specified as the first parameter in the comma-separated options attribute. In all other aspects it is identical to the filterheader.

nextmatch-accountfilter
- The Accountfilter allows to select users (via the prefered user-selection-method) to filter by them. + The Accountfilter allows to select users (via the prefered user-selection-method) to filter by them. It's identical to a nextmatch-customfilter with options="select-account".

nextmatch-header
Just a header-label for a nextmatch column. It names the column for the column for the column-selection (in difference to the label). @@ -956,9 +956,9 @@ function index($content = 0) LinkWidgets - <link type="link-to" id="name"/>

- <link type="link-list" id="name"/>

- <link type="link-string" id="name"/>

+ <link type="link-to" id="name"/>

+ <link type="link-list" id="name"/>

+ <link type="link-string" id="name"/>

no link-to

link-list

link-string @@ -1000,15 +1000,15 @@ $data['links']['to_id'] = $record_id; 'view_id'       =>      'link_id',  // name of the id variable provided to the view function above 'add'   =>      array('menuaction' => 'myapp.ui_myapp.new_entry'),  - // Function to add a new entry + // Function to add a new entry );

Also, make sure that the declared methods are implemented and methods from the UI class are listed in its $public_methods attribute:

 class ui_myapp {
 	var $public_methods = array(
-		'view' => true,
-		'add' => true
+		'view' => true,
+		'add' => true
 	);
 ...
 }
@@ -1021,14 +1021,14 @@ class ui_myapp {
 
 

The Ajax Select is a Combo Box. It lets the user type anything they want, and choose from a list of options that are presented below. The user is not limited to the choices, but there is some checking done. If what they type returns several results, and they don't choose one, for example. You can reject -any values you don't like in your UI code. It is best used where you might normally want to use a selectbox but your list of data is too large. You can have several on one page, but the name +any values you don't like in your UI code. It is best used where you might normally want to use a selectbox but your list of data is too large. You can have several on one page, but the name must be different for each.

Options can be found under the "AJAX Select options" section of the pop-up.

Data Source: the list options, can be any function that can provide data for a nextmatch widget.

Title Source: When an option from the list is selected, the text in the search function is replaced with the result of this function. The ID Field is passed. link_title() functions work well.

ID Field: Data Source is expected to return an array as for a nextmatch, with several columns. This is the key of the column you actually want returned for a value.

Result row template: (Optional) You can provide a custom eTemplate to use for the list options. It should be constructed similarly to a row template for a nextmatch, and will be repeated for each option.

-

Link: (Optional) If the field is read-only, and Link is provided, the widget will turn into a link. Link should look like: perp_ap.ui_perp_supplier.edit&supplier_id=${cont[supplier_id]} where ID Field is supplier_id.

+

Link: (Optional) If the field is read-only, and Link is provided, the widget will turn into a link. Link should look like: perp_ap.ui_perp_supplier.edit&supplier_id=${cont[supplier_id]} where ID Field is supplier_id.

Icon: (Optional) An icon placed to the left of the search box, to help indicate what the user is searching (addresses, suppliers, etc.). It will be automatically resized.

@@ -1037,11 +1037,11 @@ must be different for each.

The following eTemplate Widgets are parsing its content before displaying through htmlspecialchars() to correctly display the content and to gard against malecious data (like scripts etc.):
    -
  • Label -
  • Text, Textarea -
  • Integer, Float -
  • SelectBoxes (it's options-strings) -
  • LinkWidgets: link-to, link-list, link-string +
  • Label
  • +
  • Text, Textarea
  • +
  • Integer, Float
  • +
  • SelectBoxes (it's options-strings)
  • +
  • LinkWidgets: link-to, link-list, link-string
This is done in most cases by the underlaying html-class and not direct in eTemplate.

From a66ae024a93c52e26a4916b5f2ce3cc2c0195af8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 31 Jul 2010 07:51:52 +0000 Subject: [PATCH 17/88] Tracker #2453. Introduce a year view, which uses a fixed month / day grid and displays the events inline (like in day / week view). --- calendar/inc/class.calendar_hooks.inc.php | 1 + calendar/inc/class.calendar_ui.inc.php | 6 + calendar/inc/class.calendar_uiviews.inc.php | 329 ++++++++++++++++++-- calendar/templates/default/app.css | 21 ++ 4 files changed, 331 insertions(+), 26 deletions(-) diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 83970b3aa1..2111e382bd 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -153,6 +153,7 @@ class calendar_hooks 'week' => lang('Weekview'), 'weekN' => lang('Multiple week view'), 'month' => lang('Monthview'), + 'year' => lang('yearview'), 'planner_cat' => lang('Planner by category'), 'planner_user' => lang('Planner by user'), 'listview' => lang('Listview'), diff --git a/calendar/inc/class.calendar_ui.inc.php b/calendar/inc/class.calendar_ui.inc.php index a0f854a9d0..60a04cd28f 100644 --- a/calendar/inc/class.calendar_ui.inc.php +++ b/calendar/inc/class.calendar_ui.inc.php @@ -545,6 +545,7 @@ class calendar_ui 'week' => array('icon'=>'week','text'=>'Weekview','menuaction' => 'calendar.calendar_uiviews.week'), 'weekN' => array('icon'=>'multiweek','text'=>'Multiple week view','menuaction' => 'calendar.calendar_uiviews.weekN'), 'month' => array('icon'=>'month','text'=>'Monthview','menuaction' => 'calendar.calendar_uiviews.month'), + //'year' => array('icon'=>'year','text'=>'yearview','menuaction' => 'calendar.calendar_uiviews.year'), 'planner' => array('icon'=>'planner','text'=>'Group planner','menuaction' => 'calendar.calendar_uiviews.planner','sortby' => $this->sortby), 'list' => array('icon'=>'list','text'=>'Listview','menuaction'=>'calendar.calendar_uilist.listview'), ) as $view => $data) @@ -610,6 +611,11 @@ class calendar_ui 'value' => 'menuaction=calendar.calendar_uiviews.month', 'selected' => $this->view == 'month', ), + array( + 'text' => lang('yearview'), + 'value' => 'menuaction=calendar.calendar_uiviews.year', + 'selected' => $this->view == 'year', + ), array( 'text' => lang('planner by category'), 'value' => 'menuaction=calendar.calendar_uiviews.planner&sortby=category'. diff --git a/calendar/inc/class.calendar_uiviews.inc.php b/calendar/inc/class.calendar_uiviews.inc.php index 3736afdafe..e6721ab775 100644 --- a/calendar/inc/class.calendar_uiviews.inc.php +++ b/calendar/inc/class.calendar_uiviews.inc.php @@ -32,6 +32,7 @@ class calendar_uiviews extends calendar_ui 'week' => True, 'weekN' => True, 'month' => True, + 'year' => True, 'planner' => True, 'index' => True, ); @@ -67,14 +68,14 @@ class calendar_uiviews extends calendar_ui var $timeRow_width = 40; /** - * how many rows per day get displayed, gets set be the timeGridWidget + * how many rows per day get displayed, gets set by the timeGridWidget * * @var int */ var $rowsToDisplay; /** - * height in percent of one row, gets set be the timeGridWidget + * height in percent of one row, gets set by the timeGridWidget * * @var int */ @@ -137,6 +138,7 @@ class calendar_uiviews extends calendar_ui '4day' => lang('Four days view'), 'week' => lang('Weekview'), 'month' => lang('Monthview'), + 'year' => lang('yearview'), 'planner' => lang('Group planner'), ); $GLOBALS['egw_info']['flags']['app_header'] = $GLOBALS['egw_info']['apps']['calendar']['title']. @@ -217,6 +219,9 @@ class calendar_uiviews extends calendar_ui case 'planner': return $group_warning.$this->planner(true); + case 'year': + return $group_warning.$this->year(true); + case 'month': return $group_warning.$this->month(0,true); @@ -320,6 +325,230 @@ class calendar_uiviews extends calendar_ui return $this->month($num,$home); } + /** Month column width (usually 3 or 4) in year view */ + const YEARVIEW_COLS = 3; + + /** + * Displays a year view + * + * @param boolean $home=false if true return content suitable for home-page + */ + function &year($home=false) + { + if ($this->debug > 0) $this->bo->debug_message('uiviews::year date=%2',True,$this->date); + + $content = $this->edit_series(); + + $this->_month_align_year($this->first,$this->last); + + $GLOBALS['egw_info']['flags']['app_header'] .= ': '.$this->year; + + $days =& $this->bo->search(array( + 'start' => $this->first, + 'end' => $this->last, + ) + $this->search_params); + + /* Loop through the week-aligned months. */ + for ($month = 1; $month <= 12; $month++) + { + // The first date entry in the view may be in the last month. + if (($month - 1) % self::YEARVIEW_COLS == 0) + { + $content .= '

'."\n"; + $content .= "\t".'
'."\n"; + } + + $month_start = $this->datetime->get_weekday_start($this->year,$month,1); + // Beginning of the last week in the month + $month_end = $this->datetime->get_weekday_start( + $this->year, + $month, + $this->datetime->days_in_month($month,$this->year)); + // End of the last week in month + $month_end = strtotime("+6 days",$month_end); + + $content .= "\t\t".'
'."\n"; + + // Year Header + $content .= "\t\t\t".'
'."\n"; + if (($month) == 1) + { + $content .= ''; + $content .= html::a_href(html::image('phpgwapi','first',lang('previous'),$options=' alt="<<"'),array( + 'menuaction' => $this->view_menuaction, + 'date' => date('Ymd',strtotime('-1 year',strtotime($this->date))), + )); + $content .= ''."\n"; + } + $content .= "\t\t\t\t".''.lang(adodb_date('F',strtotime("+1 week",$month_start))).''."\n"; + if ($month == self::YEARVIEW_COLS) + { + $content .= ''; + $content .= html::a_href(html::image('phpgwapi','last',lang('next'),$options=' alt=">>"'),array( + 'menuaction' => $this->view_menuaction, + 'date' => date('Ymd',strtotime('+1 year',strtotime($this->date))), + )); + $content .= ''."\n"; + } + $content .= "\t\t\t".'
'."\n"; + + $content .= "\t\t\t".''."\n"; + $content .= "\t\t\t\t".'
'.lang('Wk').'
'."\n"; + // Day Columns, Legend + for ($i = 0; $i <= 6; $i++) + { + $day_date = ($i ? strtotime("+$i days",$month_start) : $month_start); + if (adodb_date('w',$day_date) % 6 == 0) + { + $style = 'cal_year_legend_weekend'; + } + else + { + $style = 'cal_year_legend'; + } + + $content .= "\t\t\t\t".'
'. + lang(adodb_date('D',$day_date)).'
'."\n"; + } + $content .= "\t\t\t".'
'."\n"; + + // Week rows in month + $week_start = $month_start; + for ($week_in_month = 1; $week_in_month <= 6; $week_in_month ++) + { + $content .= "\t\t\t".''."\n"; + + $content .= "\t\t\t\t".'
'."\n"; + $content .= "\t\t\t\t\t". + ''.adodb_date('W',$week_start)."\n"; + $content .= "\t\t\t\t".'
'."\n"; + // Day columns in week row + for ($i = 0; $i <= 6; $i++) + { + $day_date = $i ? strtotime("+$i days",$week_start) : $week_start; + $day_ymd = $this->bo->date2string($day_date); + $eventcount = count($days[$day_ymd]); + $in_month = true; + $css_class = ""; + $this->_day_class_holiday($day_ymd,$class,$holidays,false,false); + if (adodb_date('n',$day_date) != $month) + { + $css_class .= 'cal_year_legend'; + $in_month = false; + } + else + { + $css_class .= 'calEvent calEventAllAccepted'; + if (adodb_date('w',$day_date) % 6 == 0) + { + $css_class .= ' cal_year_weekend'; + } + else + { + if ($holidays) + { + $css_class .= ' calHoliday'; + } + else + { + $css_class .= ' cal_year_free'; + } + } + + if ($day_ymd == $this->bo->date2string($this->bo->now_su)) + { + $css_class .= ' cal_year_today'; + } + } + $content .= "\t\t\t\t".''."\n"; + $content .= "\t\t\t\t".'
'."\n"; + + + if (($in_month) && (count($days[$day_ymd]))) + { + $eventCols = $this->getEventCols($day_ymd,$days[$day_ymd]); + // displaying all event columns of the day + $row_height = 100 / count($eventCols); + $space_left = 4; //% + $space_right = 1; //% + $row_width = 11.5 - $space_left - $space_right; + // settings for time2pos + $this->scroll_to_wdstart = false; + $this->wd_start = 0; + $this->wd_end = 24*60; + $this->granularity_m = 24 * 60; + $this->extraRows = -1; + $this->rowHeight = $row_width; + foreach($eventCols as $n => $eventCol) + { + foreach ($eventCol as $event) + { + $indent = "\t\t\t\t"; + // some fields set by the dayColWidget for the other views + unset($event['whole_day_on_top']); + $data = $this->eventWidget($event,25,$indent,$this->owner,true,'planner_event'); + + $left = ((($i + 1) * 12.5) + 0.5 + $space_left + $this->time2pos($event['start_m'])); + $width = $this->time2pos($event['end_m'] - $event['start_m']); + $color = $data['color'] ? $data['color'] : 'gray'; + + $content .= $indent.'
0,'Padding'=>0)). + '>'."\n".$data['html'].$indent."
\n"; + } + } + } + } + $week_start = strtotime("+1 week",$week_start); + $content .= "\t\t\t".'
'."\n"; + } + $content .= "\t\t".'
'."\n"; + + if (($month) % self::YEARVIEW_COLS == 0) + { + $content .= "\t
\n"; + $content .= "\n"; + } + } + + if (!$home) + { + $this->do_header(); + + echo $content; + } + + return $content; + } + /** * Displays the monthview or a multiple week-view * @@ -414,6 +643,27 @@ class calendar_uiviews extends calendar_ui $last = $this->bo->date2ts($last); } + /** + * Get start and end of a year aligned to full months + * + * @param int &$first timestamp 0h of first day of week containing the first of the current year + * @param int &$last timestamp 23:59:59 of last day of week containg the last day of the current year + */ + function _month_align_year(&$first,&$last) + { + $first = $this->datetime->get_weekday_start($this->year,$this->month=1,$this->day=1); + $last = $this->datetime->get_weekday_start($this->year,$this->month+12, + $this->datetime->days_in_month($this->month+12,$this->year)); + // now we need to calculate the end of the last day of that week + // as simple $last += WEEK_s - 1; does NOT work, if daylight saving changes in that week!!! + $last = $this->bo->date2array($last); + $last['day'] += 6; + $last['hour'] = 23; + $last['min'] = $last['sec'] = 59; + unset($last['raw']); // otherwise date2ts does not calc raw new, but uses it + $last = $this->bo->date2ts($last); + } + /** * Four days view, everythings done by the week-view code ... * @@ -763,7 +1013,7 @@ function open_edit(series) } /** - * Calculates the height of a differenc between 2 times + * Calculates the height of a difference between 2 times * * workday start- and end-time, is taken into account, as well as timeGrids px_m - minutes per pixel param * @@ -927,23 +1177,14 @@ function open_edit(series) } /** - * Creates (if necessary multiple) columns for the events of a day - * - * Uses the eventColWidget to display each column. + * Sorts the events of a day into columns with non-overlapping events, the events + * are already sorted by start-time * * @param string/int $day_ymd date as Ymd - * @param array $events of events to show - * @param int $left start of the widget - * @param int $width width of the widget - * @param string $indent string for correct indention - * @param boolean/string $short_title=True should we add a label (weekday, day) with link to the day-view above each day or string with title - * @param boolean $on_off=false start with row_on or row_off, default false=row_off - * @param int $owner=0 if != 0 owner to add to the add-event link + * @param array &$events events to split into non-overlapping groups */ - function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False,$owner=0) + function getEventCols($day_ymd, &$events) { - if ($this->debug > 1 || $this->debug==='dayColWidget') $this->bo->debug_message('uiviews::dayColWidget(%1,%2,left=%3,width=%4,)',False,$day_ymd,$events,$left,$width); - $day_start = $this->bo->date2ts((string)$day_ymd); // if daylight saving is switched on or off, correct $day_start @@ -952,7 +1193,7 @@ function open_edit(series) { $day_start -= $daylight_diff; } - // sorting the event into columns with none-overlapping events, the events are already sorted by start-time + $eventCols = $col_ends = array(); foreach($events as $event) { @@ -980,6 +1221,26 @@ function open_edit(series) } $eventCols[$c][] = $event; } + return $eventCols; + } + + /** + * Creates (if necessary multiple) columns for the events of a day + * + * Uses the eventColWidget to display each column. + * + * @param string/int $day_ymd date as Ymd + * @param array $events of events to show + * @param int $left start of the widget + * @param int $width width of the widget + * @param string $indent string for correct indention + * @param boolean/string $short_title=True should we add a label (weekday, day) with link to the day-view above each day or string with title + * @param boolean $on_off=false start with row_on or row_off, default false=row_off + * @param int $owner=0 if != 0 owner to add to the add-event link + */ + function dayColWidget($day_ymd,$events,$left,$width,$indent,$short_title=True,$on_off=False,$owner=0) + { + if ($this->debug > 1 || $this->debug==='dayColWidget') $this->bo->debug_message('uiviews::dayColWidget(%1,%2,left=%3,width=%4,)',False,$day_ymd,$events,$left,$width); $html = $indent.'
'."\n"; @@ -995,7 +1256,7 @@ function open_edit(series) ); $this->_day_class_holiday($day_ymd,$class,$holidays); // the weekday and date - if (!$short_title && $holidays) $title .= ': '.$holidays; + if (!$short_title && $holidays) $title .= html::htmlspecialchars(': '.$holidays); if ($short_title === true) { @@ -1027,8 +1288,10 @@ function open_edit(series) $title .= '   '.$day_view; } } - $html .= $indent."\t".'
'. - $title."
\n"; + if (is_bool($short_title) || ($short_title != "")) { + $html .= $indent."\t".'
'.$title."
\n"; + } if ($this->use_time_grid) { @@ -1045,7 +1308,7 @@ function open_edit(series) } } // adding divs to click on for each row / time-span - for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1+$this->extraRows; + for($t = $this->scroll_to_wdstart ? 0 : $this->wd_start,$i = 1 + $this->extraRows; $t <= $this->wd_end || $this->scroll_to_wdstart && $t < 24*60; $t += $this->granularity_m,++$i) { @@ -1079,6 +1342,8 @@ function open_edit(series) } } } + + $eventCols = $this->getEventCols($day_ymd,$events); // displaying all event columns of the day foreach($eventCols as $n => $eventCol) { @@ -1098,26 +1363,35 @@ function open_edit(series) * @param string &$class class to use * @param string &$holidays commaseparted holidays or empty if none * @param boolean $only_weekend=false show only the weekend in header-color, otherwise every second days is shown too + * @param boolean $show_bdays=true If available, also show birthdays (or hide Bdays) + * Note that this is not the place to disable a preference. + * If the preferences allow birthdays to be displayed, they are cached within the holidays structure. + * This setting just suppressing the available data in the view. */ - function _day_class_holiday($day_ymd,&$class,&$holidays,$only_weekend=false) + function _day_class_holiday($day_ymd,&$class,&$holidays,$only_weekend=false,$show_bdays=true) { $class = $holidays = ''; $bday = false; if (isset($this->holidays[$day_ymd])) { + $h = array(); foreach($this->holidays[$day_ymd] as $holiday) { if (isset($holiday['birthyear'])) { - $bday = true; + if ($show_bdays) + { + $bday = true; + $h[] = $holiday['name']; + } } else { $class = 'calHoliday'; + $h[] = $holiday['name']; } - $holidays[] = $holiday['name']; } - $holidays = implode(', ',$holidays); + $holidays = implode(', ',$h); } if (!$class) { @@ -1243,7 +1517,10 @@ function open_edit(series) $small_trigger_width = 120 + 20*count($icons); $corner_radius=$width > $small_trigger_width ? 10 : 5; $header_height=$width > $small_trigger_width ? 19 : 12; // multi_3 icon has a height of 19=16+2*1padding+1border ! - if (!$return_array) $height = $this->times2height($event['start_m'],$event['end_m'],$header_height); + if (!$return_array) + { + $height = $this->times2height($event['start_m'],$event['end_m'],$header_height); + } //$body_height = max(0,$height - $header_height - $corner_radius); $border=1; $headerbgcolor = $color ? $color : '#808080'; diff --git a/calendar/templates/default/app.css b/calendar/templates/default/app.css index 6bf013edc9..a81101a260 100644 --- a/calendar/templates/default/app.css +++ b/calendar/templates/default/app.css @@ -483,6 +483,27 @@ e.g. the div with class calTimeGrid is generated by the timeGridWidget method of .plannerEvent:hover{ cursor: pointer; } + +/* Special colors for the year view */ +.cal_year_legend_weekend { + background-color: #CCCCCC; +} +.cal_year_legend { + background-color: #EFEFEF; +} +.cal_year_free { + background-color: #FFFFCC; + z-index: 0; +} +.cal_year_weekend { + background-color: #F9F9CC; + z-index: 0; +} +.cal_year_today { + border-color: #EE0000; + border-width: 2px; +} + /** * edit series or exception popup used in eventWidget and * delete series and exceptions popup used in edit event From 83e867ef01acd2b6a8e510736211432b77bd5815 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 31 Jul 2010 08:03:32 +0000 Subject: [PATCH 18/88] Disable verbose debug messages on restore. --- phpgwapi/inc/class.db_backup.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpgwapi/inc/class.db_backup.inc.php b/phpgwapi/inc/class.db_backup.inc.php index 0843f228cf..fa0abafe50 100644 --- a/phpgwapi/inc/class.db_backup.inc.php +++ b/phpgwapi/inc/class.db_backup.inc.php @@ -432,7 +432,7 @@ class db_backup $this->schemas = unserialize(trim(substr($line,8))); foreach($this->schemas as $table_name => $schema) { - echo "
$table_name => ".self::write_array($schema,1)."
\n"; + //echo "
$table_name => ".self::write_array($schema,1)."
\n"; $this->schema_proc->CreateTable($table_name,$schema); } // make the schemas availible for the db-class From 86a32e2f8bcd36f3edce79cdbb698e34acdd3816 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hans-J=C3=BCrgen=20Tappe?= Date: Sat, 31 Jul 2010 08:07:21 +0000 Subject: [PATCH 19/88] Tracker #2184. Add "select all" in the account selection popup. --- phpgwapi/inc/class.uiaccountsel.inc.php | 23 ++++++++++++++++++ phpgwapi/templates/default/uiaccountsel.tpl | 27 +++++++++++++++++++-- 2 files changed, 48 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.uiaccountsel.inc.php b/phpgwapi/inc/class.uiaccountsel.inc.php index 228735b604..99c9788e90 100644 --- a/phpgwapi/inc/class.uiaccountsel.inc.php +++ b/phpgwapi/inc/class.uiaccountsel.inc.php @@ -346,6 +346,8 @@ function addOption(id,label,value,do_onchange) $GLOBALS['egw']->template->set_file(array('accounts_list_t' => 'uiaccountsel.tpl')); $GLOBALS['egw']->template->set_block('accounts_list_t','letter_search','letter_search_cells'); $GLOBALS['egw']->template->set_block('accounts_list_t','group_cal','cal'); + $GLOBALS['egw']->template->set_block('accounts_list_t','group_selectAll','selectAllGroups'); + $GLOBALS['egw']->template->set_block('accounts_list_t','groups_multiple','multipleGroups'); $GLOBALS['egw']->template->set_block('accounts_list_t','group_other','other'); $GLOBALS['egw']->template->set_block('accounts_list_t','group_all','all'); @@ -353,12 +355,15 @@ function addOption(id,label,value,do_onchange) $GLOBALS['egw']->template->set_block('accounts_list_t','other_intro','iother'); $GLOBALS['egw']->template->set_block('accounts_list_t','all_intro','iall'); + $GLOBALS['egw']->template->set_block('accounts_list_t','accounts_selectAll','selectAllAccounts'); + $GLOBALS['egw']->template->set_block('accounts_list_t','accounts_multiple','multipleAccounts'); $GLOBALS['egw']->template->set_block('accounts_list_t','accounts_list','list'); $GLOBALS['egw']->template->set_var('font',$GLOBALS['egw_info']['theme']['font']); $GLOBALS['egw']->template->set_var('lang_search',lang('search')); $GLOBALS['egw']->template->set_var('lang_groups',lang('user groups')); $GLOBALS['egw']->template->set_var('lang_accounts',lang('user accounts')); + $GLOBALS['egw']->template->set_var('lang_all',lang('all')); $GLOBALS['egw']->template->set_var('img',common::image('phpgwapi','select')); $GLOBALS['egw']->template->set_var('lang_select_user',lang('Select user')); @@ -418,6 +423,11 @@ function addOption(id,label,value,do_onchange) $GLOBALS['egw']->template->set_var('lang_firstname',lang('firstname')); $GLOBALS['egw']->template->set_var('lang_lastname',lang('lastname')); + if ($multiple) + { + $GLOBALS['egw']->template->fp('multipleGroups','groups_multiple',True); + } + if ($app) { $app_groups = $this->accounts->split_accounts($app,'groups'); @@ -442,6 +452,10 @@ function addOption(id,label,value,do_onchange) if($use == 'both') // allow selection of groups { $GLOBALS['egw']->template->fp('cal','group_cal',True); + $GLOBALS['egw']->template->set_var('js_addAllGroups',"addOption('$element_id','". + $GLOBALS['egw']->common->grab_owner_name($group['account_id'])."','$group[account_id]',".(int)($multiple==1).")". + (!$multiple ? '; window.close();' : ';')); + $GLOBALS['egw']->template->fp('selectAllGroups','group_selectAll',True); } else { @@ -485,6 +499,11 @@ function addOption(id,label,value,do_onchange) $GLOBALS['egw']->template->set_var('lang_firstname', lang("firstname")); $GLOBALS['egw']->template->set_var('lang_lastname', lang("lastname")); + if ($multiple) + { + $GLOBALS['egw']->template->fp('multipleAccounts','accounts_multiple',True); + } + foreach($users as $user) { $GLOBALS['egw']->template->set_var('tr_color',$this->nextmatchs->alternate_row_color($tr_color,True)); @@ -500,6 +519,10 @@ function addOption(id,label,value,do_onchange) (!$multiple ? '; window.close()' : ''), )); $GLOBALS['egw']->template->fp('list','accounts_list',True); + $GLOBALS['egw']->template->set_var('js_addAllAccounts',"addOption('$element_id','". + $GLOBALS['egw']->common->grab_owner_name($user['account_id'])."','$user[account_id]',".(int)($multiple==1).")". + (!$multiple ? '; window.close()' : ';')); + $GLOBALS['egw']->template->fp('selectAllAccounts','accounts_selectAll',True); } $GLOBALS['egw']->template->set_var('accountsel_icon',html::image('phpgwapi','users-big')); diff --git a/phpgwapi/templates/default/uiaccountsel.tpl b/phpgwapi/templates/default/uiaccountsel.tpl index 03bc3ef537..c75147b404 100644 --- a/phpgwapi/templates/default/uiaccountsel.tpl +++ b/phpgwapi/templates/default/uiaccountsel.tpl @@ -23,6 +23,20 @@ } } + function addAllGroups() + { + + {js_addAllGroups} + + } + + function addAllAccounts() + { + + {js_addAllAccounts} + + } + function removeSelectedOptions(id) { openerSelectBox = opener.document.getElementById(id); @@ -134,7 +148,12 @@ - {lang_perm} + {lang_perm} + + + + + @@ -192,7 +211,11 @@ {sort_lid} {sort_firstname} {sort_lastname} -   + + + + + From 5d6e6cdc9c3668fd7a324fbb02f0e177e9f4cbb1 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 31 Jul 2010 09:23:41 +0000 Subject: [PATCH 20/88] new parameter $make_db_name_unique to append a decrementing numeric postfix to db_name and db_user, if db already exists --- setup/inc/class.setup_cmd_database.inc.php | 54 ++++++++++++++++++++-- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/setup/inc/class.setup_cmd_database.inc.php b/setup/inc/class.setup_cmd_database.inc.php index 87f031e19e..cb580b57c2 100644 --- a/setup/inc/class.setup_cmd_database.inc.php +++ b/setup/inc/class.setup_cmd_database.inc.php @@ -19,6 +19,13 @@ class setup_cmd_database extends setup_cmd * Allow to run this command via setup-cli */ const SETUP_CLI_CALLABLE = true; + + /** + * Maximum length of database name (at least for MySQL this is the limit) + * + * @var int + */ + const MAX_DB_NAME_LEN = 16; /** * Instance of egw_db to connect or create the db @@ -41,9 +48,11 @@ class setup_cmd_database extends setup_cmd * @param string $db_root_pw=null * @param string $sub_command='create_db' 'create_db', 'test_db', 'test_db_root' * @param string $db_grant_host='localhost' host/ip of webserver for grant + * @param boolean $make_db_name_unique=false true: if create fails because db exists, + * try creating a unique name by shortening the name and adding a number to it */ function __construct($domain,$db_type=null,$db_host=null,$db_port=null,$db_name=null,$db_user=null,$db_pass=null, - $db_root=null,$db_root_pw=null,$sub_command='create_db',$db_grant_host='localhost') + $db_root=null,$db_root_pw=null,$sub_command='create_db',$db_grant_host='localhost',$make_db_name_unique=false) { if (!is_array($domain)) { @@ -59,9 +68,10 @@ class setup_cmd_database extends setup_cmd 'db_root_pw' => $db_root_pw, 'sub_command' => $sub_command, 'db_grant_host' => $db_grant_host, + 'make_db_name_unique' => $make_db_name_unique, ); } - //echo __CLASS__.'::__construct()'; _debug_array($domain); + //error_log(__METHOD__.'('.array2string($domain).") make_db_name_unique=".array2string($domain['make_db_name_unique'])); admin_cmd::__construct($domain); } @@ -153,12 +163,17 @@ class setup_cmd_database extends setup_cmd * Check and if does not yet exist create the new database and user * * The check will fail if the database exists, but already contains tables + * + * if $this->make_db_name_unique is set, a decrementing nummeric prefix gets + * added to $this->db_name AND $this->db_user, if db already exists. * * @return string with success message * @throws egw_exception_wrong_userinput */ private function create() { + static $try_make_unique = 0; // to limit trials to create a unique name + try { $msg = $this->connect(); } @@ -168,9 +183,36 @@ class setup_cmd_database extends setup_cmd $this->test_db->create_database($this->db_root,$this->db_root_pw,$this->db_charset,$this->db_grant_host); $this->connect(); } - catch(egw_exception_wrong_userinput $e) { - // try connect as root to check if that's the problem + catch(egw_exception_db $e) { // catches failed to create database + // try connect as root to check if wrong root/root_pw is the problem $this->connect($this->db_root,$this->db_root_pw,$this->db_meta); + + // if we should create a db with a unique name (try it only N times, not endless!) + if ($this->make_db_name_unique && $try_make_unique++ < 20) + { + // check if we can connect as root to the db to create --> db exists already + try { + $this->connect($this->db_root,$this->db_root_pw); + // create new db_name by incrementing an existing numeric postfix + if (preg_match('/([0-9]+)$/',$this->db_name,$matches)) + { + $num = (string)(++$matches[1]); + } + else // or adding one starting with 2 + { + $num = '2'; + } + $this->set_defaults['db_name'] = $this->db_name = + $this->set_defaults['db_user'] = $this->db_user = // change user too (otherwise existing user/db could not connect any more!) + substr($this->db_name,0,self::MAX_DB_NAME_LEN-strlen($num)).$num; + + return $this->create(); + } + catch (egw_exception_wrong_userinput $e2) + { + // we can NOT connect to db as root --> ignore exception to give general error + } + } // if not give general error throw new egw_exception_wrong_userinput(lang('Can not create %1 database %2 on %3 for user %4!', $this->db_type,$this->db_name,$this->db_host.($this->db_port?':'.$this->db_port:''),$this->db_user)); @@ -263,7 +305,9 @@ class setup_cmd_database extends setup_cmd if (strpos($this->$name,'$domain') !== false) { // limit names to 16 chars (16 char is user-name limit in MySQL) - $this->set_defaults[$name] = $this->$name = substr(str_replace(array('$domain','.','-'),array($this->domain,'_','_'),$this->$name),0,16); + $this->set_defaults[$name] = $this->$name = + substr(str_replace(array('$domain','.','-'),array($this->domain,'_','_'),$this->$name), + 0,self::MAX_DB_NAME_LEN); } } } From a1c9f68a1e1820a1c7a61ade04da73f5a65109b9 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 1 Aug 2010 10:02:04 +0000 Subject: [PATCH 21/88] remove not used advanced-search-widget --- .../inc/class.advancedsearch_widget.inc.php | 279 ------------------ 1 file changed, 279 deletions(-) delete mode 100644 etemplate/inc/class.advancedsearch_widget.inc.php diff --git a/etemplate/inc/class.advancedsearch_widget.inc.php b/etemplate/inc/class.advancedsearch_widget.inc.php deleted file mode 100644 index b270f40faf..0000000000 --- a/etemplate/inc/class.advancedsearch_widget.inc.php +++ /dev/null @@ -1,279 +0,0 @@ - - * @deprecated Don't use any more, this will be removed after 1.4 release. - * Please manage advanced searches in you app! - * @version $Id$ - */ - - /** - * eTemplate Extension: Advanced Search - * - * $content[$name] = array( - * 'input_template' => app.template - * 'search_method' => app.class.method in so_sql style - * 'colums_to_present' => array with field_name => label - * 'actions' => array with actions for resultset in etemplates style - * can also contain a field 'method' which gets executed with resultset as first param - * 'row_actions' => array with actions for each row - * - * @package etemplate - * @subpackage extensions - * @author Cornelius Weiss - * @license GPL - GNU General Public License - */ - class advancedsearch_widget - { - /** - * @var $public_functions array with exported methods of this class - */ - var $public_functions = array( - 'pre_process' => True, - 'post_process' => True - ); - - /** - * @var $human_name - */ - var $human_name = 'Advanced search'; - - /** - * @var $debug bool - */ - var $debug = False; - - /** - * Constructor of the extension - * - * @param string $ui '' for html - */ - function advancedsearch_widget($ui='') - { - error_log(__LINE__. __FILE__. "@deprecated Don't use any more, this will be removed after 1.4 release. Please manage advanced searches in you app! "); - } - - /** - * 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) - { - $extension_data = is_array($extension_data) ? $extension_data : - (is_array($GLOBALS['egw']->session->appsession('advanced_search_query',$GLOBALS['egw_info']['flags']['currentapp'])) ? - $GLOBALS['egw']->session->appsession('advanced_search_query',$GLOBALS['egw_info']['flags']['currentapp']) : $value); - - $tpl = new etemplate; - $tpl->init('*** generated advanced search widget','','',0,'',0,0); // make an empty template - - if($extension_data['message']) - { - $tpl->add_child($tpl,$message = $tpl->empty_cell('label','message',array( - 'no_lang' => true, - ))); - } - - if (isset($extension_data['result_nm'])) - { - $tpl->add_child($tpl,$new_search_button = $tpl->empty_cell('button','button[new_search]',array( - 'no_lang' => true, - 'label' => lang('New search'), - ))); - $tpl->add_child($tpl, $result_nm = $tpl->empty_cell('nextmatch','result_nm',array( -// 'width' => '800px', - ))); - - $result_rows_tpl = new etemplate; - $result_rows_tpl->init('*** generated rows template for advanced search results','','',0,'',0,0); - $grid =& $result_rows_tpl->children[0]; - foreach((array)$extension_data['colums_to_present'] as $field => $label) - { - if($label == '') continue; - $result_rows_tpl->add_child($grid,$result_nm_header = $result_rows_tpl->empty_cell('nextmatch-sortheader',$field,array( - 'label' => $label, - 'no_lang' => true, - 'span' => ',nmh', - ))); - unset($result_nm_header); - } - if(!empty($extension_data['row_actions'])) - { - $result_rows_tpl->add_child($grid,$result_nm_header = $result_rows_tpl->empty_cell('label','action',array( - 'label' => 'Action', - 'no_lang' => true, - 'span' => ',nmh', - ))); - unset($result_nm_header); - } - $result_rows_tpl->add_child($grid,$rows); - foreach((array)$extension_data['colums_to_present'] as $field => $label) - { - if($label == '') continue; - $result_rows_tpl->add_child($grid,$result_nm_rows = $result_rows_tpl->empty_cell('text','${row}['.$field.']',array( - 'no_lang' => true, - 'readonly' => true, - ))); - unset($result_nm_rows); - } - if(!empty($extension_data['row_actions'])) - { - $row_actions =& $result_rows_tpl->empty_cell('hbox','row_action'); - $result_rows_tpl->add_child($grid,$row_actions); - foreach($extension_data['row_actions'] as $action => $options) - { - $result_rows_tpl->add_child($row_actions, $row_action = $result_rows_tpl->empty_cell($options['type'],$action.'[$row_cont[id]]',$options['options'])); - unset($row_action); - } - } - $value['result_nm'] = array_merge( - $extension_data['result_nm'], - array( - 'no_filter' => true, - 'no_filter2' => true, - 'no_cat' => true, - 'no_search' => true, - 'get_rows' => 'etemplate.advancedsearch_widget.get_rows', - 'search_method' => $extension_data['search_method'], - 'colums_to_present' => $extension_data['colums_to_present'], - 'template' => $result_rows_tpl, - )); - - $tpl->add_child($tpl, $action_buttons = $tpl->empty_cell('hbox','action_buttons')); - foreach ($extension_data['actions'] as $action => $options) - { - $tpl->add_child($action_buttons, $result_button = $tpl->empty_cell($options['type'],'action['.$action.']',$options['options'])); - unset($result_button); - } - } - else - { - $GLOBALS['egw_info']['etemplate']['advanced_search'] = true; - - $tpl->add_child($tpl, $search_template = $tpl->empty_cell('template',$extension_data['input_template'])); - $button_box = $tpl->empty_cell('hbox','button_box'); - $tpl->add_child($tpl, $button_box); - $tpl->add_child($button_box, $op_select = $tpl->empty_cell('select','opt_select',array( - 'sel_options' => array( - 'OR' => 'OR', - 'AND' => 'AND' - ), - 'label' => 'Operator', - 'no_lang' => true, - ))); - $tpl->add_child($button_box, $meth_select = $tpl->empty_cell('select','meth_select',array( - 'sel_options' => array( - '%' => lang('contains'), - false => lang('exact'), - ), - 'no_lang' => true, - 'default' => '%', - ))); - $tpl->add_child($button_box, $search_button = $tpl->empty_cell('button','button[search]',array( - 'label' => 'Search', - ))); - } - $cell['size'] = $cell['name']; - $cell['type'] = 'template'; - $cell['name'] = $tpl->name; - $cell['obj'] =& $tpl; - - // keep the editor away from the generated tmpls - $tpl->no_onclick = true; - return True; - } - - /** - * 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. - * - * @param string $name form-name of the widget - * @param mixed &$value the extension returns here it's input, if there's any - * @param mixed &$extension_data persistent storage between calls or pre- and post-process - * @param boolean &$loop can be set to true to request a re-submision of the form/dialog - * @param object &$tmpl the eTemplate the widget belongs too - * @param mixed &value_in the posted values (already striped of magic-quotes) - * @return boolean true if $value has valid content, on false no content will be returned! - */ - function post_process($name,&$value,&$extension_data,&$loop,&$tmpl,$value_in) - { - //echo 'advancedsearch_widget::post_process->value'; _debug_array($value); - //echo 'advancedsearch_widget::post_process->extension_data'; _debug_array($extension_data); - if(!isset($extension_data['result_nm']['search_values'])) - { - foreach($value as $haystack => $needle) - { - if($needle == '') unset($value[$haystack]); - elseif($haystack{0} != '!' && $needle{0} != '!' && $value['!'.$haystack] == 1 ) $value[$haystack] = '!'.$value[$haystack]; - } - $extension_data['result_nm']['search_values'] = $value; - } - else - { - $extension_data['result_nm'] = array_merge($extension_data['result_nm'],$value['result_nm']); - } - - //we store extension_data in session to save it over a new request - $GLOBALS['egw']->session->appsession('advanced_search_query',$GLOBALS['egw_info']['flags']['currentapp'],$extension_data); - - if(isset($value['action'])) - { - // Also inputfileds etc. could be in actions - foreach($value['action'] as $action => $label) - { - if($extension_data['actions'][$action]['type'] == 'button') - { - $result = $GLOBALS['egw']->session->appsession('advanced_search_result',$GLOBALS['egw_info']['flags']['currentapp']); - $extension_data['message'] = ExecMethod2($extension_data['actions'][$action]['method'],$result); - } - } - } - if(is_array($value['result_nm']['rows'])) - { - // Also inputfileds etc. could be in actions - foreach($value['result_nm']['rows'] as $action => $id) - { - if($extension_data['row_actions'][$action]['type'] == 'button') - { - $result = $GLOBALS['egw']->session->appsession('advanced_search_result',$GLOBALS['egw_info']['flags']['currentapp']); - $extension_data['message'] = ExecMethod2($extension_data['row_actions'][$action]['method'],key($id)); - } - } - } - - if(isset($value['button']['new_search'])) - { - $GLOBALS['egw']->session->appsession('advanced_search_query',$GLOBALS['egw_info']['flags']['currentapp'],false); - $extension_data = ''; - } - } - - function get_rows($query,&$rows,&$readonlys) - { - $order_by = $query['order'] ? $query['order'].' '.$query['sort'] : ''; - $only_keys = implode(',',array_flip($query['colums_to_present'])); - $rows = ExecMethod2($query['search_method'],$query['search_values'],$only_keys, - $order_by,'',$query['search_values']['meth_select'],'',$query['search_values']['opt_select'],$query['start']); - $result = ExecMethod2($query['search_method'],$query['search_values'],$only_keys, - '','',$query['search_values']['meth_select'],'',$query['search_values']['opt_select'],false,'','',false); - // We store the result in session so actions can fetch them here: - $GLOBALS['egw']->session->appsession('advanced_search_result',$GLOBALS['egw_info']['flags']['currentapp'],$result); - return count($result); - - } - } From bb468774f7dd82788e01790a1d98603621901c00 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 1 Aug 2010 12:47:26 +0000 Subject: [PATCH 22/88] quitened warning --- etemplate/inc/class.etemplate.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etemplate/inc/class.etemplate.inc.php b/etemplate/inc/class.etemplate.inc.php index 9a433c7005..2b71430d55 100644 --- a/etemplate/inc/class.etemplate.inc.php +++ b/etemplate/inc/class.etemplate.inc.php @@ -1602,7 +1602,7 @@ class etemplate extends boetemplate break; case 'image': // size: [link],[link_target],[imagemap],[link_popup],[id] $image = $value != '' ? $value : $name; - list($app,$img) = explode('/',$image,2); + if (!is_array($image)) list($app,$img) = explode('/',$image,2); if (!$app || !$img || !is_dir(EGW_SERVER_ROOT.'/'.$app) || strpos($img,'/')!==false) { $img = $image; From be91330030acc3627c2af8830c9ca9ffdd5b65ba Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 2 Aug 2010 14:15:05 +0000 Subject: [PATCH 23/88] fixed not working notifications for types: - responses: on participant responses too (everytime) - modifications: on all modification, but responses --- calendar/inc/class.calendar_boupdate.inc.php | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/calendar/inc/class.calendar_boupdate.inc.php b/calendar/inc/class.calendar_boupdate.inc.php index a6525236b1..15b6901709 100644 --- a/calendar/inc/class.calendar_boupdate.inc.php +++ b/calendar/inc/class.calendar_boupdate.inc.php @@ -364,7 +364,6 @@ class calendar_boupdate extends calendar_bo else // update existing event { $this->check4update($event,$old_event); - } // notify the link-class about the update, as other apps may be subscribt to it egw_link::notify_update('calendar',$cal_id,$event); @@ -434,9 +433,11 @@ class calendar_boupdate extends calendar_bo * * @param array $new_event the updated event * @param array $old_event the event before the update + * @todo check if there is a real change, not assume every save is a change */ function check4update($new_event,$old_event) { + //error_log(__METHOD__."($new_event[title])"); $modified = $added = $deleted = array(); //echo "

calendar_boupdate::check4update() new participants = ".print_r($new_event['participants'],true).", old participants =".print_r($old_event['participants'],true)."

\n"; @@ -504,12 +505,9 @@ class calendar_boupdate extends calendar_bo switch($ru = $part_prefs['calendar']['receive_updates']) { case 'responses': - if ($msg_is_response) - { - ++$want_update; - } + ++$want_update; case 'modifications': - if ($msg_type == MSG_MODIFIED) + if (!$msg_is_response) { ++$want_update; } @@ -532,7 +530,7 @@ class calendar_boupdate extends calendar_bo case 'no': break; } - //echo "

calendar_boupdate::update_requested(user=$userid,pref=".$part_prefs['calendar']['receive_updates'] .",msg_type=$msg_type,".($old_event?$old_event['title']:'False').",".($old_event?$old_event['title']:'False').") = $want_update

\n"; + //error_log(__METHOD__."(userid=$userid,,msg_type=$msg_type,...) msg_is_response=$msg_is_response, want_update=$want_update"); return $want_update > 0; } @@ -548,7 +546,7 @@ class calendar_boupdate extends calendar_bo */ function send_update($msg_type,$to_notify,$old_event,$new_event=null,$user=0) { - //echo "

".__METHOD__."($msg_type,".array2string($to_notify).",,$new_event[title],$user)

\n"; + //error_log(__METHOD__."($msg_type,".array2string($to_notify).",...)"); if (!is_array($to_notify)) { $to_notify = array(); From 5a03cff75d40bc5e6eb723a440b5b1021bd2adfa Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 5 Aug 2010 15:36:23 +0000 Subject: [PATCH 24/88] set span & class from original cell for single fields --- etemplate/inc/class.customfields_widget.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/etemplate/inc/class.customfields_widget.inc.php b/etemplate/inc/class.customfields_widget.inc.php index 5a034d0ddc..996f89600a 100644 --- a/etemplate/inc/class.customfields_widget.inc.php +++ b/etemplate/inc/class.customfields_widget.inc.php @@ -436,6 +436,7 @@ class customfields_widget } if ($singlefield) // a single field, can & need to be returned instead of the cell (no grid) { + $input['span'] = $cell['span']; // set span & class from original cell $cell = $input; if ($type == 'customfields') $cell['label'] = $field['label']; $value = $value[$this->prefix.$lname]; From a3d06cb63809ee87c733c6b958f08b41044b0781 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 5 Aug 2010 17:11:13 +0000 Subject: [PATCH 25/88] improved performance by not checking exceptions for referencing a master which mentions them as exceptions --- calendar/inc/class.calendar_groupdav.inc.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index b462dd3d49..9eb2fe5c31 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -164,8 +164,9 @@ class calendar_groupdav extends groupdav_handler /* disabling not working iterator function propfind_callback($path,array $filter,$start=false) { - if ($this->debug) $starttime = microtime(true); */ + if ($this->debug) $starttime = microtime(true); + $calendar_data = $filter['calendar_data']; unset($filter['calendar_data']); /* disabling not working iterator @@ -184,10 +185,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f // get all max user modified times at once foreach($events as $k => $event) { - if ($this->client_shared_uid_exceptions && - $event['reference'] && - ($master = $this->bo->read($event['reference'], 0, false, 'server')) && - array_search($event['recurrence'], $master['recur_exception']) !== false) + if ($this->client_shared_uid_exceptions && $event['reference']) { // this exception will be handled with the series master unset($events[$k]); @@ -232,7 +230,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f if ($this->debug) { error_log(__METHOD__."($path) took ".(microtime(true) - $starttime). - ' to return '.count($files).' items'); + ' to return '.count($files['files']).' items'); } /* disabling not working iterator return $files; From c7bc0fbd822d96621584ecf97d1711d907843ea7 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 5 Aug 2010 17:57:59 +0000 Subject: [PATCH 26/88] using created and modified timestamps from calendar, and not reading them separate from egw_content_history table --- calendar/inc/class.calendar_ical.inc.php | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 3dcba248a3..61930733f4 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -846,17 +846,13 @@ class calendar_ical extends calendar_boupdate } } - $modified = $GLOBALS['egw']->contenthistory->getTSforAction('calendar',$event['id'],'modify'); - $created = $GLOBALS['egw']->contenthistory->getTSforAction('calendar',$event['id'],'add'); - if (!$created && !$modified) $created = $event['modified']; - if ($created) + if ($event['created'] || $event['modified']) { - $attributes['CREATED'] = $created; + $attributes['CREATED'] = $event['created'] ? $event['created'] : $event['modified']; } - if (!$modified) $modified = $event['modified']; - if ($modified) + if ($event['modified']) { - $attributes['LAST-MODIFIED'] = $modified; + $attributes['LAST-MODIFIED'] = $event['modified']; } $attributes['DTSTAMP'] = time(); foreach ((array)$event['alarm'] as $alarmID => $alarmData) From b780c68c3f2329deb8d1dd6a998ae0a51ff72136 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Fri, 6 Aug 2010 18:14:36 +0000 Subject: [PATCH 27/88] Improve support of BB Funambol Client --- phpgwapi/inc/horde/Horde/SyncML/State.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/phpgwapi/inc/horde/Horde/SyncML/State.php b/phpgwapi/inc/horde/Horde/SyncML/State.php index 735830b9fa..865265d37e 100644 --- a/phpgwapi/inc/horde/Horde/SyncML/State.php +++ b/phpgwapi/inc/horde/Horde/SyncML/State.php @@ -885,17 +885,21 @@ class Horde_SyncML_State { $_type = str_replace('./','',$type); switch (strtolower($_type)) { case 'contacts': + case 'contact': case 'card': return 'text/vcard'; break; case 'notes': + case 'note': return 'text/x-vnote'; break; case 'calendar': case 'events': + case 'event': case 'tasks': + case 'task': case 'jobs': case 'caltasks': return 'text/calendar'; @@ -933,17 +937,21 @@ class Horde_SyncML_State { switch(strtolower($_type)) { case 'contacts': + case 'contact': case 'card': return 'contacts'; case 'notes': + case 'note': return 'notes'; case 'tasks': + case 'task': case 'jobs': return 'tasks'; case 'events': + case 'event': case 'calendar': return 'calendar'; From d6d71987854566924300b8d636cfd4a4eac73029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Fri, 6 Aug 2010 20:51:26 +0000 Subject: [PATCH 28/88] Fix old format vCard/vCalendar quoting issue --- phpgwapi/inc/horde/Horde/iCalendar.php | 455 +++++++++++++------------ 1 file changed, 230 insertions(+), 225 deletions(-) diff --git a/phpgwapi/inc/horde/Horde/iCalendar.php b/phpgwapi/inc/horde/Horde/iCalendar.php index f21648aa9e..73e3dea5ad 100644 --- a/phpgwapi/inc/horde/Horde/iCalendar.php +++ b/phpgwapi/inc/horde/Horde/iCalendar.php @@ -650,233 +650,238 @@ class Horde_iCalendar { } // Charset and encoding handling. - if (isset($params['QUOTED-PRINTABLE'])) { - $params['ENCODING'] = 'QUOTED-PRINTABLE'; - } - if (isset($params['BASE64'])) { - $params['ENCODING'] = 'BASE64'; - } - if (isset($params['ENCODING'])) { - switch (String::upper($params['ENCODING'])) { - case 'Q': - case 'QUOTED-PRINTABLE': - $value = quoted_printable_decode($value); - if (isset($params['CHARSET'])) { - $value = $GLOBALS['egw']->translation->convert($value, $params['CHARSET']); - } else { - $value = $GLOBALS['egw']->translation->convert($value, - empty($charset) ? ($this->isOldFormat() ? 'iso-8859-1' : 'utf-8') : $charset); - } - // Funambol hack :-( - $value = str_replace('\\\\n', "\n", $value); - break; - case 'B': - case 'BASE64': - $value = base64_decode($value); - break; - } - } elseif (isset($params['CHARSET'])) { - $value = $GLOBALS['egw']->translation->convert($value, $params['CHARSET']); - } else { - // As per RFC 2279, assume UTF8 if we don't have an - // explicit charset parameter. - $value = $GLOBALS['egw']->translation->convert($value, - empty($charset) ? ($this->isOldFormat() ? 'iso-8859-1' : 'utf-8') : $charset); - } - - // Get timezone info for date fields from $params. - $tzid = isset($params['TZID']) ? trim($params['TZID'], '\"') : false; - - switch ($tag) { - case 'VERSION': // already processed - break; - // Date fields. - case 'COMPLETED': - case 'CREATED': - case 'LAST-MODIFIED': - $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params); - break; - - case 'BDAY': - case 'X-SYNCJE-ANNIVERSARY': - $this->setAttribute($tag, $value, $params, true, $this->_parseDate($value)); - break; - - case 'DTEND': - case 'DTSTART': - case 'DTSTAMP': - case 'DUE': - case 'AALARM': - case 'DALARM': - case 'RECURRENCE-ID': - case 'X-RECURRENCE-ID': - // types like AALARM may contain additional data after a ; - // ignore these. - $ts = explode(';', $value); - if (isset($params['VALUE']) && $params['VALUE'] == 'DATE') { - $isDate = true; - $this->setAttribute($tag, $this->_parseDateTime($ts[0], $tzid), $params, true, $this->_parseDate($ts[0])); - } else { - $this->setAttribute($tag, $this->_parseDateTime($ts[0], $tzid), $params); - } - break; - - case 'TRIGGER': - if (isset($params['VALUE'])) { - if ($params['VALUE'] == 'DATE-TIME') { - $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params); - } else { - $this->setAttribute($tag, $this->_parseDuration($value), $params); - } - } else { - $this->setAttribute($tag, $this->_parseDuration($value), $params); - } - break; - - // Comma or semicolon seperated dates. - case 'EXDATE': - case 'RDATE': - $dates = array(); - preg_match_all('/[;,]([^;,]*)/', ';' . $value, $values); - - foreach ($values[1] as $value) { - if ((isset($params['VALUE']) - && $params['VALUE'] == 'DATE') || (!isset($params['VALUE']) && $isDate)) { - $dates[] = $this->_parseDate(trim($value)); - } else { - $dates[] = $this->_parseDateTime(trim($value), $tzid); - } - } - $this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates); - break; - - // Duration fields. - case 'DURATION': - $this->setAttribute($tag, $this->_parseDuration($value), $params); - break; - - // Period of time fields. - case 'FREEBUSY': - $periods = array(); - preg_match_all('/,([^,]*)/', ',' . $value, $values); - foreach ($values[1] as $value) { - $periods[] = $this->_parsePeriod($value); - } - - $this->setAttribute($tag, isset($periods[0]) ? $periods[0] : null, $params, true, $periods); - break; - - // UTC offset fields. - case 'TZOFFSETFROM': - case 'TZOFFSETTO': - $this->setAttribute($tag, $this->_parseUtcOffset($value), $params); - break; - - // Integer fields. - case 'PERCENT-COMPLETE': - case 'PRIORITY': - case 'REPEAT': - case 'SEQUENCE': - $this->setAttribute($tag, intval($value), $params); - break; - - // Geo fields. - case 'GEO': - if ($this->isOldFormat()) { - $floats = explode(',', $value); - $value = array('latitude' => floatval($floats[1]), - 'longitude' => floatval($floats[0])); - } else { - $floats = explode(';', $value); - $value = array('latitude' => floatval($floats[0]), - 'longitude' => floatval($floats[1])); - } - $this->setAttribute($tag, $value, $params); - break; - - // Recursion fields. # add more flexibility - #case 'EXRULE': - #case 'RRULE': - # $this->setAttribute($tag, trim($value), $params); - # break; - - // Binary fields. - case 'PHOTO': - $this->setAttribute($tag, $value, $params); - break; - - // ADR, ORG and N are lists seperated by unescaped semicolons - // with a specific number of slots. - case 'ADR': - case 'N': - case 'ORG': - $value = trim($value); - // As of rfc 2426 2.4.2 semicolon, comma, and colon must - // be escaped (comma is unescaped after splitting below). - $value = str_replace(array('\\n', '\\N', '\\;', '\\:'), - array("\n", "\n", ';', ':'), - $value); - - // Split by unescaped semicolons: - $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); - break; - - // CATEGORIES is a lists seperated by unescaped commas - // with a unspecific number of slots. - case 'CATEGORIES': - $value = trim($value); - // As of rfc 2426 2.4.2 semicolon, comma, and colon must - // be escaped (semicolon is unescaped after splitting below). - $value = str_replace(array('\\n', '\\N', '\\,', '\\:'), - array("\n", "\n", ',', ':'), - $value); - - // Split by unescaped commas: - $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); - break; - - // String fields. - default: - if ($this->isOldFormat()) { - // vCalendar 1.0 and vCard 2.1 only escape semicolons - // and use unescaped semicolons to create lists. - $value = trim($value); - // Split by unescaped semicolons: - $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); - } else { - $value = trim($value); - // As of rfc 2426 2.4.2 semicolon, comma, and colon - // must be escaped (comma is unescaped after splitting - // below). - $value = str_replace(array('\\n', '\\N', '\\;', '\\:', '\\\\'), - array("\n", "\n", ';', ':', '\\'), - $value); - - // Split by unescaped commas. - $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); - } - break; - } + if (isset($params['QUOTED-PRINTABLE'])) { + $params['ENCODING'] = 'QUOTED-PRINTABLE'; + } + if (isset($params['BASE64'])) { + $params['ENCODING'] = 'BASE64'; + } + if (isset($params['ENCODING'])) { + switch (String::upper($params['ENCODING'])) { + case 'Q': + case 'QUOTED-PRINTABLE': + $value = quoted_printable_decode($value); + if (isset($params['CHARSET'])) { + $value = $GLOBALS['egw']->translation->convert($value, $params['CHARSET']); + } else { + $value = $GLOBALS['egw']->translation->convert($value, + empty($charset) ? ($this->isOldFormat() ? 'iso-8859-1' : 'utf-8') : $charset); + } + // Funambol hack :-( + $value = str_replace('\\\\n', "\n", $value); + break; + case 'B': + case 'BASE64': + $value = base64_decode($value); + break; + } + } elseif (isset($params['CHARSET'])) { + $value = $GLOBALS['egw']->translation->convert($value, $params['CHARSET']); + } else { + // As per RFC 2279, assume UTF8 if we don't have an + // explicit charset parameter. + $value = $GLOBALS['egw']->translation->convert($value, + empty($charset) ? ($this->isOldFormat() ? 'iso-8859-1' : 'utf-8') : $charset); + } + + // Get timezone info for date fields from $params. + $tzid = isset($params['TZID']) ? trim($params['TZID'], '\"') : false; + + switch ($tag) { + case 'VERSION': // already processed + break; + // Date fields. + case 'COMPLETED': + case 'CREATED': + case 'LAST-MODIFIED': + $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params); + break; + + case 'BDAY': + case 'X-SYNCJE-ANNIVERSARY': + $this->setAttribute($tag, $value, $params, true, $this->_parseDate($value)); + break; + + case 'DTEND': + case 'DTSTART': + case 'DTSTAMP': + case 'DUE': + case 'AALARM': + case 'DALARM': + case 'RECURRENCE-ID': + case 'X-RECURRENCE-ID': + // types like AALARM may contain additional data after a ; + // ignore these. + $ts = explode(';', $value); + if (isset($params['VALUE']) && $params['VALUE'] == 'DATE') { + $isDate = true; + $this->setAttribute($tag, $this->_parseDateTime($ts[0], $tzid), $params, true, $this->_parseDate($ts[0])); + } else { + $this->setAttribute($tag, $this->_parseDateTime($ts[0], $tzid), $params); + } + break; + + case 'TRIGGER': + if (isset($params['VALUE'])) { + if ($params['VALUE'] == 'DATE-TIME') { + $this->setAttribute($tag, $this->_parseDateTime($value, $tzid), $params); + } else { + $this->setAttribute($tag, $this->_parseDuration($value), $params); + } + } else { + $this->setAttribute($tag, $this->_parseDuration($value), $params); + } + break; + + // Comma or semicolon seperated dates. + case 'EXDATE': + case 'RDATE': + $dates = array(); + preg_match_all('/[;,]([^;,]*)/', ';' . $value, $values); + + foreach ($values[1] as $value) { + if ((isset($params['VALUE']) + && $params['VALUE'] == 'DATE') || (!isset($params['VALUE']) && $isDate)) { + $dates[] = $this->_parseDate(trim($value)); + } else { + $dates[] = $this->_parseDateTime(trim($value), $tzid); + } + } + $this->setAttribute($tag, isset($dates[0]) ? $dates[0] : null, $params, true, $dates); + break; + + // Duration fields. + case 'DURATION': + $this->setAttribute($tag, $this->_parseDuration($value), $params); + break; + + // Period of time fields. + case 'FREEBUSY': + $periods = array(); + preg_match_all('/,([^,]*)/', ',' . $value, $values); + foreach ($values[1] as $value) { + $periods[] = $this->_parsePeriod($value); + } + + $this->setAttribute($tag, isset($periods[0]) ? $periods[0] : null, $params, true, $periods); + break; + + // UTC offset fields. + case 'TZOFFSETFROM': + case 'TZOFFSETTO': + $this->setAttribute($tag, $this->_parseUtcOffset($value), $params); + break; + + // Integer fields. + case 'PERCENT-COMPLETE': + case 'PRIORITY': + case 'REPEAT': + case 'SEQUENCE': + $this->setAttribute($tag, intval($value), $params); + break; + + // Geo fields. + case 'GEO': + if ($this->isOldFormat()) { + $floats = explode(',', $value); + $value = array('latitude' => floatval($floats[1]), + 'longitude' => floatval($floats[0])); + } else { + $floats = explode(';', $value); + $value = array('latitude' => floatval($floats[0]), + 'longitude' => floatval($floats[1])); + } + $this->setAttribute($tag, $value, $params); + break; + + // Recursion fields. # add more flexibility + #case 'EXRULE': + #case 'RRULE': + # $this->setAttribute($tag, trim($value), $params); + # break; + + // Binary fields. + case 'PHOTO': + $this->setAttribute($tag, $value, $params); + break; + + // ADR, ORG and N are lists seperated by unescaped semicolons + // with a specific number of slots. + case 'ADR': + case 'N': + case 'ORG': + $value = trim($value); + // As of rfc 2426 2.4.2 semicolon, comma, and colon must + // be escaped (comma is unescaped after splitting below). + $value = str_replace(array('\\n', '\\N', '\\;', '\\:'), + array("\n", "\n", ';', ':'), + $value); + + // Split by unescaped semicolons: + $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); + break; + + // CATEGORIES is a lists seperated by unescaped commas + // with a unspecific number of slots. + case 'CATEGORIES': + $value = trim($value); + // As of rfc 2426 2.4.2 semicolon, comma, and colon must + // be escaped (semicolon is unescaped after splitting below). + $value = str_replace(array('\\n', '\\N', '\\,', '\\:'), + array("\n", "\n", ',', ':'), + $value); + + // Split by unescaped commas: + $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); + break; + + // String fields. + default: + if ($this->isOldFormat()) { + $value = trim($value); + // vCalendar 1.0 and vCard 2.1 only escape semicolons + // and use unescaped semicolons to create lists. + $value = str_replace(array('\\n', '\\N', '\\;', '\\:'), + array("\n", "\n", ';', ':'), + $value); + + // Split by unescaped semicolons: + $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); + } else { + $value = trim($value); + // As of rfc 2426 2.4.2 semicolon, comma, and colon + // must be escaped (comma is unescaped after splitting + // below). + $value = str_replace(array('\\n', '\\N', '\\;', '\\:', '\\\\'), + array("\n", "\n", ';', ':', '\\'), + $value); + + // Split by unescaped commas. + $values = preg_split('/(?setAttribute($tag, trim($value), $params, true, $values); + } + break; + } } } - + return true; } From 4838885cd2502962d83239d61476db30b0794eed Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 8 Aug 2010 13:13:03 +0000 Subject: [PATCH 29/88] generate package changelog automatic from svn log messages since the last svn tag --- doc/rpm-build/checkout-build-archives.php | 185 +++++++++++++++++++++- 1 file changed, 178 insertions(+), 7 deletions(-) diff --git a/doc/rpm-build/checkout-build-archives.php b/doc/rpm-build/checkout-build-archives.php index c10765aff9..02930e576a 100755 --- a/doc/rpm-build/checkout-build-archives.php +++ b/doc/rpm-build/checkout-build-archives.php @@ -38,6 +38,8 @@ $config = array( 'obs' => false, 'changelog' => false, // eg. '* 1. Zeile\n* 2. Zeile' for debian.changes 'changelog_packager' => 'Ralf Becker ', + 'editsvnchangelog' => '* ', + 'editor' => '/usr/bin/vi', 'svntag' => false, // eg. '$version.$packaging' 'skip' => array(), 'run' => array('checkout','copy','virusscan','create','sign') @@ -84,6 +86,14 @@ while(($arg = array_shift($argv))) $config[$name] = $value; array_unshift($config['run'],'svntag'); break; + + case 'editsvnchangelog': + $config[$name] = $value ? $value : true; + if (!in_array('editsvnchangelog',$config['run'])) + { + array_unshift($config['run'],'editsvnchanglog'); + } + break; case 'obs': if (!is_dir($value)) @@ -111,8 +121,152 @@ $svn = $config['svn']; foreach(array_diff($config['run'],$config['skip']) as $func) { - $func = 'do_'.$func; - $func(); + call_user_func('do_'.$func); +} + +/** + * Query changelog from svn and let user edit it + */ +function do_editsvnchangelog() +{ + global $config,$svn,$verbose; + + echo "Querying changelog from SVN\n"; + if (!isset($config['modules'])) + { + get_modules_per_repro(); + } + // query changelog per repo + $changelog = ''; + foreach($config['modules'] as $repo => $modules) + { + $branch_url = ''; + $revision = null; + foreach($modules as $path => $url) + { + $module = basename($path); + $burl = substr($url,0,-strlen($module)-1); + if (empty($branch_url) || $burl != $branch_url) + { + if (empty($branch_url)) $url = $branch_url = $burl; + //if (count($config['modules']) > 1) $changelog .= $url."\n"; + $changelog .= get_changelog_from_svn($url,$config['editsvnchangelog'],$revision); + } + } + } + $logfile = tempnam('/tmp','checkout-build-archives'); + file_put_contents($logfile,$changelog); + $cmd = $config['editor'].' '.escapeshellarg($logfile); + passthru($cmd); + $config['changlog'] = file_get_contents($logfile); + // allow user to abort, by deleting the changelog + if (strlen($config['changlog']) <= 2) + { + die("\nChangelog must not be empty --> aborting\n\n"); + } +} + +/** + * Read changelog for given branch from (last) tag or given revision from svn + * + * @param string $branch_url='svn+ssh://svn@svn.stylite.de/egroupware/branches/Stylite-EPL-10.1' + * @param string $log_pattern=null a preg regular expression or start of line a log message must match, to be returned + * if regular perl regular expression given only first expression in brackets \\1 is used, + * for a start of line match, only the first line is used, otherwise whole message is used + * @param string $revision=null from which to HEAD the log should be retrieved, default search revision of latest tag in ^/tags + * @param string $prefix='* ' prefix, which if not presend should be added to all log messages + */ +function get_changelog_from_svn($branch_url,$log_pattern=null,&$revision,$prefix='* ') +{ + //echo __FUNCTION__."('$branch_url','$log_pattern','$revision','$prefix')\n"; + global $config,$verbose,$svn; + + if (is_null($revision)) + { + list($tags_url,$branch) = explode('/branches/',$branch_url); + $tags_url .= '/tags'; + $pattern=str_replace('Stylite-EPL-10\.1',preg_quote($branch),'/tags\/(Stylite-EPL-10\.1\.\d{8})/'); + $revision = get_last_svn_tag($tags_url,$pattern,$matches); + $tag = $matches[1]; + } + elseif(!is_numeric($revision)) + { + $revision = get_last_svn_tag($tags_url,$tag=$revision); + } + $cmd = $svn.' log --xml -r '.escapeshellarg($revision.':HEAD').' '.escapeshellarg($branch_url); + if (($v = $verbose)) + { + echo "Querying SVN for log from r$revision".($tag ? " ($tag)" : '').":\n$cmd\n"; + $verbose = false; // otherwise no $output! + } + $output = array(); + run_cmd($cmd,$output); + $verbose = $v; + array_shift($output); // remove the command + + $xml = simplexml_load_string($output=implode("\n",$output)); + $message = ''; + $pattern_len = strlen($log_pattern); + $prefix_len = strlen($prefix); + foreach($xml as $log) + { + $msg = $log->msg; + if ($log_pattern[0] == '/' && preg_match($log_pattern,$msg,$matches)) + { + $msg = $matches[1]; + } + elseif($log_pattern && $log_pattern[0] != '/' && substr($msg,0,$pattern_len) == $log_pattern) + { + list($msg) = explode("\n",$msg); + } + elseif($log_pattern) + { + continue; // no match --> ignore + } + if ($prefix_len && substr($msg,0,$prefix_len) != $prefix) $msg = $prefix.$msg; + $message .= $msg."\n"; + } + if ($verbose) echo $message; + + return $message; +} + +/** + * Get revision of last svn tag matching a given pattern in the log message + * + * @param string $tags_url + * @param string $pattern which has to be contained in the log message (NOT the tag itself) + * or (perl) regular expression against which log message is matched + * @param array &$matches=null on return matches of preg_match + * @return int revision of last svn tag matching pattern + */ +function get_last_svn_tag($tags_url,$pattern,&$matches=null) +{ + global $config,$verbose,$svn; + + $cmd = $svn.' log --xml --limit 10 '.escapeshellarg($tags_url); + if (($v = $verbose)) + { + echo "Querying SVN for last tags\n$cmd\n"; + $verbose = false; // otherwise no $output! + } + $output = array(); + run_cmd($cmd,$output); + $verbose = $v; + array_shift($output); // remove the command + + $xml = simplexml_load_string($output=implode("\n",$output)); + foreach($xml as $log) + { + //print_r($log); + if ($pattern[0] != '/' && strpos($log->msg,$pattern) !== false || + $pattern[0] == '/' && preg_match($pattern,$log->msg,$matches)) + { + if ($verbose) echo "Revision {$log['revision']} matches".($matches?': '.$matches[1] : '')."\n"; + return (int)$log['revision']; + } + } + return null; } /** @@ -435,9 +589,11 @@ function do_checkout() } /** - * Create svn tag or branch + * Get module name per svn repro + * + * @return array with $repro_url => array(module1, ..., moduleN) pairs */ -function do_svntag() +function get_modules_per_repro() { global $config,$svn,$verbose; @@ -448,7 +604,6 @@ function do_svntag() { $config['svntag'] = strtr($config['svntag'],$translate); } - echo "Creating SVN tag $config[svntag]\n"; // process alias/externals $svnbranch = $config['svnbase'].'/'.$config['svnbranch']; @@ -464,6 +619,7 @@ function do_svntag() list($path,$url) = preg_split('/[ \t\r\n]+/',$line); if (!preg_match('/([a-z+]+:\/\/[a-z@.]+\/[a-z]+)\/(branches|tags|trunk)/',$url,$matches)) die("Invalid SVN URL: $url\n"); $repo = $matches[1]; + if ($repo == 'http://svn.egroupware.org/egroupware') $repo = 'svn+ssh://svn@dev.egroupware.org/egroupware'; $config['modules'][$repo][$path] = $url; } // process extra modules @@ -478,12 +634,27 @@ function do_svntag() if (strpos($module,'://') !== false) $module = basename($module); if (!preg_match('/([a-z+]+:\/\/[a-z@.]+\/[a-z]+)\/(branches|tags|trunk)/',$url,$matches)) die("Invalid SVN URL: $url\n"); $repo = $matches[1]; + if ($repo == 'http://svn.egroupware.org/egroupware') $repo = 'svn+ssh://svn@dev.egroupware.org/egroupware'; $config['modules'][$repo][$config['aliasdir'].'/'.$module] = $url; } + return $config['modules']; +} + +/** + * Create svn tag or branch + */ +function do_svntag() +{ + global $config,$svn,$verbose; + + echo "Creating SVN tag $config[svntag]\n"; + if (!isset($config['modules'])) + { + get_modules_per_repro(); + } // create tags (per repo) foreach($config['modules'] as $repo => $modules) { - if ($repo == 'http://svn.egroupware.org/egroupware') $repo = 'svn+ssh://svn@dev.egroupware.org/egroupware'; $cmd = $svn.' cp --parents -m '.escapeshellarg('Creating '.$config['svntag']).' '.implode(' ',$modules).' '.$repo.'/'.$config['svntag'].'/'; run_cmd($cmd); } @@ -493,7 +664,7 @@ function do_svntag() * Runs given shell command, exists with error-code after echoing the output of the failed command (if not already running verbose) * * @param string $cmd - * @param array &$output=null $output of command + * @param array &$output=null $output of command, only if !$verbose !!! * @param int|array $no_bailout=null exit code(s) to NOT bail out * @return int exit code of $cmd */ From 0f284a0bfbe26a7f89b58ce59c431888db33ada7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sun, 8 Aug 2010 21:52:44 +0000 Subject: [PATCH 30/88] Disable Thunderbird special treatment --- calendar/inc/class.calendar_ical.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 61930733f4..aeb1be0785 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -2207,6 +2207,7 @@ class calendar_ical extends calendar_boupdate return false; } + /* $mozillaACK = $component->getAttribute('X-MOZ-LASTACK'); if ($this->productName == 'lightning' && !is_a($mozillaACK, 'PEAR_Error')) { @@ -2217,6 +2218,7 @@ class calendar_ical extends calendar_boupdate } return false; } + */ if (!empty($GLOBALS['egw_info']['user']['preferences']['syncml']['minimum_uid_length'])) { From ca3c4694e615e836ad2608729a66eb13f28b8254 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 9 Aug 2010 11:42:50 +0000 Subject: [PATCH 31/88] fixed typo in editsvnchang_e_log and not replaced message about created svntag --- doc/rpm-build/checkout-build-archives.php | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/doc/rpm-build/checkout-build-archives.php b/doc/rpm-build/checkout-build-archives.php index 02930e576a..f9fc191837 100755 --- a/doc/rpm-build/checkout-build-archives.php +++ b/doc/rpm-build/checkout-build-archives.php @@ -91,7 +91,7 @@ while(($arg = array_shift($argv))) $config[$name] = $value ? $value : true; if (!in_array('editsvnchangelog',$config['run'])) { - array_unshift($config['run'],'editsvnchanglog'); + array_unshift($config['run'],'editsvnchangelog'); } break; @@ -600,11 +600,6 @@ function get_modules_per_repro() $translate = array(); foreach($config as $name => $value) $translate['$'.$name] = $value; - if (strpos($config['svntag'],'$') !== false) // allow to use config vars like $version in tag - { - $config['svntag'] = strtr($config['svntag'],$translate); - } - // process alias/externals $svnbranch = $config['svnbase'].'/'.$config['svnbranch']; $url = $svnbranch.'/'.$config['svnalias']; @@ -647,6 +642,13 @@ function do_svntag() { global $config,$svn,$verbose; + $translate = array(); + foreach($config as $name => $value) $translate['$'.$name] = $value; + + if (strpos($config['svntag'],'$') !== false) // allow to use config vars like $version in tag + { + $config['svntag'] = strtr($config['svntag'],$translate); + } echo "Creating SVN tag $config[svntag]\n"; if (!isset($config['modules'])) { From 4be804492155bffcca675d18e5d9cc47c07b1a27 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Tue, 10 Aug 2010 07:36:19 +0000 Subject: [PATCH 32/88] remove trailing newlines --- doc/rpm-build/checkout-build-archives.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/doc/rpm-build/checkout-build-archives.php b/doc/rpm-build/checkout-build-archives.php index f9fc191837..18d1423325 100755 --- a/doc/rpm-build/checkout-build-archives.php +++ b/doc/rpm-build/checkout-build-archives.php @@ -159,6 +159,11 @@ function do_editsvnchangelog() $cmd = $config['editor'].' '.escapeshellarg($logfile); passthru($cmd); $config['changlog'] = file_get_contents($logfile); + // remove trailing newlines + while (substr($config['changelog'],-1) == "\n") + { + $config['changelog'] = substr($config['changelog'],0,-1); + } // allow user to abort, by deleting the changelog if (strlen($config['changlog']) <= 2) { From 7ea2ef612cfe2550f2bc10a50691bbc5a93b5518 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Tue, 10 Aug 2010 14:48:05 +0000 Subject: [PATCH 33/88] Add static method to access history and slice it using a start and num_rows, instead of getting all history at once --- phpgwapi/inc/class.historylog.inc.php | 44 +++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/phpgwapi/inc/class.historylog.inc.php b/phpgwapi/inc/class.historylog.inc.php index 96e3d9d7d7..fea7e4803d 100644 --- a/phpgwapi/inc/class.historylog.inc.php +++ b/phpgwapi/inc/class.historylog.inc.php @@ -161,6 +161,50 @@ class historylog return $rows; } + /** + * Get a slice of history records + * + * Similar to search(), except this one can take a start and a number of records + */ + public static function get_rows(&$query, &$rows) { + $filter = array(); + $rows = array(); + $filter['history_appname'] = $query['appname']; + $filter['history_record_id'] = $query['record_id']; + if(is_array($query['colfilter'])) { + foreach($query['colfilter'] as $column => $value) { + $filter[$column] = $value; + } + } + if ($GLOBALS['egw']->db->Type == 'mysql' && $GLOBALS['egw']->db->ServerInfo['version'] >= 4.0) + { + $mysql_calc_rows = 'SQL_CALC_FOUND_ROWS '; + } + else + { + $total = $GLOBALS['egw']->db->select(self::TABLE,'COUNT(*)',$filter,__LINE__,__FILE__,false,'','phpgwapi',0)->fetchColumn(); + } + foreach($GLOBALS['egw']->db->select( + self::TABLE, + $mysql_calc_rows.'*', + $filter, + __LINE__, __FILE__, + $query['start'], + 'ORDER BY ' . ($query['order'] ? $query['order'] : 'history_id') . ' ' . ($query['sort'] ? $query['sort'] : 'DESC'), + 'phpgwapi', + $query['num_rows'] + ) as $row) { + $row['user_ts'] = $GLOBALS['egw']->db->from_timestamp($row['history_timestamp']) + 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset']; + $rows[] = egw_db::strip_array_keys($row,'history_'); + } + if ($mysql_calc_rows) + { + $total = $GLOBALS['egw']->db->query('SELECT FOUND_ROWS()')->fetchColumn(); + } + + return $total; + } + /** * return history-log for one record of $this->appname * From 8728ba5512b180abca2962ca87953deaa38a8dec Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Tue, 10 Aug 2010 14:58:54 +0000 Subject: [PATCH 34/88] - Implement request.abort() to cancel a request - Check that the response is there before trying to access a key inside it - Don't return null for encoding 0 --- phpgwapi/js/egw_json.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/phpgwapi/js/egw_json.js b/phpgwapi/js/egw_json.js index 26a7629832..fd4ec8d972 100644 --- a/phpgwapi/js/egw_json.js +++ b/phpgwapi/js/egw_json.js @@ -80,7 +80,7 @@ function _egw_json_encode_simple(input) function egw_json_encode(input) { - if (!input) return 'null'; + if (input == null || !input && input.length == 0) return 'null'; var simple_res = _egw_json_encode_simple(input); if (simple_res == null) @@ -197,6 +197,7 @@ function egw_json_request(_menuaction, _parameters, _context) this.url = url + '/json.php'; + this.request = null; this.sender = null; this.callback = null; this.alertHandler = this.alertFunc; @@ -237,7 +238,7 @@ egw_json_request.prototype.sendRequest = function(_async, _callback, _sender) }; //Send the request via the jquery AJAX interface to the server - $.ajax({url: this.url + '?menuaction=' + this.menuaction, + this.request = $.ajax({url: this.url + '?menuaction=' + this.menuaction, async: is_async, context: this, data: request_obj, @@ -248,6 +249,11 @@ egw_json_request.prototype.sendRequest = function(_async, _callback, _sender) }); } +egw_json_request.prototype.abort = function() +{ + this.request.abort(); +} + egw_json_request.prototype.alertFunc = function(_message, _details) { alert(_message); @@ -350,7 +356,7 @@ egw_json_request.prototype.handleResponse = function(data, textStatus, XMLHttpRe } catch (e) { - _egw_json_debug_log(e); + _egw_json_debug_log(e, {'Function': res.data.func, 'Parameters': res.data.params}); } hasResponse = true; } else @@ -453,7 +459,7 @@ egw_json_request.prototype.handleResponse = function(data, textStatus, XMLHttpRe { this.loadedJSFiles[res.data] = true; } - } + } } hasResponse = true; } else @@ -477,7 +483,7 @@ egw_json_request.prototype.handleResponse = function(data, textStatus, XMLHttpRe } /* If no explicit response has been specified, call the callback (if one was set) */ - if (!hasResponse && this.callback) + if (!hasResponse && this.callback && data.response[i]) { this.callback.call(this.sender, data.response[i].data); } From f90e1da24f2406c3f70dfa2e25d712f54b0057ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Tue, 10 Aug 2010 21:21:18 +0000 Subject: [PATCH 35/88] Fix alarm handling for recurring events starting in the past --- calendar/inc/class.calendar_so.inc.php | 27 ++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/calendar/inc/class.calendar_so.inc.php b/calendar/inc/class.calendar_so.inc.php index 570df77f38..ef62f0b17c 100644 --- a/calendar/inc/class.calendar_so.inc.php +++ b/calendar/inc/class.calendar_so.inc.php @@ -1089,21 +1089,23 @@ ORDER BY cal_user_type, cal_usre_id { $alarm['time'] = $event['cal_start'] - $alarm['offset']; } - - //pgoerzen: don't add an alarm in the past - if ($event['recur_type'] != MCAL_RECUR_NONE) + + $start = (int)time() + $alarm['offset']; + if ($alarm['time'] < $start) { - $where = array('cal_id' => $cal_id); - $where[] = 'cal_start >= ' . (int)(time() + $alarm['offset']); - if (($next_occurrence = (int) $this->db->select($this->dates_table,'MIN(cal_start)',$where,__LINE__,__FILE__,false,'','calendar')->fetchColumn()) - && ($time = $next_occurrence - $alarm['offset']) > $alarm['time']) + //pgoerzen: don't add an alarm in the past + if ($event['recur_type'] == MCAL_RECUR_NONE) continue; + $event['start'] = $event['cal_start']; + $event['end'] = $event['cal_end']; + $event['tzid'] = $event['cal_tzid']; + $rrule = calendar_rrule::event2rrule($event, false); + foreach ($rrule as $time) { - $alarm['time'] = $time; - } - elseif (!$next_occurrence) - { - continue; + if ($start< ($ts = egw_time::to($time,'server'))) break; + $ts = 0; } + if (!$ts) continue; + $alarm['time'] = $ts - $alarm['offset']; } $this->save_alarm($cal_id,$alarm); } @@ -1601,6 +1603,7 @@ ORDER BY cal_user_type, cal_usre_id function save_alarm($cal_id, $alarm, $now=0) { //echo "

save_alarm(cal_id=$cal_id, alarm="; print_r($alarm); echo ")

\n"; + //error_log(__METHOD__."(.$cal_id,$now,".array2string($alarm).')'); if (!($id = $alarm['id'])) { $alarms = $this->read_alarms($cal_id); // find a free alarm# From f156301f251bea2c2cd78c120895c86226c0cdf4 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 12 Aug 2010 18:03:03 +0000 Subject: [PATCH 36/88] fixed wrongly display blur text for value "0" --- etemplate/inc/class.etemplate.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etemplate/inc/class.etemplate.inc.php b/etemplate/inc/class.etemplate.inc.php index 2b71430d55..9a00d49a4b 100644 --- a/etemplate/inc/class.etemplate.inc.php +++ b/etemplate/inc/class.etemplate.inc.php @@ -1133,7 +1133,7 @@ class etemplate extends boetemplate { if ($blur) { - if (empty($value)) + if ((string)$value === '') { $value = $blur; } From 70e094751768fb7e285194fed76401b32e1ab220 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 12 Aug 2010 18:15:37 +0000 Subject: [PATCH 37/88] allow to return a limited result set and arbitrary columns --- phpgwapi/inc/class.asyncservice.inc.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/phpgwapi/inc/class.asyncservice.inc.php b/phpgwapi/inc/class.asyncservice.inc.php index fcfc8e824b..760a25f826 100644 --- a/phpgwapi/inc/class.asyncservice.inc.php +++ b/phpgwapi/inc/class.asyncservice.inc.php @@ -62,12 +62,13 @@ class asyncservice * @param mixed $data This data is passed back when the method is called. It might simply be an * integer id, but it can also be a complete array. * @param int $account_id account_id, under which the methode should be called or False for the actual user + * @param boolean $debug=false * @return boolean False if $id already exists, else True */ - function set_timer($times,$id,$method,$data,$account_id=False) + function set_timer($times,$id,$method,$data,$account_id=False,$debug=false) { if (empty($id) || empty($method) || $this->read($id) || - !($next = $this->next_run($times))) + !($next = $this->next_run($times,$debug))) { return False; } @@ -447,9 +448,13 @@ class asyncservice * * @param string $id =0 reads all expired rows / jobs ready to run\ * != 0 reads all rows/jobs matching $id (sql-wildcards '%' and '_' can be used) + * @param array|string $cols='*' string or array of column-names / select-expressions + * @param int|bool $offset=False offset for a limited query or False (default) + * @param string $append string to append to the end of the query, eg. ORDER BY ... + * @param int $num_rows=0 number of rows to return if offset set, default 0 = use default in user prefs * @return array/boolean db-rows / jobs as array or False if no matches */ - function read($id=0) + function read($id=0,$cols='*',$offset=False,$append='',$num_rows=0) { if (!is_array($id) && (strpos($id,'%') !== False || strpos($id,'_') !== False)) { @@ -465,7 +470,7 @@ class asyncservice $where = array('async_id' => $id); } $jobs = array(); - foreach($this->db->select($this->db_table,'*',$where,__LINE__,__FILE__) as $row) + foreach($this->db->select($this->db_table,$cols,$where,__LINE__,__FILE__,$offset,'',False,$num_rows) as $row) { $row['async_times'] = unserialize($row['async_times']); $row['async_data'] = unserialize($row['async_data']); From d4985709c511de00e67aa0cdc4271013ffd40786 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sun, 15 Aug 2010 06:34:16 +0000 Subject: [PATCH 38/88] Fix recurrence enddate issue --- calendar/inc/class.calendar_ical.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index aeb1be0785..968a818c12 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -2938,8 +2938,8 @@ class calendar_ical extends calendar_boupdate $last = clone $rriter->current; $rriter->next_no_exception(); } - $delta = $event['end'] - $event['start']; - $last->modify('+' . $delta . ' seconds'); + //$delta = $event['end'] - $event['start']; + //$last->modify('+' . $delta . ' seconds'); $last->setTime(0, 0, 0); $event['recur_enddate'] = egw_time::to($last, 'server'); } From 44c3c7eb9a0669bbe9ef894867695dffbecf96dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Lehrke?= Date: Sun, 15 Aug 2010 06:42:05 +0000 Subject: [PATCH 39/88] Improve iPhone iCal support --- calendar/inc/class.calendar_groupdav.inc.php | 15 ++++++++++++--- calendar/inc/class.calendar_hooks.inc.php | 10 ++++++++++ calendar/inc/class.calendar_ical.inc.php | 15 +++++++++++++-- phpgwapi/inc/class.groupdav_handler.inc.php | 4 ++++ 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/calendar/inc/class.calendar_groupdav.inc.php b/calendar/inc/class.calendar_groupdav.inc.php index 9eb2fe5c31..e4da86a4ff 100644 --- a/calendar/inc/class.calendar_groupdav.inc.php +++ b/calendar/inc/class.calendar_groupdav.inc.php @@ -475,7 +475,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f function put(&$options,$id,$user=null) { if ($this->debug) error_log(__METHOD__."($id, $user)".print_r($options,true)); - + $return_no_access = true; // as handled by importVCal anyway and allows it to set the status for participants $oldEvent = $this->_common_get_put_delete('PUT',$options,$id,$return_no_access); if (!is_null($oldEvent) && !is_array($oldEvent)) @@ -867,6 +867,15 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f */ static function extra_properties(array $props=array(), $displayname, $base_uri=null) { + if (strlen($GLOBALS['egw_info']['user']['preferences']['calendar']['display_color']) == 9 && + $GLOBALS['egw_info']['user']['preferences']['calendar']['display_color'][0] == '#') + { + $display_color = $GLOBALS['egw_info']['user']['preferences']['calendar']['display_color']; + } + else + { + $display_color = '#0040A0FF'; + } // calendar description $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-description',$displayname); /* @@ -903,7 +912,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f $props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'supported-calendar-data',array( HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data', array('content-type' => 'text/calendar', 'version'=> '2.0')), HTTP_WebDAV_Server::mkprop(groupdav::CALDAV,'calendar-data', array('content-type' => 'text/x-calendar', 'version'=> '1.0')))); - $props[] = HTTP_WebDAV_Server::mkprop(groupdav::ICAL,'calendar-color','#0040A0FF'); // TODO: make it configurable + $props[] = HTTP_WebDAV_Server::mkprop(groupdav::ICAL,'calendar-color',$display_color); //$props[] = HTTP_WebDAV_Server::mkprop(groupdav::CALENDARSERVER,'publish-url',array( // HTTP_WebDAV_Server::mkprop('href',$base_uri.'/calendar/'))); @@ -920,7 +929,7 @@ error_log(__METHOD__."($path,,".array2string($start).") filter=".array2string($f { $handler = new calendar_ical(); $handler->setSupportedFields('GroupDAV',$this->agent); - if ($this->debug > 1) error_log("ical Handler called:" . $this->agent); + if ($this->debug > 1) error_log("ical Handler called: " . $this->agent); return $handler; } } diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index 2111e382bd..2538aeda19 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -410,6 +410,16 @@ class calendar_hooks 'xmlrpc' => True, 'admin' => False ), + 'display_color' => array( + 'type' => 'input', + 'size' => 9, + 'label' => 'Select a color for this calendar', + 'name' => 'display_color', + 'help' => 'The Apple iCal Apps use this color to display events from this calendar.', + 'forced' => '#0040A0FF', + 'xmlrpc' => True, + 'admin' => False + ), 'default_private' => array( 'type' => 'check', 'label' => 'Set new events to private', diff --git a/calendar/inc/class.calendar_ical.inc.php b/calendar/inc/class.calendar_ical.inc.php index 968a818c12..2cbc9b9e6c 100644 --- a/calendar/inc/class.calendar_ical.inc.php +++ b/calendar/inc/class.calendar_ical.inc.php @@ -619,8 +619,19 @@ class calendar_ical extends calendar_boupdate $rrule = $rriter->generate_rrule($version); if ($event['recur_enddate']) { - $length = ($event['end'] - $event['start']) / 2; - $rrule['UNTIL']->modify($length . ' second'); + if ($this->productManufacturer == 'groupdav' && $this->productName == 'iphone') + { + // Fix iPhone issue + $length = ($event['end'] - $event['start']); + $rrule['UNTIL']->modify($length . ' second'); + $rrule['UNTIL']->setTime(23, 59, 59); + } + else + { + $length = ($event['end'] - $event['start']) / 2; + $rrule['UNTIL']->modify($length . ' second'); + } + if (!$tzid || $version != '1.0') { if (!isset(self::$tz_cache['UTC'])) diff --git a/phpgwapi/inc/class.groupdav_handler.inc.php b/phpgwapi/inc/class.groupdav_handler.inc.php index 473398e5e5..b797699ab2 100644 --- a/phpgwapi/inc/class.groupdav_handler.inc.php +++ b/phpgwapi/inc/class.groupdav_handler.inc.php @@ -341,6 +341,7 @@ abstract class groupdav_handler // identify the agent (GroupDAV client) from the HTTP_USER_AGENT header $user_agent = strtolower($_SERVER['HTTP_USER_AGENT']); foreach(array( + 'iphone' => 'iphone', // Apple iPhone iCal 'davkit' => 'davkit', // Apple iCal 'cfnetwork' => 'cfnetwork', // Apple Addressbook 'bionicmessage.net' => 'funambol', // funambol GroupDAV connector from bionicmessage.net @@ -372,6 +373,9 @@ abstract class groupdav_handler } } } + + if ($debug) error_log(__METHOD__."GroupDAV client: $agent"); + return $agent; } } From 10cf1b2b8e2d86e514565a82f5e593fef0796e97 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 15 Aug 2010 15:46:23 +0000 Subject: [PATCH 40/88] * not overwriting system configuration (eg. pathes) on restore, which break a running system and if called from within EGroupware update the restored backup, in case it is an older version --- phpgwapi/inc/class.db_backup.inc.php | 61 +++++++++++++++++++++++----- setup/db_backup.php | 20 +++++++-- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/phpgwapi/inc/class.db_backup.inc.php b/phpgwapi/inc/class.db_backup.inc.php index fa0abafe50..63ac11678e 100644 --- a/phpgwapi/inc/class.db_backup.inc.php +++ b/phpgwapi/inc/class.db_backup.inc.php @@ -7,7 +7,7 @@ * @package api * @subpackage db * @author Ralf Becker - * @copyright (c) 2003-8 by Ralf Becker + * @copyright (c) 2003-10 by Ralf Becker * @version $Id$ */ @@ -328,8 +328,26 @@ class db_backup $this->backup_mincount = $minCount; $this->backup_files = (bool)$backupFiles; // Update session cache - $GLOBALS['egw']->invalidate_session_cache(); + if (is_a($GLOBALS['egw'],'egw')) $GLOBALS['egw']->invalidate_session_cache(); } + + /** + * Certain config settings NOT to restore (because they break a working system) + * + * @var array + */ + static $system_config = array( + 'files_dir', + 'temp_dir', + 'backup_dir', + 'webserver_url', + 'aspell_path', + 'hostname', + 'httpproxy_server', + 'httpproxy_port', + 'httpproxy_server_username', + 'httpproxy_server_password', + ); /** * Backup all data in the form of a (compressed) csv file @@ -337,14 +355,26 @@ class db_backup * @param resource $f file opened with fopen for reading * @param boolean $convert_to_system_charset=false convert the restored data to the selected system-charset * @param string $filename='' gives the file name which is used in case of a zip archive. + * @param boolean $protect_system_config=true should above system_config values be protected (NOT overwritten) * * @returns An empty string or an error message in case of failure. */ - function restore($f,$convert_to_system_charset=false,$filename='') + function restore($f,$convert_to_system_charset=false,$filename='',$protect_system_config=true) { @set_time_limit(0); ini_set('auto_detect_line_endings',true); - + + if ($protect_system_config) + { + $system_config = array(); + foreach($this->db->select(self::TABLE,'*',array( + 'config_app' => 'phpgwapi', + 'config_name' => self::$system_config, + ),__LINE__,__FILE__) as $row) + { + $system_config[] = $row; + } + } $this->db->transaction_begin(); // drop all existing tables @@ -468,8 +498,8 @@ class db_backup $import = true; $data = self::csv_split($line,$cols); if ($table == 'egw_async' && in_array('##last-check-run##',$data)) { - echo '

'.lang("Line %1: '%2'
csv data does contain ##last-check-run## of table %3 ==> ignored",$n,$line,$table)."

\n"; - echo 'data=
'.print_r($data,true)."
\n"; + //echo '

'.lang("Line %1: '%2'
csv data does contain ##last-check-run## of table %3 ==> ignored",$n,$line,$table)."

\n"; + //echo 'data=
'.print_r($data,true)."
\n"; $import = false; } if (in_array($table,$this->exclude_tables)) @@ -477,7 +507,8 @@ class db_backup echo '

'.lang("Table %1 is excluded from backup and restore. Data will not be restored.",$table)."

\n"; $import = false; // dont restore data of excluded tables } - if ($import) { + if ($import) + { if (count($data) == count($cols)) { if ($convert_to_system_charset && !$this->db->capabilities['client_encoding']) @@ -524,6 +555,16 @@ class db_backup unlink($name); rmdir($dir.'/database_backup'); } + if ($protect_system_config) + { + foreach($system_config as $row) + { + $this->db->insert(self::TABLE,array('config_value'=>$row['config_value']),array( + 'config_name' => $row['config_name'], + 'config_app' => $row['config_app'], + ),__LINE__,__FILE__); + } + } if (!$this->db->transaction_commit()) { return lang('Restore failed'); @@ -738,12 +779,12 @@ class db_backup * gets a list of all files on $f * * @param string file $f - * [@param int $cnt] - * [@param string $path_name] + * @param int $cnt=0 + * @param string $path_name='' * * @return array (list of files) */ - function get_file_list($f, $cnt = 0, $path_name = "") + function get_file_list($f, $cnt = 0, $path_name = '') { //chdir($f); //echo "Processing $f
"; diff --git a/setup/db_backup.php b/setup/db_backup.php index 942876241d..d79194e6ad 100644 --- a/setup/db_backup.php +++ b/setup/db_backup.php @@ -37,8 +37,7 @@ if ($_POST['download']) list($file) = each($_POST['download']); $file = $db_backup->backup_dir.'/'.basename($file); // basename to now allow to change the dir ob_end_clean(); - $browser = CreateObject('phpgwapi.browser'); - $browser->content_header(basename($file)); + html::content_header(basename($file)); $f = fopen($file,'rb'); fpassthru($f); fclose($f); @@ -68,8 +67,9 @@ else $setup_tpl->set_block('T_db_backup','setup_header'); $setup_tpl->set_var('setup_header',''); $GLOBALS['egw_info']['flags']['app_header'] = $stage_title; - $GLOBALS['egw']->common->phpgw_header(); + common::egw_header(); parse_navbar(); + $run_in_egw = true; } // save backup housekeeping settings if ($_POST['save_backup_settings']) @@ -178,6 +178,14 @@ if ($_POST['restore']) echo '

'.lang('restore started, this might take a few minutes ...')."

\n".str_repeat(' ',4096); $db_backup->restore($f, FALSE, $file); $setup_tpl->set_var('error_msg',lang("backup '%1' restored",$file)); + if ($run_in_egw) + { + // updating the backup + $cmd = new setup_cmd_update($GLOBALS['egw']->session->account_domain, + $GLOBALS['egw_info']['server']['header_admin_user']='admin', + $GLOBALS['egw_info']['server']['header_admin_password']=uniqid('pw',true),false,true); + echo $cmd->run()."\n"; + } } else { @@ -271,7 +279,11 @@ $setup_tpl->set_var(array( $setup_tpl->set_var('self',$self); $setup_tpl->pparse('out','T_db_backup'); -if (is_object($GLOBALS['egw_setup']->html)) +if ($run_in_egw) +{ + common::egw_footer(); +} +else { $GLOBALS['egw_setup']->html->show_footer(); } From 912973e47fc8900dc94d8be7bd5539408108dddf Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 15 Aug 2010 20:05:39 +0000 Subject: [PATCH 41/88] fixed halfway implemented $append parameter and app-detection for static methods --- phpgwapi/inc/class.asyncservice.inc.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/phpgwapi/inc/class.asyncservice.inc.php b/phpgwapi/inc/class.asyncservice.inc.php index 760a25f826..a65a92ddad 100644 --- a/phpgwapi/inc/class.asyncservice.inc.php +++ b/phpgwapi/inc/class.asyncservice.inc.php @@ -409,7 +409,7 @@ class asyncservice if ($lang != $GLOBALS['egw_info']['user']['preferences']['common']['lang']) { unset($GLOBALS['lang']); - $GLOBALS['egw']->translation->add_app('common'); + translation::add_app('common'); } } else @@ -417,8 +417,9 @@ class asyncservice $GLOBALS['egw_info']['user']['domain'] = $domain; } } - list($app) = explode('.',$job['method']); - $GLOBALS['egw']->translation->add_app($app); + list($app) = strpos($job['method'],'::') !== false ? explode('_',$job['method']) : + explode('.',$job['method']); + translation::add_app($app); ExecMethod($job['method'],$job['data']); // re-read job, in case it had been updated or even deleted in the method @@ -470,7 +471,7 @@ class asyncservice $where = array('async_id' => $id); } $jobs = array(); - foreach($this->db->select($this->db_table,$cols,$where,__LINE__,__FILE__,$offset,'',False,$num_rows) as $row) + foreach($this->db->select($this->db_table,$cols,$where,__LINE__,__FILE__,$offset,$append,False,$num_rows) as $row) { $row['async_times'] = unserialize($row['async_times']); $row['async_data'] = unserialize($row['async_data']); From 1d987285525703c5e20ab2b3620d7c5d187f25a1 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 16 Aug 2010 07:13:47 +0000 Subject: [PATCH 42/88] fixed not disabled custom field tab, if no cfs exist (problem was added history tab, which changed name of tabs widget, now using "tabs=general|...") --- calendar/inc/class.calendar_uiforms.inc.php | 17 +++++------------ calendar/setup/etemplates.inc.php | 4 ++-- calendar/templates/default/edit.xet | 2 +- 3 files changed, 8 insertions(+), 15 deletions(-) diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 9429140615..73febc4968 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -40,13 +40,6 @@ class calendar_uiforms extends calendar_ui */ var $durations = array(); - /** - * Name of the tabs used in edit - * - * @var string - */ - var $tabs = 'general|description|participants|recurrence|custom|links|alarms|history'; - /** * default locking time for entries, that are opened by another user * @@ -424,7 +417,7 @@ class calendar_uiforms extends calendar_ui 'recurrence' => $content['recurrence'], 'actual_date' => $content['actual_date'], 'no_popup' => $content['no_popup'], - $this->tabs => $content[$this->tabs], + 'tabs' => $content['tabs'], 'template' => $content['template'], ); $noerror=true; @@ -1188,7 +1181,7 @@ function replace_eTemplate_onsubmit() 'to_app' => 'calendar', ), 'edit_single' => $preserv['edit_single'], // need to be in content too, as it is used in the template - $this->tabs => $preserv[$this->tabs], + 'tabs' => $preserv['tabs'], 'view' => $view, 'msg' => $msg, )); @@ -1327,7 +1320,7 @@ function replace_eTemplate_onsubmit() else { // hide the alarm tab for newly created exceptions - $readonlys[$this->tabs]['alarms'] = true; + $readonlys['tabs']['alarms'] = true; // disable the alarm tab functionality $readonlys['button[add_alarm]'] = true; @@ -1354,7 +1347,7 @@ function replace_eTemplate_onsubmit() if ($key != 'alarm') $readonlys[$key] = true; } // we need to unset the tab itself, as this would make all content (incl. the change-status selects) readonly - unset($readonlys[$this->tabs]); + unset($readonlys['tabs']); // participants are handled individual unset($readonlys['participants']); @@ -1402,7 +1395,7 @@ function replace_eTemplate_onsubmit() } } // disabling the custom fields tab, if there are none - $readonlys[$this->tabs] = array( + $readonlys['tabs'] = array( 'custom' => !count($this->bo->config['customfields']), 'participants' => $this->accountsel->account_selection == 'none', ); diff --git a/calendar/setup/etemplates.inc.php b/calendar/setup/etemplates.inc.php index 6b8daae838..b192dd8900 100644 --- a/calendar/setup/etemplates.inc.php +++ b/calendar/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * eGroupWare - eTemplates for Application calendar * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2010-06-28 15:43 + * generated by soetemplate::dump4setup() 2010-08-16 09:15 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package calendar @@ -20,7 +20,7 @@ $templ_data[] = array('name' => 'calendar.conflicts','template' => '','lang' => $templ_data[] = array('name' => 'calendar.delete_series','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:8:{s:4:"type";s:4:"vbox";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:1:"2";i:1;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"1";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:30:"This event is part of a series";}s:4:"name";s:14:"#dialog-header";s:4:"span";s:13:",promptheader";}i:2;a:6:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:59:"Do you want to keep the series exceptions in your calendar?";}s:4:"span";s:7:",prompt";s:4:"name";s:14:"dialog-content";i:2;a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:15:"Keep exceptions";s:4:"help";s:48:"All exceptions are converted into single events.";s:4:"name";s:30:"button[delete_keep_exceptions]";}i:2;a:4:{s:4:"type";s:6:"button";s:5:"label";s:17:"Delete exceptions";s:4:"help";s:52:"The exceptions are deleted together with the series.";s:4:"name";s:25:"button[delete_exceptions]";}i:3;a:3:{s:4:"type";s:10:"buttononly";s:5:"label";s:6:"Cancel";s:7:"onclick";s:64:"document.getElementById(\'delete_series\').style.display = \'none\';";}s:5:"align";s:6:"center";}}s:4:"name";s:14:"#delete_series";}}','size' => '','style' => '','modified' => '1273136129',); -$templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:7:{s:1:"A";s:3:"100";s:1:"B";s:3:"300";s:2:"h1";s:6:",!@msg";s:2:"c2";s:2:"th";s:2:"h2";s:2:"28";s:2:"h4";s:8:",!@owner";s:2:"h6";s:13:",!@recur_type";}i:1;a:4:{s:1:"A";a:5:{s:4:"span";s:13:"all,redItalic";s:7:"no_lang";s:1:"1";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";s:4:"type";s:4:"html";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:2;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Title";}s:1:"B";a:5:{s:4:"size";s:6:"80,255";s:4:"span";s:3:"all";s:6:"needed";s:1:"1";s:4:"name";s:5:"title";s:4:"type";s:4:"text";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:3;a:4:{s:1:"A";a:5:{s:4:"span";s:3:"all";s:4:"name";s:183:"calendar.edit.general|calendar.edit.description|calendar.edit.participants|calendar.edit.recurrence|calendar.edit.custom|calendar.edit.links|calendar.edit.alarms|calendar.edit.history";s:4:"type";s:3:"tab";s:5:"label";s:71:"General|Description|Participants|Recurrence|Custom|Links|Alarms|History";s:4:"help";s:173:"Location, Start- and Endtimes, ...|Full description|Participants, Resources, ...|Repeating Event Information|Custom fields|Links, Attachments|Alarm management|Change history";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:4;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"B";a:3:{s:4:"type";s:14:"select-account";s:8:"readonly";s:4:"true";s:4:"name";s:5:"owner";}s:1:"C";a:7:{s:5:"label";s:7:"Updated";s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:5:"align";s:5:"right";s:4:"name";s:8:"modified";s:4:"type";s:9:"date-time";s:4:"span";s:8:",noBreak";}s:1:"D";a:6:{s:5:"class";s:7:"noBreak";s:4:"type";s:14:"select-account";s:5:"label";s:2:"by";s:8:"readonly";s:4:"true";s:5:"align";s:5:"right";s:4:"name";s:8:"modifier";}}i:5;a:4:{s:1:"A";a:7:{s:4:"span";s:1:"3";s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";i:1;a:4:{s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"type";s:6:"button";s:4:"help";s:22:"saves the changes made";}i:2;a:4:{s:5:"label";s:5:"Apply";s:4:"name";s:13:"button[apply]";s:4:"type";s:6:"button";s:4:"help";s:17:"apply the changes";}i:3;a:5:{s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";s:4:"name";s:14:"button[cancel]";s:4:"type";s:6:"button";s:4:"help";s:16:"Close the window";}i:4;a:5:{s:8:"onchange";s:34:"this.form.submit(); this.value=\'\';";s:4:"name";s:6:"action";s:4:"size";s:10:"Actions...";s:4:"type";s:6:"select";s:4:"help";s:39:"Execute a further action for this entry";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:137:"if ($cont[recur_type]) { getElementById(\'delete_series\').style.display=\'block\'; return false; } else return confirm(\'Delete this event\');";s:5:"align";s:5:"right";s:4:"name";s:14:"button[delete]";s:4:"type";s:6:"button";s:4:"help";s:17:"Delete this event";}}i:6;a:4:{s:1:"A";a:3:{s:4:"type";s:8:"template";s:4:"span";s:3:"all";s:4:"name";s:22:"calendar.delete_series";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:4;s:4:"rows";i:6;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.end_hide { display: block; white-space: nowrap; margin-left: 10px; } +$templ_data[] = array('name' => 'calendar.edit','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:7:{s:1:"A";s:3:"100";s:1:"B";s:3:"300";s:2:"h1";s:6:",!@msg";s:2:"c2";s:2:"th";s:2:"h2";s:2:"28";s:2:"h4";s:8:",!@owner";s:2:"h6";s:13:",!@recur_type";}i:1;a:4:{s:1:"A";a:5:{s:4:"span";s:13:"all,redItalic";s:7:"no_lang";s:1:"1";s:5:"align";s:6:"center";s:4:"name";s:3:"msg";s:4:"type";s:4:"html";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:2;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Title";}s:1:"B";a:5:{s:4:"size";s:6:"80,255";s:4:"span";s:3:"all";s:6:"needed";s:1:"1";s:4:"name";s:5:"title";s:4:"type";s:4:"text";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:3;a:4:{s:1:"A";a:5:{s:4:"span";s:3:"all";s:4:"name";s:76:"tabs=general|description|participants|recurrence|custom|links|alarms|history";s:4:"type";s:3:"tab";s:5:"label";s:71:"General|Description|Participants|Recurrence|Custom|Links|Alarms|History";s:4:"help";s:173:"Location, Start- and Endtimes, ...|Full description|Participants, Resources, ...|Repeating Event Information|Custom fields|Links, Attachments|Alarm management|Change history";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}i:4;a:4:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"B";a:3:{s:4:"type";s:14:"select-account";s:8:"readonly";s:4:"true";s:4:"name";s:5:"owner";}s:1:"C";a:7:{s:5:"label";s:7:"Updated";s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:5:"align";s:5:"right";s:4:"name";s:8:"modified";s:4:"type";s:9:"date-time";s:4:"span";s:8:",noBreak";}s:1:"D";a:6:{s:5:"class";s:7:"noBreak";s:4:"type";s:14:"select-account";s:5:"label";s:2:"by";s:8:"readonly";s:4:"true";s:5:"align";s:5:"right";s:4:"name";s:8:"modifier";}}i:5;a:4:{s:1:"A";a:7:{s:4:"span";s:1:"3";s:4:"type";s:4:"hbox";s:4:"size";s:1:"4";i:1;a:4:{s:5:"label";s:4:"Save";s:4:"name";s:12:"button[save]";s:4:"type";s:6:"button";s:4:"help";s:22:"saves the changes made";}i:2;a:4:{s:5:"label";s:5:"Apply";s:4:"name";s:13:"button[apply]";s:4:"type";s:6:"button";s:4:"help";s:17:"apply the changes";}i:3;a:5:{s:5:"label";s:6:"Cancel";s:7:"onclick";s:15:"window.close();";s:4:"name";s:14:"button[cancel]";s:4:"type";s:6:"button";s:4:"help";s:16:"Close the window";}i:4;a:5:{s:8:"onchange";s:34:"this.form.submit(); this.value=\'\';";s:4:"name";s:6:"action";s:4:"size";s:10:"Actions...";s:4:"type";s:6:"select";s:4:"help";s:39:"Execute a further action for this entry";}}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:137:"if ($cont[recur_type]) { getElementById(\'delete_series\').style.display=\'block\'; return false; } else return confirm(\'Delete this event\');";s:5:"align";s:5:"right";s:4:"name";s:14:"button[delete]";s:4:"type";s:6:"button";s:4:"help";s:17:"Delete this event";}}i:6;a:4:{s:1:"A";a:3:{s:4:"type";s:8:"template";s:4:"span";s:3:"all";s:4:"name";s:22:"calendar.delete_series";}s:1:"B";a:1:{s:4:"type";s:5:"label";}s:1:"C";a:1:{s:4:"type";s:5:"label";}s:1:"D";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:4;s:4:"rows";i:6;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.end_hide { display: block; white-space: nowrap; margin-left: 10px; } .noBreak { white-space: nowrap; }','modified' => '1274342574',); $templ_data[] = array('name' => 'calendar.edit.alarms','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:5:{s:2:"c1";s:3:"row";s:1:"A";s:2:"95";s:2:"h1";s:16:"20,@no_add_alarm";s:2:"c2";s:4:",top";s:2:"h2";s:8:",!@alarm";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"B";a:10:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"8";i:1;a:4:{s:4:"type";s:13:"select-number";s:4:"size";s:4:",0,7";s:4:"name";s:15:"new_alarm[days]";s:4:"help";s:4:"days";}i:2;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[days]";s:5:"label";s:4:"days";}i:3;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:16:"new_alarm[hours]";s:4:"size";s:5:",0,23";s:4:"help";s:5:"hours";}i:4;a:3:{s:4:"type";s:5:"label";s:4:"size";s:19:",,,new_alarm[hours]";s:5:"label";s:5:"hours";}i:5;a:4:{s:4:"type";s:13:"select-number";s:4:"name";s:15:"new_alarm[mins]";s:4:"size";s:7:",0,55,5";s:4:"help";s:7:"Minutes";}i:6;a:3:{s:4:"type";s:5:"label";s:4:"size";s:18:",,,new_alarm[mins]";s:5:"label";s:7:"Minutes";}i:7;a:5:{s:4:"type";s:6:"select";s:4:"name";s:16:"new_alarm[owner]";s:7:"no_lang";s:1:"1";s:5:"label";s:3:"for";s:4:"help";s:31:"Select who should get the alarm";}i:8;a:3:{s:4:"type";s:6:"button";s:4:"name";s:17:"button[add_alarm]";s:5:"label";s:9:"Add alarm";}}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Alarms";}s:1:"B";a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:2:{s:2:"c1";s:2:"th";s:2:"c2";s:3:"row";}i:1;a:5:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:4:"Time";}s:1:"B";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"before the event";}s:1:"C";a:2:{s:4:"type";s:5:"label";s:5:"label";s:16:"All participants";}s:1:"D";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Owner";}s:1:"E";a:2:{s:4:"type";s:5:"label";s:5:"label";s:6:"Action";}}i:2;a:5:{s:1:"A";a:3:{s:4:"type";s:9:"date-time";s:4:"name";s:12:"${row}[time]";s:8:"readonly";s:1:"1";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:4:"name";s:14:"${row}[offset]";s:7:"no_lang";s:1:"1";}s:1:"C";a:4:{s:4:"type";s:8:"checkbox";s:5:"align";s:6:"center";s:4:"name";s:11:"${row}[all]";s:8:"readonly";s:1:"1";}s:1:"D";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:13:"${row}[owner]";s:8:"readonly";s:1:"1";}s:1:"E";a:7:{s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:5:"label";s:6:"Delete";s:5:"align";s:6:"center";s:4:"name";s:27:"delete_alarm[$row_cont[id]]";s:4:"help";s:17:"Delete this alarm";s:7:"onclick";s:36:"return confirm(\'Delete this alarm\');";}}}s:4:"rows";i:2;s:4:"cols";i:5;s:4:"name";s:5:"alarm";s:7:"options";a:0:{}}}}s:4:"rows";i:2;s:4:"cols";i:2;s:4:"size";s:17:"100%,210,,,,,auto";s:7:"options";a:3:{i:0;s:4:"100%";i:1;s:3:"210";i:6;s:4:"auto";}}}','size' => '100%,210,,,,,auto','style' => '','modified' => '1118780740',); diff --git a/calendar/templates/default/edit.xet b/calendar/templates/default/edit.xet index aacc908900..9b48846d69 100644 --- a/calendar/templates/default/edit.xet +++ b/calendar/templates/default/edit.xet @@ -323,7 +323,7 @@ - + From 3835e9acbc9d950b3bc3d42bc7f838d229c08d22 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 16 Aug 2010 07:28:58 +0000 Subject: [PATCH 43/88] reverted r31570/7: using label as blur text, as label is already used (as label), using blur text field now, no idea why I didnt used it in the first place ;-) --- etemplate/inc/class.link_widget.inc.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/etemplate/inc/class.link_widget.inc.php b/etemplate/inc/class.link_widget.inc.php index 7d46c2d415..2e224843a7 100644 --- a/etemplate/inc/class.link_widget.inc.php +++ b/etemplate/inc/class.link_widget.inc.php @@ -7,7 +7,7 @@ * @subpackage extensions * @link http://www.egroupware.org * @author Ralf Becker - * @copyright 2002-9 by RalfBecker@outdoor-training.de + * @copyright 2002-10 by RalfBecker@outdoor-training.de * @version $Id$ */ @@ -414,11 +414,10 @@ class link_widget 'no_app_sel' => !!$extension_data['app'], 'id' => is_array($value) ? $value['current'] : $id, 'query' => is_array($value) ? $value['query'] : '', - 'blur' => $cell['label'] ? lang($cell['label']) : + 'blur' => $cell['blur'] ? lang($cell['blur']) : (count($options) == 1 ? lang($app) : lang('Search')), 'extra' => $cell['onchange'] ? ','.self::AJAX_NEED_ONCHANGE : null, // store flang for ajax_search, to display extra_line required by onchange ); - $cell['label'] = ''; // displayed as blur text if ($options) // limit the app-selectbox to the given apps { From a5b73a08eeb573acda8ffbdb38926dcf83c95572 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 16 Aug 2010 14:56:14 +0000 Subject: [PATCH 44/88] quitent debug output if dry-run is set --- addressbook/inc/class.addressbook_import_contacts_csv.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addressbook/inc/class.addressbook_import_contacts_csv.inc.php b/addressbook/inc/class.addressbook_import_contacts_csv.inc.php index 9efa5632b0..ee13a3d3f2 100644 --- a/addressbook/inc/class.addressbook_import_contacts_csv.inc.php +++ b/addressbook/inc/class.addressbook_import_contacts_csv.inc.php @@ -232,7 +232,7 @@ class addressbook_import_contacts_csv implements importexport_iface_import_plugi unset($_data['id']); } if ( $this->dry_run ) { - print_r($_data); + //print_r($_data); $this->results[$_action]++; return true; } else { From 587ffbcf28a444467c87f06c95de85df3c4067e5 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 16 Aug 2010 14:57:36 +0000 Subject: [PATCH 45/88] fixed fatal error: unsupported operand typ --- importexport/inc/class.importexport_import_ui.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/importexport/inc/class.importexport_import_ui.inc.php b/importexport/inc/class.importexport_import_ui.inc.php index b76181a301..70dda8dd88 100644 --- a/importexport/inc/class.importexport_import_ui.inc.php +++ b/importexport/inc/class.importexport_import_ui.inc.php @@ -48,7 +48,7 @@ try { $definition_obj = new importexport_definition($content['definition']); if($content['dry-run']) { - $definition_obj->plugin_options = $definition_obj->plugin_options + array('dry_run' => true); + $definition_obj->plugin_options = (array)$definition_obj->plugin_options + array('dry_run' => true); } $plugin = new $definition_obj->plugin; $file = fopen($content['file']['tmp_name'], 'r'); From 5af87bf976ed89af6130da53ad0f742baf6dfa24 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 18 Aug 2010 09:14:30 +0000 Subject: [PATCH 46/88] using organizationalRole instead of namedObject which seems to be suse specific --- setup/inc/class.setup_cmd_ldap.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup/inc/class.setup_cmd_ldap.inc.php b/setup/inc/class.setup_cmd_ldap.inc.php index 6294805930..97abe8ad36 100644 --- a/setup/inc/class.setup_cmd_ldap.inc.php +++ b/setup/inc/class.setup_cmd_ldap.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package setup - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -173,7 +173,7 @@ class setup_cmd_ldap extends setup_cmd static $requiredObjectclasses = array( 'o' => 'organization', 'ou' => 'organizationalUnit', - 'cn' => array('namedObject','simpleSecurityObject'), + 'cn' => array('organizationalRole','simpleSecurityObject'), 'dc' => array('organization','dcObject'), ); From 6699869e2b276f966d3233780c1bba4bf3addd46 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 18 Aug 2010 14:57:56 +0000 Subject: [PATCH 47/88] If deleted addresses are kept, delete links on final purge, not initial delete (#8157) --- addressbook/inc/class.addressbook_bo.inc.php | 1 - 1 file changed, 1 deletion(-) diff --git a/addressbook/inc/class.addressbook_bo.inc.php b/addressbook/inc/class.addressbook_bo.inc.php index 7be00f21d6..50fa34c496 100755 --- a/addressbook/inc/class.addressbook_bo.inc.php +++ b/addressbook/inc/class.addressbook_bo.inc.php @@ -724,7 +724,6 @@ class addressbook_bo extends addressbook_so $delete = $old; $delete['tid'] = addressbook_so::DELETED_TYPE; $ok = $this->save($delete); - egw_link::unlink(0,'addressbook',$id,'','!file'); } elseif (($ok = parent::delete($id,$check_etag))) { From a65406b88bd3677be27b3e77024ffece9d63e0c6 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 19 Aug 2010 08:07:06 +0000 Subject: [PATCH 48/88] * restore to current system charset, to force all restores to utf-8, and do NOT halt on sql errors in restore --- phpgwapi/inc/class.db_backup.inc.php | 35 ++++++++++++++++++++-------- setup/db_backup.php | 7 +++--- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/phpgwapi/inc/class.db_backup.inc.php b/phpgwapi/inc/class.db_backup.inc.php index 63ac11678e..447c96f206 100644 --- a/phpgwapi/inc/class.db_backup.inc.php +++ b/phpgwapi/inc/class.db_backup.inc.php @@ -347,23 +347,26 @@ class db_backup 'httpproxy_port', 'httpproxy_server_username', 'httpproxy_server_password', + 'system_charset', ); /** * Backup all data in the form of a (compressed) csv file * * @param resource $f file opened with fopen for reading - * @param boolean $convert_to_system_charset=false convert the restored data to the selected system-charset + * @param boolean $convert_to_system_charset=true convert the restored data to the selected system-charset * @param string $filename='' gives the file name which is used in case of a zip archive. * @param boolean $protect_system_config=true should above system_config values be protected (NOT overwritten) * * @returns An empty string or an error message in case of failure. */ - function restore($f,$convert_to_system_charset=false,$filename='',$protect_system_config=true) + function restore($f,$convert_to_system_charset=true,$filename='',$protect_system_config=true) { @set_time_limit(0); ini_set('auto_detect_line_endings',true); + $convert_to_system_charset = true; // enforce now utf-8 as system charset restores of old backups + if ($protect_system_config) { $system_config = array(); @@ -419,6 +422,12 @@ class db_backup return lang("Cant open '%1' for %2", $filename, lang("reading"))."
\n"; } } + // do not stop if for whatever reason some sql statement fails + if ($this->db->Halt_On_Error != 'no') + { + $backup_db_halt_on_error = $this->db->Halt_On_Error; + $this->db->Halt_On_Error = 'no'; + } $table = False; $n = 0; while(!feof($f)) @@ -547,14 +556,7 @@ class db_backup 'config_name' => 'system_charset', ),__LINE__,__FILE__); } - // zip? - if($type == 'zip') - { - fclose($f); - $f = $save_f; - unlink($name); - rmdir($dir.'/database_backup'); - } + // restore protected system config if ($protect_system_config) { foreach($system_config as $row) @@ -565,6 +567,19 @@ class db_backup ),__LINE__,__FILE__); } } + // restore original Halt_On_Error state (if changed) + if ($backup_db_halt_on_error) + { + $this->db->Halt_On_Error = $backup_db_halt_on_error; + } + // zip? + if($type == 'zip') + { + fclose($f); + $f = $save_f; + unlink($name); + rmdir($dir.'/database_backup'); + } if (!$this->db->transaction_commit()) { return lang('Restore failed'); diff --git a/setup/db_backup.php b/setup/db_backup.php index d79194e6ad..da4a019172 100644 --- a/setup/db_backup.php +++ b/setup/db_backup.php @@ -28,8 +28,8 @@ if (!is_object(@$GLOBALS['egw'])) // called from outside eGW ==> setup $tpl_root = $GLOBALS['egw_setup']->html->setup_tpl_dir('setup'); $self = 'db_backup.php'; } -$db_backup = CreateObject('phpgwapi.db_backup'); -$asyncservice = CreateObject('phpgwapi.asyncservice'); +$db_backup = new db_backup(); +$asyncservice = new asyncservice(); // download a backup, has to be before any output !!! if ($_POST['download']) @@ -176,7 +176,7 @@ if ($_POST['restore']) if (is_resource($f = $db_backup->fopen_backup($file,true))) { echo '

'.lang('restore started, this might take a few minutes ...')."

\n".str_repeat(' ',4096); - $db_backup->restore($f, FALSE, $file); + $db_backup->restore($f, true, $file); // allways convert to current system charset on restore $setup_tpl->set_var('error_msg',lang("backup '%1' restored",$file)); if ($run_in_egw) { @@ -185,6 +185,7 @@ if ($_POST['restore']) $GLOBALS['egw_info']['server']['header_admin_user']='admin', $GLOBALS['egw_info']['server']['header_admin_password']=uniqid('pw',true),false,true); echo $cmd->run()."\n"; + echo '

'.lang('You should %1log out%2 and in again, to update your current session!','','')."

\n"; } } else From dd54552881d1d432cddfac543198597696f787b3 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Thu, 19 Aug 2010 16:15:10 +0000 Subject: [PATCH 49/88] Fix incorrect address count when searching organisations (#8117) --- addressbook/inc/class.addressbook_sql.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addressbook/inc/class.addressbook_sql.inc.php b/addressbook/inc/class.addressbook_sql.inc.php index 4a8cb91622..7356122db8 100644 --- a/addressbook/inc/class.addressbook_sql.inc.php +++ b/addressbook/inc/class.addressbook_sql.inc.php @@ -146,7 +146,7 @@ class addressbook_sql extends so_sql_cf if (!$by) { $extra = array( - 'COUNT(org_name) AS org_count', + 'COUNT(DISTINCT egw_addressbook.contact_id) AS org_count', "COUNT(DISTINCT CASE WHEN org_unit IS NULL THEN '' ELSE org_unit END) AS org_unit_count", "COUNT(DISTINCT CASE WHEN adr_one_locality IS NULL THEN '' ELSE adr_one_locality END) AS adr_one_locality_count", ); @@ -161,7 +161,7 @@ class addressbook_sql extends so_sql_cf parent::search($param['search'],array('org_name'),$append,array( "NULL AS $by", '1 AS is_main', - 'COUNT(org_name) AS org_count', + 'COUNT(DISTINCT egw_addressbook.contact_id) AS org_count', "COUNT(DISTINCT CASE WHEN org_unit IS NULL THEN '' ELSE org_unit END) AS org_unit_count", "COUNT(DISTINCT CASE WHEN adr_one_locality IS NULL THEN '' ELSE adr_one_locality END) AS adr_one_locality_count", ),'%',false,'OR','UNION',$filter); @@ -170,7 +170,7 @@ class addressbook_sql extends so_sql_cf parent::search($param['search'],array('org_name'),$append,array( "CASE WHEN $by IS NULL THEN '' ELSE $by END AS $by", '0 AS is_main', - 'COUNT(org_name) AS org_count', + 'COUNT(DISTINCT egw_addressbook.contact_id) AS org_count', "COUNT(DISTINCT CASE WHEN org_unit IS NULL THEN '' ELSE org_unit END) AS org_unit_count", "COUNT(DISTINCT CASE WHEN adr_one_locality IS NULL THEN '' ELSE adr_one_locality END) AS adr_one_locality_count", ),'%',false,'OR','UNION',$filter); From ebc83beb06913b3ca135595f702e40d6106d048b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 10:18:29 +0000 Subject: [PATCH 50/88] updated mail config to match current emailadmin --- setup/inc/class.setup_cmd_config.inc.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/setup/inc/class.setup_cmd_config.inc.php b/setup/inc/class.setup_cmd_config.inc.php index 29e559f2da..9ff6da5f28 100644 --- a/setup/inc/class.setup_cmd_config.inc.php +++ b/setup/inc/class.setup_cmd_config.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package setup - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -140,13 +140,14 @@ class setup_cmd_config extends setup_cmd array('name' => 'mail_login_type','allowed' => array( 'username (standard)' => 'standard', 'username@domain (virtual mail manager)' => 'vmailmgr', + 'Username/Password defined by admin' => 'admin', 'email (Standard Maildomain should be set)' => 'email', ),'default'=>'standard'), ), '--cyrus' => array( 'imapAdminUsername', 'imapAdminPW', - array('name' => 'imapType','default' => 3), + array('name' => 'imapType','default' => 'cyrusimap'), array('name' => 'imapEnableCyrusAdmin','default' => 'yes'), ), '--sieve' => array( @@ -156,7 +157,7 @@ class setup_cmd_config extends setup_cmd ), '--postfix' => array( array('name' => 'editforwardingaddress','allowed' => array('yes',null)), - array('name' => 'smtpType','default' => 2), + array('name' => 'smtpType','default' => 'postfixldap'), ), '--smtpserver' => array( //smtp server,[smtp port],[smtp user],[smtp password] 'smtp_server',array('name' => 'smtp_port','default' => 25),'smtp_auth_user','smtp_auth_passwd','' From 19a25d392d8f8bfe76ac55a7a84112335e794260 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 11:15:59 +0000 Subject: [PATCH 51/88] some more adapting to current emailadmin: sieve host is not yet used, but defaulting to NULL is what emailadmin does too --- setup/inc/class.setup_cmd_config.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup/inc/class.setup_cmd_config.inc.php b/setup/inc/class.setup_cmd_config.inc.php index 9ff6da5f28..8e96c8df47 100644 --- a/setup/inc/class.setup_cmd_config.inc.php +++ b/setup/inc/class.setup_cmd_config.inc.php @@ -151,7 +151,7 @@ class setup_cmd_config extends setup_cmd array('name' => 'imapEnableCyrusAdmin','default' => 'yes'), ), '--sieve' => array( - array('name' => 'imapSieveServer','default' => 'localhost'), + array('name' => 'imapSieveServer'), array('name' => 'imapSievePort','default' => 2000), array('name' => 'imapEnableSieve','default' => 'yes'), // null or yes ), @@ -365,8 +365,8 @@ class setup_cmd_config extends setup_cmd $defaults['mail_suffix'] = '$domain'; $defaults['imapAdminUsername'] = 'cyrus@$domain'; $defaults['imapAdminPW'] = self::randomstring(); - $defaults['imapType'] = 2; // standard IMAP - $defaults['smtpType'] = 1; // standard SMTP + $defaults['imapType'] = 'defaultimap'; // standard IMAP + $defaults['smtpType'] = 'defaultsmtp'; // standard SMTP return $defaults; } From c71ffc8646a4e1a6ebe114c542440a431b7ede8d Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 13:27:37 +0000 Subject: [PATCH 52/88] new sub-command to delete ldap tree --- setup/inc/class.setup_cmd_ldap.inc.php | 60 +++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/setup/inc/class.setup_cmd_ldap.inc.php b/setup/inc/class.setup_cmd_ldap.inc.php index 97abe8ad36..4ca25cc587 100644 --- a/setup/inc/class.setup_cmd_ldap.inc.php +++ b/setup/inc/class.setup_cmd_ldap.inc.php @@ -95,6 +95,9 @@ class setup_cmd_ldap extends setup_cmd case 'test_ldap': $msg = $this->connect(); break; + case 'delete_ldap': + $msg = $this->delete(); + break; case 'create_ldap': default: $msg = $this->create(); @@ -139,8 +142,6 @@ class setup_cmd_ldap extends setup_cmd /** * Check and if does not yet exist create the new database and user * - * The check will fail if the database exists, but already contains tables - * * @return string with success message * @throws egw_exception_wrong_userinput */ @@ -165,6 +166,61 @@ class setup_cmd_ldap extends setup_cmd $this->ldap_host,$this->ldap_base); } + /** + * Delete whole LDAP tree of an instance dn=$this->ldap_base using $this->ldap_admin/_pw + * + * @return string with success message + * @throws egw_exception if dn not found, not listable or delete fails + */ + private function delete() + { + $this->connect($this->ldap_admin,$this->ldap_admin_pw); + + // if base not set, use context minus one hierarchy, eg. ou=accounts,(o=domain,dc=local) + if (empty($this->ldap_base) && $this->ldap_context) + { + list(,$this->ldap_base) = explode(',',$this->ldap_context,2); + } + // some precausion to not delete whole ldap tree! + if (count(explode(',',$this->ldap_base)) < 2) + { + throw new egw_exception_assertion_failed('Refusing to delete dn "%1"!',$this->ldap_base); + } + // check if base does exist + if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*')) + { + throw new egw_exception_wrong_userinput('Base dn "%1" NOT found!',$this->ldap_base); + } + return lang('LDAP dn="%1" with %2 entries deleted.', + $this->ldap_base,$this->rdelete($this->ldap_base)); + } + + /** + * Recursive delete a dn + * + * @param string $dn + * @return int integer number of deleted entries + * @throws egw_exception if dn not listable or delete fails + */ + private function rdelete($dn) + { + $sr = ldap_list($this->test_ldap->ds,$dn,'ObjectClass=*',array('')); + if (!($entries = ldap_get_entries($this->test_ldap->ds, $sr))) + { + throw new egw_exception('Error listing "dn=%1"!',$dn); + } + foreach($entries as $n => $entry) + { + if ($n == 'count') continue; + $this->rdelete($this->test_ldap->ds,$entry['dn']); + } + if (!ldap_delete($this->test_ldap->ds,$dn)) + { + throw new egw_exception('Error deleting "dn=%1"!',$dn); + } + return 1 + $entries['count']; + } + /** * array with objectclasses for the objects we can create * From aee5936945988af1ef8753a0ca9fda0e9e0a9e33 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 14:18:21 +0000 Subject: [PATCH 53/88] some fixes for base deletion and counting of active users --- setup/inc/class.setup_cmd_ldap.inc.php | 54 ++++++++++++++++++++------ 1 file changed, 43 insertions(+), 11 deletions(-) diff --git a/setup/inc/class.setup_cmd_ldap.inc.php b/setup/inc/class.setup_cmd_ldap.inc.php index 4ca25cc587..9fbaf313f0 100644 --- a/setup/inc/class.setup_cmd_ldap.inc.php +++ b/setup/inc/class.setup_cmd_ldap.inc.php @@ -96,7 +96,10 @@ class setup_cmd_ldap extends setup_cmd $msg = $this->connect(); break; case 'delete_ldap': - $msg = $this->delete(); + $msg = $this->delete_base(); + break; + case 'users_ldap': + $msg = $this->users(); break; case 'create_ldap': default: @@ -138,7 +141,32 @@ class setup_cmd_ldap extends setup_cmd } return lang('Successful connected to LDAP server on %1 using DN %2.',$this->ldap_host,$dn); } - + + /** + * Count active (not expired) users + * + * @return int number of active users + * @throws egw_exception_wrong_userinput + */ + private function users() + { + $this->connect(); + + $sr = ldap_list($this->test_ldap->ds,$this->ldap_context,'ObjectClass=posixAccount',array('dn','shadowExpire')); + if (!($entries = ldap_get_entries($this->test_ldap->ds, $sr))) + { + throw new egw_exception('Error listing "dn=%1"!',$this->ldap_context); + } + $num = 0; + foreach($entries as $n => $entry) + { + if ($n === 'count') continue; + if (isset($entry['shadowexpire']) && $entry['shadowexpire'][0]*24*3600 < time()) continue; + ++$num; + } + return $num; + } + /** * Check and if does not yet exist create the new database and user * @@ -172,7 +200,7 @@ class setup_cmd_ldap extends setup_cmd * @return string with success message * @throws egw_exception if dn not found, not listable or delete fails */ - private function delete() + private function delete_base() { $this->connect($this->ldap_admin,$this->ldap_admin_pw); @@ -184,12 +212,12 @@ class setup_cmd_ldap extends setup_cmd // some precausion to not delete whole ldap tree! if (count(explode(',',$this->ldap_base)) < 2) { - throw new egw_exception_assertion_failed('Refusing to delete dn "%1"!',$this->ldap_base); + throw new egw_exception_assertion_failed(lang('Refusing to delete dn "%1"!',$this->ldap_base)); } // check if base does exist if (!@ldap_read($this->test_ldap->ds,$this->ldap_base,'objectClass=*')) { - throw new egw_exception_wrong_userinput('Base dn "%1" NOT found!',$this->ldap_base); + throw new egw_exception_wrong_userinput(lang('Base dn "%1" NOT found!',$this->ldap_base)); } return lang('LDAP dn="%1" with %2 entries deleted.', $this->ldap_base,$this->rdelete($this->ldap_base)); @@ -204,19 +232,19 @@ class setup_cmd_ldap extends setup_cmd */ private function rdelete($dn) { - $sr = ldap_list($this->test_ldap->ds,$dn,'ObjectClass=*',array('')); - if (!($entries = ldap_get_entries($this->test_ldap->ds, $sr))) + if (!($sr = ldap_list($this->test_ldap->ds,$dn,'ObjectClass=*',array(''))) || + !($entries = ldap_get_entries($this->test_ldap->ds, $sr))) { - throw new egw_exception('Error listing "dn=%1"!',$dn); + throw new egw_exception(lang('Error listing "dn=%1"!',$dn)); } foreach($entries as $n => $entry) { - if ($n == 'count') continue; - $this->rdelete($this->test_ldap->ds,$entry['dn']); + if ($n === 'count') continue; + $this->rdelete($entry['dn']); } if (!ldap_delete($this->test_ldap->ds,$dn)) { - throw new egw_exception('Error deleting "dn=%1"!',$dn); + throw new egw_exception(lang('Error deleting "dn=%1"!',$dn)); } return 1 + $entries['count']; } @@ -304,6 +332,10 @@ class setup_cmd_ldap extends setup_cmd { foreach(self::defaults() as $name => $default) { + if ($this->sub_command == 'delete_ldap' && in_array($name,array('ldap_base','ldap_context'))) + { + continue; // no default on what to delete! + } if (!$this->$name) { //echo "

setting $name='{$this->$name}' to it's default='$default'

\n"; From 9df4f2e93bdeac44d9903d52cdc1babc069560bd Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 19:28:08 +0000 Subject: [PATCH 54/88] setup_cmd_admin execs "admin/admin-cli.php --edit-user" to run all hooks, which can NOT run in setup --- setup/inc/class.setup.inc.php | 16 +++++++++++++++- setup/inc/class.setup_cmd_admin.inc.php | 22 +++++++++++++++++++--- setup/inc/functions.inc.php | 7 +++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/setup/inc/class.setup.inc.php b/setup/inc/class.setup.inc.php index adbb41c5d8..cc5765fc3c 100644 --- a/setup/inc/class.setup.inc.php +++ b/setup/inc/class.setup.inc.php @@ -1053,14 +1053,28 @@ class setup error_log("setup::add_account('$username','$first','$last',\$passwd,'$primary_group',$changepw,'$email') failed! accountid=$accountid"); return false; } - // call add{account|group} hook to create the vfs-home-dirs + // call vfs_home_hooks::add{account|group} hook to create the vfs-home-dirs + // calling general add{account|group} hook fails, as we are only in setup + // --> setup_cmd_admin execs "admin/admin-cli.php --edit-user" to run them if ($primary_group) { vfs_home_hooks::addAccount($account); +/* + $GLOBALS['hook_values'] = $account + array('new_passwd' => $account['account_passwd']); + $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( + 'location' => 'addaccount' + ),False,True); // called for every app now, not only enabled ones +*/ } else { vfs_home_hooks::addGroup($account+array('account_name' => $account['account_lid'])); +/* + $GLOBALS['hook_values'] = $account+(array('account_name' => $account['account_lid'])); + $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( + 'location' => 'addgroup' + ),False,True); // called for every app now, not only enabled ones) +*/ } } if ($primary_group) // only for users, NOT groups diff --git a/setup/inc/class.setup_cmd_admin.inc.php b/setup/inc/class.setup_cmd_admin.inc.php index 99ef65e997..620c05c2f2 100644 --- a/setup/inc/class.setup_cmd_admin.inc.php +++ b/setup/inc/class.setup_cmd_admin.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package setup - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -76,7 +76,12 @@ class setup_cmd_admin extends setup_cmd if (!$this->admin_firstname) $this->set_defaults['admin_firstname'] = $this->admin_firstname = lang('Admin'); if (!$this->admin_lastname) $this->set_defaults['admin_lastname'] = $this->admin_lastname = lang('User'); - + if (strpos($this->admin_email,'$') !== false) + { + $this->set_defaults['email'] = $this->admin_email = str_replace( + array('$domain','$uid','$account_lid'), + array($this->domain,$this->admin_user,$this->admin_user),$this->admin_email); + } $_POST['username'] = $this->admin_user; $_POST['passwd2'] = $_POST['passwd'] = $this->admin_password; $_POST['fname'] = $this->admin_firstname; @@ -97,7 +102,18 @@ class setup_cmd_admin extends setup_cmd throw new egw_exception_wrong_userinput(lang('Error in group-creation !!!'),42); } $this->restore_db(); - + + // run admin/admin-cli.php --edit-user to store the new accounts once in EGroupware + // to run all hooks (some of them can NOT run inside setup) + $cmd = EGW_SERVER_ROOT.'/admin/admin-cli.php --edit-user '. + escapeshellarg($this->admin_user.'@'.$this->domain.','.$this->admin_password.','.$this->admin_user); + exec($cmd,$output,$ret); + $output = implode("\n",$output); + //echo "ret=$ret\n".$output; + if ($ret) + { + throw new egw_exception ($output,$ret); + } return lang('Admin account successful created.'); } } diff --git a/setup/inc/functions.inc.php b/setup/inc/functions.inc.php index 62aad93fa8..91f06e3040 100644 --- a/setup/inc/functions.inc.php +++ b/setup/inc/functions.inc.php @@ -59,6 +59,13 @@ if(!defined('EGW_SERVER_ROOT') && !defined('EGW_INCLUDE_ROOT')) require_once(EGW_INCLUDE_ROOT . '/phpgwapi/inc/common_functions.inc.php'); +// check if eGW's pear repository is installed and prefer it over the regular one +if (is_dir(EGW_SERVER_ROOT.'/egw-pear')) +{ + set_include_path(EGW_SERVER_ROOT.'/egw-pear'.PATH_SEPARATOR.get_include_path()); + //echo "

include_path='".get_include_path()."'

\n"; +} + define('SEP',filesystem_separator()); /** From 442bdbe56475c4f296ca5a482d30c33536e55fb5 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 20 Aug 2010 20:28:59 +0000 Subject: [PATCH 55/88] - new admin-cli command --add-user like --edit-user, but runs addaccount hook for existing accounts too - using --add-user instead of --edit-user in setup_cmd_admin --- admin/admin-cli.php | 14 ++++++++------ admin/inc/class.admin_cmd_edit_user.inc.php | 8 +++++--- setup/inc/class.setup_cmd_admin.inc.php | 4 ++-- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/admin/admin-cli.php b/admin/admin-cli.php index 1e3a74061b..e2da0c7e7d 100755 --- a/admin/admin-cli.php +++ b/admin/admin-cli.php @@ -6,7 +6,7 @@ * @link http://www.egroupware.org * @package admin * @author Ralf Becker - * @copyright (c) 2006/7 by Ralf Becker + * @copyright (c) 2006-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -54,6 +54,9 @@ switch($action) case '--edit-user': return do_edit_user($arg0s); + case '--add-user': // like --edit-account, but always runs addaccount hook + return do_edit_user($arg0s,true); + case '--change-pw': return do_change_pw($arg0s); @@ -280,8 +283,9 @@ function do_change_pw($args) * Edit or add a user to eGroupWare. If you specify groups, they *replace* the exiting memberships! * 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12 * @param array $args admin-account[@domain],admin-password,account[=new-account-name],first-name,last-name,password,email,expires{never(default)|YYYY-MM-DD|already},can-change-pw{true(default)|false},anon-user{true|false(default)},primary-group{Default(default)|...}[,groups,...][,homedirectory,loginshell] + * @param boolean $run_addaccount_hook=null default run hook depending on account existence, true=allways run addaccount hook */ -function do_edit_user($args) +function do_edit_user($args,$run_addaccount_hook=null) { array_shift($args); // admin-account array_shift($args); // admin-pw @@ -321,7 +325,7 @@ function do_edit_user($args) $data['account_lid'] = $account; $account = false; }; - run_command(new admin_cmd_edit_user($account,$data)); + run_command(new admin_cmd_edit_user($account,$data,null,$run_addaccount_hook)); } /** @@ -412,9 +416,7 @@ function do_subscribe_other($account_lid,$pw=null) 'account_lid' => $account_lid, 'passwd' => $pw, ); - include_once(EGW_INCLUDE_ROOT.'/emailadmin/inc/class.bo.inc.php'); - - $emailadmin = new bo(); + $emailadmin = new emailadmin_bo(); $user_profile = $emailadmin->getUserProfile('felamimail'); unset($emailadmin); diff --git a/admin/inc/class.admin_cmd_edit_user.inc.php b/admin/inc/class.admin_cmd_edit_user.inc.php index 9ab3d00c37..9fb4a0f941 100644 --- a/admin/inc/class.admin_cmd_edit_user.inc.php +++ b/admin/inc/class.admin_cmd_edit_user.inc.php @@ -5,7 +5,7 @@ * @link http://www.egroupware.org * @author Ralf Becker * @package admin - * @copyright (c) 2007 by Ralf Becker + * @copyright (c) 2007-10 by Ralf Becker * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @version $Id$ */ @@ -21,8 +21,9 @@ class admin_cmd_edit_user extends admin_cmd_change_pw * @param string/int/array $account account name or id (!$account to add a new account), or array with all parameters * @param array $set=null array with all data to change * @param string $password=null password + * @param boolean $run_addaccount_hook=null default run addaccount for new accounts and editaccount for existing ones */ - function __construct($account,$set=null,$password=null) + function __construct($account,$set=null,$password=null,$run_addaccount_hook=null) { if (!is_array($account)) { @@ -30,6 +31,7 @@ class admin_cmd_edit_user extends admin_cmd_change_pw 'account' => $account, 'set' => $set, 'password' => is_null($password) ? $set['account_passwd'] : $password, + 'run_addaccount_hook' => $run_addaccount_hook, ); } admin_cmd::__construct($account); @@ -167,7 +169,7 @@ class admin_cmd_edit_user extends admin_cmd_change_pw $data['account_passwd'] = $this->password; $GLOBALS['hook_values'] =& $data; $GLOBALS['egw']->hooks->process($GLOBALS['hook_values']+array( - 'location' => $this->account ? 'editaccount' : 'addaccount' + 'location' => $this->account && $this->run_addaccount_hook !== true ? 'editaccount' : 'addaccount' ),False,True); // called for every app now, not only enabled ones) return lang("Account %1 %2",$this->account ? $this->account : $data['account_lid'], diff --git a/setup/inc/class.setup_cmd_admin.inc.php b/setup/inc/class.setup_cmd_admin.inc.php index 620c05c2f2..06d2664cc9 100644 --- a/setup/inc/class.setup_cmd_admin.inc.php +++ b/setup/inc/class.setup_cmd_admin.inc.php @@ -103,9 +103,9 @@ class setup_cmd_admin extends setup_cmd } $this->restore_db(); - // run admin/admin-cli.php --edit-user to store the new accounts once in EGroupware + // run admin/admin-cli.php --add-user to store the new accounts once in EGroupware // to run all hooks (some of them can NOT run inside setup) - $cmd = EGW_SERVER_ROOT.'/admin/admin-cli.php --edit-user '. + $cmd = EGW_SERVER_ROOT.'/admin/admin-cli.php --add-user '. escapeshellarg($this->admin_user.'@'.$this->domain.','.$this->admin_password.','.$this->admin_user); exec($cmd,$output,$ret); $output = implode("\n",$output); From 3597959259c918ad2044b100b860d4003212a808 Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Mon, 23 Aug 2010 11:44:30 +0000 Subject: [PATCH 56/88] show infolog id in linktitle too (,if show_id is switched on) --- infolog/inc/class.infolog_bo.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/infolog/inc/class.infolog_bo.inc.php b/infolog/inc/class.infolog_bo.inc.php index 09fb4cb195..fe40af292f 100644 --- a/infolog/inc/class.infolog_bo.inc.php +++ b/infolog/inc/class.infolog_bo.inc.php @@ -1094,8 +1094,8 @@ class infolog_bo { return $info; } - return !empty($info['info_subject']) ? $info['info_subject'] : - self::subject_from_des($info['info_descr']); + $title = !empty($info['info_subject']) ? $info['info_subject'] :self::subject_from_des($info['info_descr']); + return $title.($GLOBALS['egw_info']['user']['preferences']['infolog']['show_id']?' (#'.$info['info_id'].')':''); } /** From ea97fc438f8440c188f0daa330235ebfb71af3ed Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 23 Aug 2010 12:03:03 +0000 Subject: [PATCH 57/88] using utf-8 encoding for html merge-print --- etemplate/inc/class.bo_merge.inc.php | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/etemplate/inc/class.bo_merge.inc.php b/etemplate/inc/class.bo_merge.inc.php index 5d712df993..b85961cd8b 100644 --- a/etemplate/inc/class.bo_merge.inc.php +++ b/etemplate/inc/class.bo_merge.inc.php @@ -472,22 +472,20 @@ abstract class bo_merge case 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': case 'application/xml': case 'text/xml': + case 'text/html': $is_xml = true; $charset = 'utf-8'; // xml files --> always use utf-8 break; - case 'text/html': - $is_xml = true; - // fall through default: // div. text files --> use our export-charset, defined in addressbook prefs $charset = $this->contacts->prefs['csv_charset']; break; } - //error_log(__METHOD__."('$document', ... ,$mimetype) --> $charset (egw=".$GLOBALS['egw']->translation->charset().', export='.$this->contacts->prefs['csv_charset'].')'); + //error_log(__METHOD__."('$document', ... ,$mimetype) --> $charset (egw=".translation::charset().', export='.$this->contacts->prefs['csv_charset'].')'); // do we need to convert charset - if ($charset && $charset != $GLOBALS['egw']->translation->charset()) + if ($charset && $charset != translation::charset()) { - $replacements = $GLOBALS['egw']->translation->convert($replacements,$GLOBALS['egw']->translation->charset(),$charset); + $replacements = translation::convert($replacements,translation::charset(),$charset); } if ($is_xml) // zip'ed xml document (eg. OO) { From b51c2487680de0af16ce2f61df3dc531a22c6e5e Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 23 Aug 2010 15:57:09 +0000 Subject: [PATCH 58/88] * fixed printing in calendar (only first page of list was printed and some UI not disabled) --> fix for egw.stylite.de bug #8142: calendar list view printed on three pages --- calendar/setup/etemplates.inc.php | 6 +++--- calendar/templates/default/list.xet | 28 +++++++++++++++++----------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/calendar/setup/etemplates.inc.php b/calendar/setup/etemplates.inc.php index b192dd8900..76d2a190ab 100644 --- a/calendar/setup/etemplates.inc.php +++ b/calendar/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * eGroupWare - eTemplates for Application calendar * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2010-08-16 09:15 + * generated by soetemplate::dump4setup() 2010-08-23 17:52 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package calendar @@ -53,7 +53,7 @@ $templ_data[] = array('name' => 'calendar.freetimesearch.rows','template' => '', $templ_data[] = array('name' => 'calendar.import','template' => '','lang' => '','group' => '0','version' => '1.0.1.001','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:0:{}i:1;a:2:{s:1:"A";a:5:{s:4:"type";s:5:"label";s:4:"span";s:13:"all,redItalic";s:4:"name";s:3:"msg";s:7:"no_lang";s:1:"1";s:5:"align";s:6:"center";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:9:"iCal file";}s:1:"B";a:3:{s:4:"type";s:4:"file";s:4:"name";s:9:"ical_file";s:6:"needed";s:1:"1";}}i:3;a:2:{s:1:"A";a:1:{s:4:"type";s:5:"label";}s:1:"B";a:3:{s:4:"type";s:6:"button";s:5:"label";s:6:"Import";s:4:"name";s:6:"import";}}i:4;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:4;s:4:"cols";i:2;s:5:"align";s:6:"center";s:7:"options";a:0:{}}}','size' => '','style' => '','modified' => '1131469789',); -$templ_data[] = array('name' => 'calendar.list','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:2:{s:2:"h2";s:6:",!@msg";s:2:"h1";s:6:",!@css";}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:4:"html";s:4:"span";s:3:"all";s:4:"name";s:3:"css";}}i:2;a:1:{s:1:"A";a:5:{s:5:"align";s:6:"center";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";s:4:"span";s:10:",redItalic";}}i:3;a:1:{s:1:"A";a:3:{s:4:"name";s:2:"nm";s:4:"size";s:18:"calendar.list.rows";s:4:"type";s:9:"nextmatch";}}i:4;a:1:{s:1:"A";a:6:{s:5:"align";s:5:"right";s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:5:{s:5:"label";s:11:"whole query";s:8:"onchange";s:121:"if (this.checked==true && !confirm(\'Apply the action on the whole query, NOT only the shown events\')) this.checked=false;";s:4:"name";s:7:"use_all";s:4:"type";s:8:"checkbox";s:4:"help";s:62:"Apply the action on the whole query, NOT only the shown events";}i:2;a:6:{s:8:"onchange";s:16:"do_action(this);";s:7:"no_lang";s:1:"1";s:4:"name";s:6:"action";s:4:"size";s:19:"Select an action...";s:4:"type";s:6:"select";s:4:"help";s:16:"Select an action";}i:3;a:8:{s:5:"label";s:9:"Check all";s:7:"onclick";s:70:"toggle_all(this.form,form::name(\'nm[rows][checked][]\')); return false;";s:6:"needed";s:1:"1";s:4:"name";s:9:"check_all";s:4:"type";s:6:"button";s:4:"size";s:9:"arrow_ltr";s:4:"help";s:9:"Check all";s:4:"span";s:14:",checkAllArrow";}}}}s:4:"cols";i:1;s:4:"rows";i:4;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.noWrap { white-space: nowrap; } +$templ_data[] = array('name' => 'calendar.list','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:5:{i:0;a:3:{s:2:"h2";s:6:",!@msg";s:2:"h1";s:6:",!@css";s:2:"c4";s:7:"noPrint";}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:4:"html";s:4:"span";s:3:"all";s:4:"name";s:3:"css";}}i:2;a:1:{s:1:"A";a:5:{s:5:"align";s:6:"center";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";s:4:"span";s:10:",redItalic";}}i:3;a:1:{s:1:"A";a:3:{s:4:"name";s:2:"nm";s:4:"size";s:18:"calendar.list.rows";s:4:"type";s:9:"nextmatch";}}i:4;a:1:{s:1:"A";a:6:{s:5:"align";s:5:"right";s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:5:{s:5:"label";s:11:"whole query";s:8:"onchange";s:121:"if (this.checked==true && !confirm(\'Apply the action on the whole query, NOT only the shown events\')) this.checked=false;";s:4:"name";s:7:"use_all";s:4:"type";s:8:"checkbox";s:4:"help";s:62:"Apply the action on the whole query, NOT only the shown events";}i:2;a:6:{s:8:"onchange";s:16:"do_action(this);";s:7:"no_lang";s:1:"1";s:4:"name";s:6:"action";s:4:"size";s:19:"Select an action...";s:4:"type";s:6:"select";s:4:"help";s:16:"Select an action";}i:3;a:8:{s:5:"label";s:9:"Check all";s:7:"onclick";s:70:"toggle_all(this.form,form::name(\'nm[rows][checked][]\')); return false;";s:6:"needed";s:1:"1";s:4:"name";s:9:"check_all";s:4:"type";s:6:"button";s:4:"size";s:9:"arrow_ltr";s:4:"help";s:9:"Check all";s:4:"span";s:14:",checkAllArrow";}}}}s:4:"cols";i:1;s:4:"rows";i:4;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.noWrap { white-space: nowrap; } .image16 img { height: 16px; @@ -61,7 +61,7 @@ $templ_data[] = array('name' => 'calendar.list','template' => '','lang' => '','g $templ_data[] = array('name' => 'calendar.list.dates','template' => '','lang' => '','group' => '0','version' => '1.3.001','data' => 'a:1:{i:0;a:10:{s:4:"type";s:4:"hbox";s:4:"data";a:2:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:1:{s:4:"type";s:5:"label";}}}s:4:"rows";i:1;s:4:"cols";i:1;s:4:"size";s:1:"4";i:1;a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Start";}i:2;a:2:{s:4:"type";s:4:"date";s:4:"name";s:9:"startdate";}i:3;a:2:{s:4:"type";s:5:"label";s:5:"label";s:3:"End";}i:4;a:2:{s:4:"type";s:4:"date";s:4:"name";s:7:"enddate";}s:4:"span";s:12:",custom_hide";}}','size' => '','style' => '.custom_hide { visibility: hidden; }','modified' => '1173420675',); -$templ_data[] = array('name' => 'calendar.list.rows','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:1:"B";s:3:"40%";s:2:"c1";s:2:"th";s:2:"c2";s:7:"row,top";}i:1;a:13:{s:1:"A";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:5:"label";s:5:"Start";s:4:"name";s:9:"cal_start";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:3:"End";s:4:"name";s:7:"cal_end";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"B";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:5:"label";s:5:"Title";s:4:"name";s:9:"cal_title";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:11:"Description";s:4:"name";s:15:"cal_description";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"C";a:3:{s:5:"label";s:5:"Title";s:4:"name";s:9:"cal_title";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"D";a:3:{s:5:"label";s:11:"Description";s:4:"name";s:15:"cal_description";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"E";a:3:{s:5:"label";s:10:"Recurrence";s:4:"name";s:6:"recure";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"F";a:3:{s:5:"label";s:7:"Project";s:4:"name";s:5:"pm_id";s:4:"type";s:16:"nextmatch-header";}s:1:"G";a:3:{s:5:"label";s:8:"Category";s:4:"name";s:6:"cat_id";s:4:"type";s:16:"nextmatch-header";}s:1:"H";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:5:"label";s:5:"Owner";s:8:"readonly";s:4:"true";s:4:"name";s:9:"cal_owner";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:8:"Location";s:4:"name";s:12:"cal_location";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"I";a:4:{s:5:"label";s:5:"Owner";s:8:"readonly";s:4:"true";s:4:"name";s:9:"cal_owner";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"J";a:3:{s:5:"label";s:8:"Location";s:4:"name";s:12:"cal_location";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"K";a:4:{s:5:"label";s:12:"Participants";s:4:"name";s:11:"participant";s:4:"size";s:3:"All";s:4:"type";s:23:"nextmatch-accountfilter";}s:1:"L";a:4:{s:5:"label";s:13:"Custom fields";s:8:"readonly";s:4:"true";s:4:"name";s:3:"cfs";s:4:"type";s:22:"nextmatch-customfields";}s:1:"M";a:6:{s:5:"label";s:7:"Actions";s:5:"class";s:7:"noPrint";s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";s:4:"help";s:9:"Check all";s:4:"span";s:8:",noPrint";}i:2;a:8:{s:5:"label";s:9:"Check all";s:7:"onclick";s:60:"toggle_all(this.form,form::name(\'checked[]\')); return false;";s:6:"needed";s:1:"1";s:5:"align";s:5:"right";s:4:"name";s:9:"check_all";s:4:"type";s:6:"button";s:4:"size";s:5:"check";s:4:"help";s:9:"Check all";}}}i:2;a:13:{s:1:"A";a:5:{s:4:"name";s:5:"start";s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:5:{s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[start]";s:4:"size";s:14:",$cont[format]";s:4:"type";s:9:"date-time";s:4:"span";s:7:",noWrap";}i:2;a:5:{s:8:"readonly";s:4:"true";s:4:"name";s:11:"${row}[end]";s:4:"size";s:14:",$cont[format]";s:4:"type";s:9:"date-time";s:4:"span";s:7:",noWrap";}}s:1:"B";a:5:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"3";i:1;a:5:{s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:4:"name";s:13:"${row}[title]";s:4:"size";s:1:"b";s:4:"type";s:5:"label";}i:2;a:5:{s:7:"no_lang";s:1:"1";s:4:"type";s:3:"box";s:4:"size";s:1:"1";i:1;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[description]";s:4:"type";s:5:"label";}s:4:"span";s:16:",listDescription";}i:3;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:14:"${row}[recure]";s:4:"type";s:5:"label";}}s:1:"C";a:2:{s:4:"name";s:13:"${row}[title]";s:4:"type";s:5:"label";}s:1:"D";a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[description]";s:4:"type";s:5:"label";}s:1:"E";a:2:{s:4:"name";s:14:"${row}[recure]";s:4:"type";s:5:"label";}s:1:"F";a:3:{s:4:"name";s:10:"${row}[id]";s:4:"size";s:23:"calendar,projectmanager";s:4:"type";s:11:"link-string";}s:1:"G";a:3:{s:4:"type";s:10:"select-cat";s:8:"readonly";s:4:"true";s:4:"name";s:16:"${row}[category]";}s:1:"H";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:14:"select-account";s:5:"class";s:6:"noWrap";s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[owner]";}i:2;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:16:"${row}[location]";s:4:"type";s:5:"label";}}s:1:"I";a:4:{s:4:"type";s:14:"select-account";s:5:"class";s:6:"noWrap";s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[owner]";}s:1:"J";a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:16:"${row}[location]";s:4:"type";s:5:"label";}s:1:"K";a:4:{s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:4:"name";s:13:"${row}[parts]";s:4:"type";s:4:"html";}s:1:"L";a:2:{s:4:"name";s:4:"$row";s:4:"type";s:17:"customfields-list";}s:1:"M";a:4:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"vbox";i:1;a:8:{s:5:"class";s:7:"noPrint";s:4:"type";s:4:"hbox";s:4:"size";s:1:"5";i:1;a:6:{s:5:"label";s:4:"View";s:7:"onclick";s:206:"window.open(egw::link(\'/index.php\',\'menuaction=calendar.calendar_uiforms.edit&cal_id=$row_cont[id]&date=$row_cont[date]\'),\'425\',\'dependent=yes,width=750,height=450,scrollbars=yes,status=yes\'); return false;";s:4:"name";s:19:"view[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:4:"view";s:4:"help";s:15:"View this event";}i:2;a:6:{s:4:"type";s:6:"button";s:4:"size";s:9:"timesheet";s:5:"label";s:19:"Add timesheet entry";s:4:"name";s:29:"timesheet[$row_cont[info_id]]";s:7:"onclick";s:267:"window.open(egw::link(\'/index.php\',\'menuaction=timesheet.timesheet_ui.edit&link_app[]=$row_cont[app]&cat_id=$row_cont[category]&link_id[]=$row_cont[app_id]$row_cont[extra_links]\'),\'_blank\',\'dependent=yes,width=600,height=400,scrollbars=yes,status=yes\'); return false;";s:5:"align";s:6:"center";}i:3;a:6:{s:5:"label";s:4:"Edit";s:7:"onclick";s:20:"$row_cont[edit_link]";s:4:"name";s:19:"edit[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:4:"help";s:15:"Edit this event";}i:4;a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:36:"return confirm(\'Delete this event\');";s:4:"name";s:21:"delete[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:17:"Delete this event";}i:5;a:5:{s:5:"align";s:5:"right";s:4:"name";s:9:"checked[]";s:4:"size";s:35:"$row_cont[id]:$row_cont[recur_date]";s:4:"type";s:8:"checkbox";s:4:"help";s:45:"Select multiple contacts for a further action";}}i:2;a:3:{s:4:"size";s:6:"1,,0,0";s:4:"type";s:4:"hbox";i:1;a:6:{s:5:"label";s:11:"Filemanager";s:4:"size";s:18:"filemanager/navbar";s:4:"type";s:6:"button";s:4:"name";s:26:"filemanager[$row_cont[id]]";s:4:"span";s:8:",image16";s:7:"onclick";s:139:"window.location.href=egw::link(\'/index.php\',\'menuaction=filemanager.filemanager_ui.index&path=/apps/calendar/$row_cont[id]\'); return false;";}}}}}s:4:"cols";i:13;s:4:"rows";i:2;s:4:"size";s:14:"100%,,,,,,auto";}}','size' => '100%,,,,,,auto','style' => '','modified' => '1272389089',); +$templ_data[] = array('name' => 'calendar.list.rows','template' => '','lang' => '','group' => '0','version' => '1.7.003','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:1:"B";s:3:"40%";s:2:"c1";s:2:"th";s:2:"c2";s:7:"row,top";}i:1;a:13:{s:1:"A";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:5:"label";s:5:"Start";s:4:"name";s:9:"cal_start";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:3:"End";s:4:"name";s:7:"cal_end";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"B";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:3:{s:5:"label";s:5:"Title";s:4:"name";s:9:"cal_title";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:11:"Description";s:4:"name";s:15:"cal_description";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"C";a:3:{s:5:"label";s:5:"Title";s:4:"name";s:9:"cal_title";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"D";a:3:{s:5:"label";s:11:"Description";s:4:"name";s:15:"cal_description";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"E";a:3:{s:5:"label";s:10:"Recurrence";s:4:"name";s:6:"recure";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"F";a:3:{s:5:"label";s:7:"Project";s:4:"name";s:5:"pm_id";s:4:"type";s:16:"nextmatch-header";}s:1:"G";a:3:{s:5:"label";s:8:"Category";s:4:"name";s:6:"cat_id";s:4:"type";s:16:"nextmatch-header";}s:1:"H";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:5:"label";s:5:"Owner";s:8:"readonly";s:4:"true";s:4:"name";s:9:"cal_owner";s:4:"type";s:20:"nextmatch-sortheader";}i:2;a:3:{s:5:"label";s:8:"Location";s:4:"name";s:12:"cal_location";s:4:"type";s:20:"nextmatch-sortheader";}}s:1:"I";a:4:{s:5:"label";s:5:"Owner";s:8:"readonly";s:4:"true";s:4:"name";s:9:"cal_owner";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"J";a:3:{s:5:"label";s:8:"Location";s:4:"name";s:12:"cal_location";s:4:"type";s:20:"nextmatch-sortheader";}s:1:"K";a:4:{s:5:"label";s:12:"Participants";s:4:"name";s:11:"participant";s:4:"size";s:3:"All";s:4:"type";s:23:"nextmatch-accountfilter";}s:1:"L";a:4:{s:5:"label";s:13:"Custom fields";s:8:"readonly";s:4:"true";s:4:"name";s:3:"cfs";s:4:"type";s:22:"nextmatch-customfields";}s:1:"M";a:7:{s:5:"label";s:7:"Actions";s:5:"class";s:7:"noPrint";s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:5:"label";s:5:"label";s:7:"Actions";s:4:"help";s:9:"Check all";s:4:"span";s:8:",noPrint";}i:2;a:8:{s:5:"label";s:9:"Check all";s:7:"onclick";s:60:"toggle_all(this.form,form::name(\'checked[]\')); return false;";s:6:"needed";s:1:"1";s:5:"align";s:5:"right";s:4:"name";s:9:"check_all";s:4:"type";s:6:"button";s:4:"size";s:5:"check";s:4:"help";s:9:"Check all";}s:4:"span";s:8:",noPrint";}}i:2;a:13:{s:1:"A";a:5:{s:4:"name";s:5:"start";s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:5:{s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[start]";s:4:"size";s:14:",$cont[format]";s:4:"type";s:9:"date-time";s:4:"span";s:7:",noWrap";}i:2;a:5:{s:8:"readonly";s:4:"true";s:4:"name";s:11:"${row}[end]";s:4:"size";s:14:",$cont[format]";s:4:"type";s:9:"date-time";s:4:"span";s:7:",noWrap";}}s:1:"B";a:5:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"3";i:1;a:5:{s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:4:"name";s:13:"${row}[title]";s:4:"size";s:1:"b";s:4:"type";s:5:"label";}i:2;a:5:{s:7:"no_lang";s:1:"1";s:4:"type";s:3:"box";s:4:"size";s:1:"1";i:1;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[description]";s:4:"type";s:5:"label";}s:4:"span";s:16:",listDescription";}i:3;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:14:"${row}[recure]";s:4:"type";s:5:"label";}}s:1:"C";a:2:{s:4:"name";s:13:"${row}[title]";s:4:"type";s:5:"label";}s:1:"D";a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[description]";s:4:"type";s:5:"label";}s:1:"E";a:2:{s:4:"name";s:14:"${row}[recure]";s:4:"type";s:5:"label";}s:1:"F";a:3:{s:4:"name";s:10:"${row}[id]";s:4:"size";s:23:"calendar,projectmanager";s:4:"type";s:11:"link-string";}s:1:"G";a:3:{s:4:"type";s:10:"select-cat";s:8:"readonly";s:4:"true";s:4:"name";s:16:"${row}[category]";}s:1:"H";a:4:{s:4:"type";s:4:"vbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:14:"select-account";s:5:"class";s:6:"noWrap";s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[owner]";}i:2;a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:16:"${row}[location]";s:4:"type";s:5:"label";}}s:1:"I";a:4:{s:4:"type";s:14:"select-account";s:5:"class";s:6:"noWrap";s:8:"readonly";s:4:"true";s:4:"name";s:13:"${row}[owner]";}s:1:"J";a:3:{s:7:"no_lang";s:1:"1";s:4:"name";s:16:"${row}[location]";s:4:"type";s:5:"label";}s:1:"K";a:4:{s:8:"readonly";s:4:"true";s:7:"no_lang";s:1:"1";s:4:"name";s:13:"${row}[parts]";s:4:"type";s:4:"html";}s:1:"L";a:2:{s:4:"name";s:4:"$row";s:4:"type";s:17:"customfields-list";}s:1:"M";a:5:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"vbox";i:1;a:8:{s:5:"class";s:7:"noPrint";s:4:"type";s:4:"hbox";s:4:"size";s:1:"5";i:1;a:6:{s:5:"label";s:4:"View";s:7:"onclick";s:206:"window.open(egw::link(\'/index.php\',\'menuaction=calendar.calendar_uiforms.edit&cal_id=$row_cont[id]&date=$row_cont[date]\'),\'425\',\'dependent=yes,width=750,height=450,scrollbars=yes,status=yes\'); return false;";s:4:"name";s:19:"view[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:4:"view";s:4:"help";s:15:"View this event";}i:2;a:6:{s:4:"type";s:6:"button";s:4:"size";s:9:"timesheet";s:5:"label";s:19:"Add timesheet entry";s:4:"name";s:29:"timesheet[$row_cont[info_id]]";s:7:"onclick";s:267:"window.open(egw::link(\'/index.php\',\'menuaction=timesheet.timesheet_ui.edit&link_app[]=$row_cont[app]&cat_id=$row_cont[category]&link_id[]=$row_cont[app_id]$row_cont[extra_links]\'),\'_blank\',\'dependent=yes,width=600,height=400,scrollbars=yes,status=yes\'); return false;";s:5:"align";s:6:"center";}i:3;a:6:{s:5:"label";s:4:"Edit";s:7:"onclick";s:20:"$row_cont[edit_link]";s:4:"name";s:19:"edit[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:4:"help";s:15:"Edit this event";}i:4;a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:36:"return confirm(\'Delete this event\');";s:4:"name";s:21:"delete[$row_cont[id]]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:17:"Delete this event";}i:5;a:5:{s:5:"align";s:5:"right";s:4:"name";s:9:"checked[]";s:4:"size";s:35:"$row_cont[id]:$row_cont[recur_date]";s:4:"type";s:8:"checkbox";s:4:"help";s:45:"Select multiple contacts for a further action";}}i:2;a:3:{s:4:"size";s:6:"1,,0,0";s:4:"type";s:4:"hbox";i:1;a:6:{s:5:"label";s:11:"Filemanager";s:4:"size";s:18:"filemanager/navbar";s:4:"type";s:6:"button";s:4:"name";s:26:"filemanager[$row_cont[id]]";s:4:"span";s:8:",image16";s:7:"onclick";s:139:"window.location.href=egw::link(\'/index.php\',\'menuaction=filemanager.filemanager_ui.index&path=/apps/calendar/$row_cont[id]\'); return false;";}}s:4:"span";s:8:",noPrint";}}}s:4:"cols";i:13;s:4:"rows";i:2;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '','modified' => '1272389089',); $templ_data[] = array('name' => 'calendar.print','template' => '','lang' => '','group' => '0','version' => '1.6.001','data' => 'a:1:{i:0;a:3:{s:4:"size";s:6:"1,,0,0";s:4:"type";s:4:"hbox";i:1;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:15:{i:0;a:13:{s:1:"A";s:2:"95";s:2:"c2";s:2:"th";s:2:"c5";s:3:"row";s:2:"c6";s:7:"row_off";s:2:"c7";s:3:"row";s:2:"c3";s:3:"row";s:2:"c4";s:3:"row";s:2:"c8";s:3:"row";s:2:"c9";s:3:"row";s:2:"h2";s:2:"28";s:2:"c1";s:2:"th";s:3:"c10";s:4:",top";s:3:"c11";s:2:"th";}i:1;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"image";s:4:"name";s:5:"print";s:7:"onclick";s:15:"window.print();";}s:1:"B";a:4:{s:4:"type";s:5:"label";s:4:"span";s:5:",bold";s:5:"label";s:8:"Calendar";s:4:"size";s:4:"bold";}}i:2;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:5:"Title";}s:1:"B";a:5:{s:4:"type";s:4:"text";s:4:"name";s:5:"title";s:4:"size";s:6:"80,255";s:8:"readonly";s:1:"1";s:4:"span";s:3:"all";}}i:3;a:2:{s:1:"A";a:4:{s:5:"width";s:2:"95";s:4:"size";s:8:",,,start";s:4:"type";s:5:"label";s:5:"label";s:5:"Start";}s:1:"B";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:6:"2,,0,0";i:1;a:3:{s:4:"name";s:5:"start";s:4:"type";s:9:"date-time";s:8:"readonly";s:1:"1";}i:2;a:7:{s:5:"label";s:9:"whole day";s:4:"name";s:9:"whole_day";s:4:"size";s:11:",, ,disable";s:4:"type";s:8:"checkbox";s:4:"help";s:31:"Event will occupy the whole day";s:5:"align";s:6:"center";s:8:"readonly";s:1:"1";}}}i:4;a:2:{s:1:"A";a:4:{s:5:"width";s:1:"0";s:4:"size";s:11:",,,duration";s:4:"type";s:5:"label";s:5:"label";s:8:"Duration";}s:1:"B";a:4:{s:4:"size";s:6:"2,,0,0";s:4:"type";s:4:"hbox";i:1;a:7:{s:7:"no_lang";s:1:"1";s:8:"onchange";s:227:"set_style_by_class(\'table\',\'end_hide\',\'visibility\',this.value == \'\' ? \'visible\' : \'hidden\'); if (this.value == \'\') document.getElementById(form::name(\'end[str]\')).value = document.getElementById(form::name(\'start[str]\')).value;";s:4:"name";s:8:"duration";s:4:"size";s:12:"Use end date";s:4:"type";s:6:"select";s:4:"help";s:23:"Duration of the meeting";s:8:"readonly";s:1:"1";}i:2;a:4:{s:4:"name";s:3:"end";s:4:"type";s:9:"date-time";s:4:"span";s:9:",end_hide";s:8:"readonly";s:1:"1";}}}i:5;a:2:{s:1:"A";a:4:{s:4:"size";s:11:",,,location";s:4:"type";s:5:"label";s:5:"label";s:8:"Location";s:5:"width";s:1:"0";}s:1:"B";a:5:{s:4:"size";s:4:",255";s:4:"name";s:8:"location";s:4:"type";s:4:"text";s:4:"span";s:15:",inputFullWidth";s:8:"readonly";s:1:"1";}}i:6;a:2:{s:1:"A";a:4:{s:4:"size";s:11:",,,priority";s:4:"type";s:5:"label";s:5:"label";s:8:"Priority";s:5:"width";s:1:"0";}s:1:"B";a:3:{s:4:"type";s:15:"select-priority";s:4:"name";s:8:"priority";s:8:"readonly";s:1:"1";}}i:7;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:7:"Options";s:5:"width";s:1:"0";}s:1:"B";a:6:{s:4:"name";s:12:"non_blocking";s:4:"size";s:11:",, ,disable";s:4:"type";s:8:"checkbox";s:4:"help";s:56:"A non blocking event will not conflict with other events";s:5:"label";s:12:"non blocking";s:8:"readonly";s:1:"1";}}i:8;a:2:{s:1:"A";a:1:{s:4:"type";s:5:"label";}s:1:"B";a:5:{s:4:"name";s:6:"public";s:4:"size";s:3:"0,1";s:4:"type";s:8:"checkbox";s:5:"label";s:7:"Private";s:8:"readonly";s:1:"1";}}i:9;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:10:"Categories";}s:1:"B";a:3:{s:4:"type";s:10:"select-cat";s:4:"name";s:8:"category";s:8:"readonly";s:1:"1";}}i:10;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:11:"Description";}s:1:"B";a:3:{s:4:"type";s:8:"textarea";s:4:"name";s:11:"description";s:8:"readonly";s:1:"1";}}i:11;a:2:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:13:"custom fields";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:12;a:2:{s:1:"A";a:3:{s:4:"type";s:12:"customfields";s:4:"span";s:3:"all";s:8:"readonly";s:1:"1";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:13;a:2:{s:1:"A";a:3:{s:4:"type";s:8:"template";s:4:"name";s:27:"calendar.print.participants";s:4:"span";s:3:"all";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}i:14;a:2:{s:1:"A";a:3:{s:4:"type";s:8:"template";s:4:"span";s:3:"all";s:4:"name";s:20:"calendar.print.links";}s:1:"B";a:1:{s:4:"type";s:5:"label";}}}s:4:"cols";i:2;s:4:"rows";i:14;s:4:"size";s:8:"100%,200";}}}','size' => '','style' => '','modified' => '1229596125',); diff --git a/calendar/templates/default/list.xet b/calendar/templates/default/list.xet index 3c7a7dddc3..64c95ad2cf 100644 --- a/calendar/templates/default/list.xet +++ b/calendar/templates/default/list.xet @@ -1,8 +1,8 @@ -