From af5804129f646bca80a77b5566412c4fffc914be Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 8 Jun 2007 08:51:05 +0000 Subject: [PATCH 01/15] "two fixes for the num_rows in the next-match widget: - use a default of 15 rows, if user&group has no pref set - use what's stored in the extension data, if value not set (nm-header not shown)" --- etemplate/inc/class.nextmatch_widget.inc.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/etemplate/inc/class.nextmatch_widget.inc.php b/etemplate/inc/class.nextmatch_widget.inc.php index 4f141075c1..9c4af8cae5 100644 --- a/etemplate/inc/class.nextmatch_widget.inc.php +++ b/etemplate/inc/class.nextmatch_widget.inc.php @@ -209,7 +209,7 @@ } if (!isset($value['cat_app'])) $value['cat_app'] = $app; // if no cat_app set, use the app from the get_rows func - $max = (int)$GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; + if (!($max = (int)$GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs'])) $max = 15; $row_options = array(); foreach(array(5,12,25,50,100,200,500,999) as $n) { @@ -593,10 +593,10 @@ { $loop = true; // num_rows changed } - $value['num_rows'] = (int) $value['num_rows']; + // num_rows: use old value in extension data, if $value['num_rows'] is not set because nm-header is not shown + $value['num_rows'] = isset($value['num_rows']) ? (int) $value['num_rows'] : (int) $extension_data['num_rows']; $max = $value['num_rows'] ? $value['num_rows'] : (int)$GLOBALS['egw_info']['user']['preferences']['common']['maxmatchs']; - if(strpos($value['search'],'xjxquery')) { // We deal with advancedsearch $aXml = $value['search']; From 6ca395375e3b1aa751399088902b8c437194a38b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 8 Jun 2007 10:24:20 +0000 Subject: [PATCH 02/15] fixed not working context sensitiv manual page for site config pages --- 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 7c413e02a6..ce2aa6e4c9 100644 --- a/phpgwapi/inc/class.egw_framework.inc.php +++ b/phpgwapi/inc/class.egw_framework.inc.php @@ -449,7 +449,7 @@ class egw_framework { $apps[$app]['target'] = ' target="'.$app.'" onClick="'."if (this != '') { window.open(this+'". (strpos($apps[$app]['url'],'?') !== false ? '&' : '?'). - "referer='+encodeURI(location),this.target,'width=800,height=600,scrollbars=yes,resizable=yes'); return false; } else { return true; }".'"'; + "referer='+encodeURIComponent(location),this.target,'width=800,height=600,scrollbars=yes,resizable=yes'); return false; } else { return true; }".'"'; } elseif(isset($GLOBALS['egw_info']['flags']['navbar_target']) && $GLOBALS['egw_info']['flags']['navbar_target']) { From d56c51d71d44a442bac65507c24002795ed30687 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Fri, 8 Jun 2007 15:42:07 +0000 Subject: [PATCH 03/15] deny ADS logins with empty passwords, in case anonymous search/bind is enabled on ADS --- phpgwapi/inc/class.auth_ads.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.auth_ads.inc.php b/phpgwapi/inc/class.auth_ads.inc.php index f9262538cd..6b59b91265 100644 --- a/phpgwapi/inc/class.auth_ads.inc.php +++ b/phpgwapi/inc/class.auth_ads.inc.php @@ -48,8 +48,8 @@ ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0); - /* Login with the LDAP Admin. User to find the User DN. */ - if(!@ldap_bind($ldap,$username.'@'.$GLOBALS['egw_info']['server']['ads_domain'],$passwd)) + // bind with username@ads_domain, only if a non-empty password given, in case anonymous search is enabled + if(empty($passwd) || !@ldap_bind($ldap,$username.'@'.$GLOBALS['egw_info']['server']['ads_domain'],$passwd)) { //echo "

Cant bind with '$username@".$GLOBALS['egw_info']['server']['ads_domain']."' with PW '$passwd' !!!

\n"; return False; From 81cb29ce8b5dd353a266e074c1103bcfb0097096 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 9 Jun 2007 14:56:49 +0000 Subject: [PATCH 04/15] "fixing the fix: if no title was entered, you got a validation error instead of the blur-title" --- timesheet/inc/class.uitimesheet.inc.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/timesheet/inc/class.uitimesheet.inc.php b/timesheet/inc/class.uitimesheet.inc.php index eb2b184c4d..cbfbcc5683 100644 --- a/timesheet/inc/class.uitimesheet.inc.php +++ b/timesheet/inc/class.uitimesheet.inc.php @@ -140,10 +140,15 @@ class uitimesheet extends botimesheet { $this->data['ts_title'] = $this->data['ts_project']; } - if (!$this->data['ts_title']) $this->data['ts_title'] = $this->data['ts_title_blur']; if (!$this->data['ts_title']) { - $etpl->set_validation_error('ts_title',lang('Field must not be empty !!!')); + $this->data['ts_title'] = $this->data['ts_title_blur'] ? + $this->data['ts_title_blur'] : $this->data['ts_project_blur']; + + if (!$this->data['ts_title']) + { + $etpl->set_validation_error('ts_title',lang('Field must not be empty !!!')); + } } if ($etpl->validation_errors()) break; // the user need to fix the error, before we can save the entry From 7a3e571376c31868485074e902aab6577fd04569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20Wei=C3=9F?= Date: Sat, 9 Jun 2007 15:36:30 +0000 Subject: [PATCH 05/15] - reworked conversions backend in importexport_helper_functions - reworked plugin interfaces. - charset is now an attribut of plugin_options only - import / export function itselve is not static any more - whiped out silly phpdoc tags generated by umbrello (kde uml modeler) - reworked representation of plugin_options for definitions - many fixes in uiexport and export_csv - lang updates --- importexport/importexport_cli.php | 11 +- importexport/inc/class.arrayxml.inc.php | 6 +- importexport/inc/class.bodefinitions.inc.php | 28 ++- importexport/inc/class.definition.inc.php | 24 +- importexport/inc/class.export_csv.inc.php | 90 +++----- .../inc/class.iface_export_plugin.inc.php | 7 +- .../inc/class.iface_export_record.inc.php | 16 +- .../inc/class.iface_import_plugin.inc.php | 3 +- .../inc/class.iface_import_record.inc.php | 16 +- importexport/inc/class.import_csv.inc.php | 53 ++--- ...ass.import_export_helper_functions.inc.php | 214 ++++++++---------- importexport/inc/class.uidefinitions.inc.php | 2 +- importexport/inc/class.uiexport.inc.php | 13 +- importexport/js/export_dialog.js | 28 +++ importexport/setup/etemplates.inc.php | 54 ++--- importexport/setup/phpgw_de.lang | 11 + importexport/setup/phpgw_en.lang | 10 + 17 files changed, 273 insertions(+), 313 deletions(-) create mode 100644 importexport/js/export_dialog.js diff --git a/importexport/importexport_cli.php b/importexport/importexport_cli.php index 2264e842d7..ac21d62cc7 100755 --- a/importexport/importexport_cli.php +++ b/importexport/importexport_cli.php @@ -15,7 +15,6 @@ $usage = "usage: --definition --file - --charset --user --password --domain \n"; @@ -48,7 +47,6 @@ $long_opts = array( 'definition=', 'file=', - 'charset=', 'user=', 'password=', 'domain=' @@ -73,9 +71,6 @@ case '--file' : $file = $option[1]; break; - case '--charset' : - $charset = $option[1]; - break; case '--definition' : $definition = $option[1]; break; @@ -98,10 +93,6 @@ fwrite(STDERR,'importexport_cli: You have to supply a username / password'."\n".$usage); exit(INVALID_OPTION); } - if ( !$charset ) { - fwrite(STDERR,'importexport_cli: You have to supply a valid charset'."\n".$usage); - exit(INVALID_OPTION); - } $GLOBALS['egw_info']['flags'] = array( 'disable_Template_class' => True, @@ -153,7 +144,7 @@ $type = $definition->type; $resource = fopen( $file, 'r' ); - $po->$type( $resource, $charset, $definition ); + $po->$type( $resource, $definition ); $GLOBALS['egw']->common->phpgw_exit(); diff --git a/importexport/inc/class.arrayxml.inc.php b/importexport/inc/class.arrayxml.inc.php index 97de6f4c2f..08bad351ed 100644 --- a/importexport/inc/class.arrayxml.inc.php +++ b/importexport/inc/class.arrayxml.inc.php @@ -25,7 +25,6 @@ class arrayxml { /** * converts a php array to an xml string * - * @static * @param mixed $_data * @param string $_name * @param DOMElement $_node @@ -72,7 +71,6 @@ class arrayxml { /** * converts XML string into php array * - * @static * @param string $_xml * @return array */ @@ -98,10 +96,10 @@ class arrayxml { } else { switch ( $type ) { case 'boolean' : - $value = utf8_decode($nc->nodeValue) == 'FALSE' ? false : true; + $value = $nc->nodeValue == 'FALSE' ? false : true; break; default : - $value = utf8_decode($nc->nodeValue); + $value = $nc->nodeValue; } $xml_array[$name] = $value; } diff --git a/importexport/inc/class.bodefinitions.inc.php b/importexport/inc/class.bodefinitions.inc.php index 1aea53a901..a64506a6ac 100644 --- a/importexport/inc/class.bodefinitions.inc.php +++ b/importexport/inc/class.bodefinitions.inc.php @@ -12,6 +12,7 @@ require_once(EGW_INCLUDE_ROOT. '/importexport/inc/class.definition.inc.php'); require_once(EGW_INCLUDE_ROOT. '/importexport/inc/class.arrayxml.inc.php'); +require_once(EGW_INCLUDE_ROOT. '/importexport/inc/class.import_export_helper_functions.inc.php'); require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.so_sql.inc.php'); /** bo to define {im|ex}ports @@ -24,11 +25,13 @@ class bodefinitions { const _defintion_talbe = 'egw_importexport_definitions'; /** - * holds so_sql - * - * @var so_sql + * @var so_sql holds so_sql */ private $so_sql; + + /** + * @var array hold definitions + */ private $definitions; public function __construct($_query=false) @@ -43,11 +46,8 @@ class bodefinitions { } /** - * gets definitions as raw data. - * well, we need a god idea for egw_record pools... - * its not a god idea to make a definition object of each - * at the moment, as each defintion holds an so_sql instance. - * + * gets array of definition ids + * * @return array */ public function get_definitions() { @@ -121,6 +121,14 @@ class bodefinitions { foreach ($keys as $definition_id) { $definition = new definition( $definition_id ); $export_data['definitions'][$definition->name] = $definition->get_record_array(); + $export_data['definitions'][$definition->name]['allowed_users'] = + import_export_helper_functions::account_id2name( + $export_data['definitions'][$definition->name]['allowed_users'] + ); + $export_data['definitions'][$definition->name]['owner'] = + import_export_helper_functions::account_id2name( + $export_data['definitions'][$definition->name]['owner'] + ); unset($export_data['definitions'][$definition->name]['definition_id']); unset($definition); } @@ -159,6 +167,10 @@ class bodefinitions { // save definition(s) into internal table foreach ( $definitions as $name => $definition_data ) { + // convert allowed_user + $definition_data['allowed_users'] = import_export_helper_functions::account_name2id( $definition_data['allowed_users'] ); + $definition_data['owner'] = import_export_helper_functions::account_name2id( $definition_data['owner'] ); + $definition = new definition( $definition_data['name'] ); $definition_id = $definition->get_identifier() ? $definition->get_identifier() : NULL; diff --git a/importexport/inc/class.definition.inc.php b/importexport/inc/class.definition.inc.php index 3a4820dcd8..1a28875d17 100644 --- a/importexport/inc/class.definition.inc.php +++ b/importexport/inc/class.definition.inc.php @@ -11,7 +11,8 @@ */ require_once(EGW_INCLUDE_ROOT. '/importexport/inc/class.iface_egw_record.inc.php'); -require_once(EGW_INCLUDE_ROOT.'/etemplate/inc/class.so_sql.inc.php'); +require_once(EGW_INCLUDE_ROOT. '/importexport/inc/class.arrayxml.inc.php'); +require_once(EGW_INCLUDE_ROOT. '/etemplate/inc/class.so_sql.inc.php'); /** * class definition @@ -39,30 +40,22 @@ class definition implements iface_egw_record { ); /** - * holds so_sql object - * - * @var so_sql + * @var so_sql holds so_sql object */ private $so_sql; /** - * internal representation of definition - * - * @var unknown_type + * @var array internal representation of definition */ private $definition = array(); /** - * holds current user - * - * @var int + * @var int holds current user */ private $user; /** - * is current user an admin? - * - * @var bool + * @var bool is current user an admin? */ private $is_admin; @@ -157,7 +150,8 @@ class definition implements iface_egw_record { * @return array */ private function get_options() { - return unserialize($this->definition['plugin_options']); + $options_data = arrayxml::xml2array( $this->definition['plugin_options'] ); + return $options_data['plugin_options']; } /** @@ -166,7 +160,7 @@ class definition implements iface_egw_record { * @param array $options */ private function set_options(array $_plugin_options) { - $this->definition['plugin_options'] = serialize( $_plugin_options ); + $this->definition['plugin_options'] = arrayxml::array2xml( $_plugin_options, 'plugin_options' ); } /** diff --git a/importexport/inc/class.export_csv.inc.php b/importexport/inc/class.export_csv.inc.php index 35a3eef570..a2f418deaf 100644 --- a/importexport/inc/class.export_csv.inc.php +++ b/importexport/inc/class.export_csv.inc.php @@ -24,57 +24,43 @@ require_once(EGW_INCLUDE_ROOT. '/phpgwapi/inc/class.translation.inc.php'); */ class export_csv implements iface_export_record { - - /** Aggregations: */ - - /** Compositions: */ - /** - * array with field mapping in form egw_field_name => exported_field_name - * @var array + * @var array array with field mapping in form egw_field_name => exported_field_name */ protected $mapping = array(); /** - * array with conversions to be done in form: egw_field_name => conversion_string - * @var array + * @var array array with conversions to be done in form: egw_field_name => conversion_string */ protected $conversion = array(); /** - * array holding the current record - * @access protected + * @var array holding the current record */ protected $record = array(); /** - * holds (charset) translation object - * @var object + * @var translation holds (charset) translation object */ protected $translation; /** - * charset of csv file - * @var string + * @var string charset of csv file */ protected $csv_charset; /** - * holds number of exported records - * @var unknown_type + * @var int holds number of exported records */ protected $num_of_records = 0; /** - * stream resource of csv file - * @var resource + * @var stream stream resource of csv file */ protected $handle; /** - * csv specific options - * - * @var array + * @var array csv specific options */ protected $csv_options = array( 'delimiter' => ';', @@ -84,21 +70,19 @@ class export_csv implements iface_export_record /** * constructor * - * @param object _handle resource where records are exported to. - * @param string _charset charset the records are exported to. + * @param stram $_stream resource where records are exported to. * @param array _options options for specific backends * @return bool - * @access public */ - public function __construct( $_handle, $_charset, array $_options=array() ) { + public function __construct( $_stream, $_options ) { if (!is_object($GLOBALS['egw']->translation)) { $GLOBALS['egw']->translation = new translation(); } $this->translation = &$GLOBALS['egw']->translation; - $this->handle = $_handle; - $this->csv_charset = $_charset; - if (!empty($_options)) { - $this->csv_options = array_merge($this->csv_options,$_options); + $this->handle = $_stream; + $this->csv_charset = $_options['charset'] ? $_options['charset'] : 'utf-8'; + if ( !empty( $_options ) ) { + $this->csv_options = array_merge( $this->csv_options, $_options ); } } @@ -111,9 +95,7 @@ class export_csv implements iface_export_record if ($this->num_of_records > 0) { throw new Exception('Error: Field mapping can\'t be set during ongoing export!'); } - foreach ($_mapping as $egw_filed => $csv_field) { - $this->mapping[$egw_filed] = $this->translation->convert($csv_field, $this->translation->charset(), $this->csv_charset); - } + $this->mapping = $_mapping; } /** @@ -131,45 +113,42 @@ class export_csv implements iface_export_record * * @param iface_egw_record record * @return bool - * @access public */ public function export_record( iface_egw_record $_record ) { - $record_data = $_record->get_record_array(); + $this->record = $_record->get_record_array(); - if (empty($this->mapping)) { - $this->mapping = array_combine(array_keys($record_data),array_keys($record_data)); - } - - // just for debug... - $this->mapping = $this->translation->convert($this->mapping, 'utf-8', 'iso-8859-1');//$this->translation->charset()); - - if ($this->num_of_records == 0 && $this->csv_options['begin_with_fieldnames'] && !empty($this->mapping)) { - fputcsv($this->handle,array_values($this->mapping),$this->csv_options['delimiter'],$this->csv_options['enclosure']); + // begin with fieldnames ? + if ($this->num_of_records == 0 && $this->csv_options['begin_with_fieldnames'] ) { + $mapping = ! empty( $this->mapping ) ? $this->mapping : array_keys ( $this->record ); + $mapping = $this->translation->convert( $mapping, $this->translation->charset(), $this->csv_charset ); + fputcsv( $this->handle ,$mapping ,$this->csv_options['delimiter'], $this->csv_options['enclosure'] ); } // do conversions - if ($this->conversion[$egw_field]) { - $record_data[$egw_field] = import_export_helper_functions::conversion($record_data,$this->conversion); + if ( !empty( $this->conversion )) { + $this->record = import_export_helper_functions::conversion( $this->record, $this->conversion ); + } + + // do fieldmapping + if ( !empty( $this->mapping ) ) { + $record_data = $this->record; + $this->record = array(); + foreach ($this->mapping as $egw_field => $csv_field) { + $this->record[$csv_field] = $record_data[$egw_field]; + } } // do charset translation - $record_data = $this->translation->convert($record_data, $this->translation->charset(), $this->csv_charset); + $this->record = $this->translation->convert( $this->record, $this->translation->charset(), $this->csv_charset ); - // do fieldmapping - foreach ($this->mapping as $egw_field => $csv_field) { - $this->record[$csv_field] = $record_data[$egw_field]; - } - - $this->fputcsv($this->handle,$this->record,$this->csv_options['delimiter'],$this->csv_options['enclosure']); + $this->fputcsv( $this->handle, $this->record, $this->csv_options['delimiter'], $this->csv_options['enclosure'] ); $this->num_of_records++; - $this->record = array(); } /** * Retruns total number of exported records. * * @return int - * @access public */ public function get_num_of_records() { return $this->num_of_records; @@ -179,7 +158,6 @@ class export_csv implements iface_export_record * destructor * * @return - * @access public */ public function __destruct() { diff --git a/importexport/inc/class.iface_export_plugin.inc.php b/importexport/inc/class.iface_export_plugin.inc.php index 667f059cf9..136c220304 100644 --- a/importexport/inc/class.iface_export_plugin.inc.php +++ b/importexport/inc/class.iface_export_plugin.inc.php @@ -39,9 +39,10 @@ interface iface_export_plugin { /** * exports entries according to given definition object. * + * @param stream $_stream * @param definition $_definition */ - public static function export($_stream, $_charset, definition $_definition); + public function export($_stream, definition $_definition); /** * returns translated name of plugin @@ -76,14 +77,14 @@ interface iface_export_plugin { * preserv => array, * ) */ - public static function get_options_etpl(); + public function get_options_etpl(); /** * returns etemplate name for slectors of this plugin * * @return string etemplate name */ - public static function get_selectors_etpl(); + public function get_selectors_etpl(); } // end of iface_export_plugin ?> diff --git a/importexport/inc/class.iface_export_record.inc.php b/importexport/inc/class.iface_export_record.inc.php index 97aabf47ef..ce18700347 100644 --- a/importexport/inc/class.iface_export_record.inc.php +++ b/importexport/inc/class.iface_export_record.inc.php @@ -26,28 +26,20 @@ */ interface iface_export_record { - - /** Aggregations: */ - - /** Compositions: */ - /** * constructor * - * @param object _handle resource where records are exported to. - * @param string _charset charset the records are exported to. - * @param array _options options for specific backends + * @param stream $_stream resource where records are exported to. + * @param array $_options options for specific backends * @return bool - * @access public */ - public function __construct( $_handle, $_charset, array $_options=array() ); + public function __construct( $_stream, array $_options ); /** * exports a record into resource of handle * * @param object of interface egw_record _record * @return bool - * @access public */ public function export_record( iface_egw_record $_record ); @@ -55,7 +47,6 @@ interface iface_export_record * Retruns total number of exported records. * * @return int - * @access public */ public function get_num_of_records( ); @@ -63,7 +54,6 @@ interface iface_export_record * destructor * * @return - * @access public */ public function __destruct( ); diff --git a/importexport/inc/class.iface_import_plugin.inc.php b/importexport/inc/class.iface_import_plugin.inc.php index 553ba6199b..a0744129cc 100644 --- a/importexport/inc/class.iface_import_plugin.inc.php +++ b/importexport/inc/class.iface_import_plugin.inc.php @@ -29,9 +29,10 @@ interface iface_import_plugin { /** * imports entries according to given definition object. * + * @param stram $_stram * @param definition $_definition */ - public function import($_stream, $_charset, definition $_definition); + public function import( $_stream, definition $_definition ); /** * returns translated name of plugin diff --git a/importexport/inc/class.iface_import_record.inc.php b/importexport/inc/class.iface_import_record.inc.php index ed1c767b15..09188248cc 100644 --- a/importexport/inc/class.iface_import_record.inc.php +++ b/importexport/inc/class.iface_import_record.inc.php @@ -20,26 +20,19 @@ */ interface iface_import_record { - - /** Aggregations: */ - - /** Compositions: */ - /** * Opens resource, returns false if something fails * - * @param string _resource resource containing data. Differs according to the implementations - * @param array _options options for the resource + * @param stream $_stream resource containing data. Differs according to the implementations + * @param array $_options options for specific backends * @return bool - * @access public */ - public function __construct( $_resource, $_options ); + public function __construct( $_stream, array $_options ); /** * cleanup * * @return - * @access public */ public function __destruct( ); @@ -48,7 +41,6 @@ interface iface_import_record * * @param string _position may be: {first|last|next|previous|somenumber} * @return bool - * @access public */ public function get_record( $_position = 'next' ); @@ -56,7 +48,6 @@ interface iface_import_record * Retruns total number of records for the open resource. * * @return int - * @access public */ public function get_num_of_records( ); @@ -64,7 +55,6 @@ interface iface_import_record * Returns pointer of current position * * @return int - * @access public */ public function get_current_position( ); diff --git a/importexport/inc/class.import_csv.inc.php b/importexport/inc/class.import_csv.inc.php index bc75912eb9..a5b4345550 100755 --- a/importexport/inc/class.import_csv.inc.php +++ b/importexport/inc/class.import_csv.inc.php @@ -19,49 +19,40 @@ require_once('class.import_export_helper_functions.inc.php'); * This a an abstract implementation of interface iface_import_record * An record is e.g. a single address or or single event. * No mater where the records come from, at the end the get_record method comes out + * @todo Throw away spechial chars and trim() entries ? + * @todo Check for XSS like userinput! (see common_functions) */ class import_csv implements iface_import_record { //, Iterator { const csv_max_linelength = 8000; /** - * @static import_export_helper_functions - */ - - /** - * array with field mapping in form column number => new_field_name - * @access public + * @var array array with field mapping in form column number => new_field_name */ public $mapping = array(); /** - * array with conversions to be done in form: new_field_name => conversion_string - * @access public + * @var array with conversions to be done in form: new_field_name => conversion_string */ public $conversion = array(); /** - * array holding the current record - * @access protected + * @var array holding the current record */ protected $record = array(); /** - * current position counter - * @access protected + * @var int current position counter */ protected $current_position = 0; /** - * holds total number of records - * @access private - * @var int + * @var int holds total number of records */ protected $num_of_records = 0; /** - * csv resource - * @access private + * @var stream */ private $resource; @@ -73,18 +64,16 @@ class import_csv implements iface_import_record { //, Iterator { private $csv_fieldsep; /** - * charset of csv file - * @var string - * @access privat + * + * @var string charset of csv file */ private $csv_charset; /** - * @param string _resource resource containing data. May be each valid php-stream - * @param array _options options for the resource array with keys: charset and fieldsep - * @access public + * @param string $_resource resource containing data. May be each valid php-stream + * @param array $_options options for the resource array with keys: charset and fieldsep */ - public function __construct( $_resource, $_options = array() ) { + public function __construct( $_resource, $_options ) { $this->resource = $_resource; $this->csv_fieldsep = $_options['fieldsep']; $this->csv_charset = $_options['charset']; @@ -93,9 +82,6 @@ class import_csv implements iface_import_record { //, Iterator { /** * cleanup - * - * @return - * @access public */ public function __destruct( ) { } // end of member function __destruct @@ -105,21 +91,20 @@ class import_csv implements iface_import_record { //, Iterator { * * @param mixed _position may be: {current|first|last|next|previous|somenumber} * @return mixed array with data / false if no furtor records - * @access public */ public function get_record( $_position = 'next' ) { if ($this->get_raw_record( $_position ) === false) { return false; } - if ( !empty( $this->mapping ) ) { - $this->do_fieldmapping(); - } - if ( !empty( $this->conversion ) ) { $this->do_conversions(); } + if ( !empty( $this->mapping ) ) { + $this->do_fieldmapping(); + } + return $this->record; } // end of member function get_record @@ -191,7 +176,6 @@ class import_csv implements iface_import_record { //, Iterator { * Retruns total number of records for the open resource. * * @return int - * @access public */ public function get_num_of_records( ) { if ($this->num_of_records > 0) { @@ -208,7 +192,6 @@ class import_csv implements iface_import_record { //, Iterator { * Returns pointer of current position * * @return int - * @access public */ public function get_current_position( ) { @@ -221,7 +204,6 @@ class import_csv implements iface_import_record { //, Iterator { * does fieldmapping according to $this->mapping * * @return - * @access protected */ protected function do_fieldmapping( ) { $record = $this->record; @@ -236,7 +218,6 @@ class import_csv implements iface_import_record { //, Iterator { * does conversions according to $this->conversion * * @return bool - * @access protected */ protected function do_conversions( ) { if ( $record = import_export_helper_functions::conversion( $this->record, $this->conversion )) { diff --git a/importexport/inc/class.import_export_helper_functions.inc.php b/importexport/inc/class.import_export_helper_functions.inc.php index f9d3e51af8..39cfdeda15 100755 --- a/importexport/inc/class.import_export_helper_functions.inc.php +++ b/importexport/inc/class.import_export_helper_functions.inc.php @@ -10,8 +10,6 @@ * @version $Id$ */ - - /** * class import_export_helper_functions (only static methods) * use import_export_helper_functions::method @@ -25,80 +23,75 @@ class import_export_helper_functions { /** * converts accound_lid to account_id * - * @param string _account_lid comma seperated list - * @return string comma seperated list - * @static - * @access public + * @param mixed $_account_lid comma seperated list or array with lids + * @return mixed comma seperated list or array with ids */ - public static function account_lid2id( $_account_lids ) { - $account_lids = explode( ',', $_account_lids ); + public static function account_name2id( $_account_lids ) { + $account_lids = is_array( $_account_lids ) ? $_account_lids : explode( ',', $_account_lids ); foreach ( $account_lids as $account_lid ) { if ( $account_id = $GLOBALS['egw']->accounts->name2id( $account_lid )) { $account_ids[] = $account_id; } } - return implode( ',', $account_ids ); + return is_array( $_account_lids ) ? $account_ids : implode( ',', $account_ids ); } // end of member function account_lid2id /** * converts account_ids to account_lids * - * @param int _account_ids comma seperated list - * @return string comma seperated list - * @static - * @access public + * @param mixed $_account_ids comma seperated list or array with ids + * @return mixed comma seperated list or array with lids */ - public static function account_id2lid( $_account_id ) { - $account_ids = explode( ',', $_account_id ); + public static function account_id2name( $_account_id ) { + $account_ids = is_array( $_account_id ) ? $_account_id : explode( ',', $_account_id ); foreach ( $account_ids as $account_id ) { if ( $account_lid = $GLOBALS['egw']->accounts->id2name( $account_id )) { $account_lids[] = $account_lid; } } - return implode( ',', $account_lids ); + return is_array( $_account_id ) ? $account_lids : implode( ',', $account_lids ); } // end of member function account_id2lid - + /** * converts cat_id to a cat_name * - * @param int _cat_ids comma seperated list - * @return mixed string cat_name - * @static - * @access public + * @param mixed _cat_ids comma seperated list or array + * @return mixed comma seperated list or array with cat_names */ public static function cat_id2name( $_cat_ids ) { - if ( !is_object($GLOBALS['egw']->categories) ) { - $GLOBALS['egw']->categories =& CreateObject('phpgwapi.categories'); - } - $cat_ids = explode( ',', $_cat_id ); + $cats = &CreateObject( 'phpgwapi.categories' ); + + $cat_ids = is_array( $_cat_ids ) ? $_cat_ids : explode( ',', $_cat_ids ); foreach ( $cat_ids as $cat_id ) { - $cat_names[] = $GLOBALS['egw']->categories->id2name( (int)$cat_id ); + $cat_names[] = $cats->id2name( (int)$cat_id ); } - return implode(',',$cat_names); + return is_array( $_cat_ids ) ? $cat_names : implode(',',$cat_names); } // end of member function category_id2name /** * converts cat_name to a cat_id. * If a cat isn't found, it will be created. * - * @param string _cat_names comma seperated list. - * @return mixed int / string (comma seperated cat id's) - * @static - * @access public + * @param mixed $_cat_names comma seperated list or array. + * @return mixed comma seperated list or array with cat_ids */ - public static function cat_name2id( $_cat_names, $_create = true ) { - if (!is_object($GLOBALS['egw']->categories)) { - $GLOBALS['egw']->categories =& CreateObject( 'phpgwapi.categories' ); - } - $cat_names = explode( ',', $_cat_names ); + public static function cat_name2id( $_cat_names ) { + $cats = &CreateObject( 'phpgwapi.categories' ); + $cats->app_name = 'phpgw'; + + $cat_names = is_array( $_cat_names ) ? $_cat_names : explode( ',', $_cat_names ); + foreach ( $cat_names as $cat_name ) { - if ( $cat_id = $GLOBALS['egw']->categories->name2id( addslashes( $cat_name ))) { } - elseif ($_create) $cat_id = $GLOBALS['egw']->categories->add( array( 'name' => $cat_name,'descr' => $cat_name )); - else continue; + if ( $cat_id = $cats->name2id( addslashes( $cat_name ))) { } + else $cat_id = $cats->add( array( + 'name' => $cat_name, + 'access' => 'public', + 'descr' => $cat_name. ' ('. lang('Automatically created by importexport'). ')' + )); $cat_ids[] = $cat_id; } - return implode( ',', $cat_ids ); + return $_cat_names ? $cat_ids : implode( ',', $cat_ids ); } // end of member function category_name2id @@ -113,115 +106,90 @@ class import_export_helper_functions { * This will translate a '1' in the _record field to 'privat' and everything else to 'public'. * * In addintion to the fields assign by the pattern of the reg.exp. - * you can use all other _record fields, with the syntax |[FIELDNAME]. + * you can use all other _record fields, with the syntax |[FIELDINDEX]. * Example: - * .+|>|[Company]: |[NFamily], |[NGiven]|||[NFamily], |[NGiven] - * It is used on the _record field 'Company' and constructs a something like - * Company: FamilyName, GivenName or FamilyName, GivenName if 'Company' is empty. + * Your record is: + * array( 0 => Company, 1 => NFamily, 2 => NGiven + * Your conversion string for field 0 (Company): + * .+|>|[0]: |[1], |[2]|||[1], |[2] + * This constructs something like + * Company: FamilyName, GivenName or FamilyName, GivenName if 'Company' is empty. * - * Moreover the helper function of this class can be used using the '@' operator. - * @cat_name2id(Cat1,...,CatN) returns a (','-separated) list with the cat_id's. If a + * Moreover the two helper function cat() and account() can be used. + * cat(Cat1,...,CatN) returns a (','-separated) list with the cat_id's. If a * category isn't found, it will be automaticaly added. * * Patterns as well as the replacement can be regular expressions (the replacement is done * via ereg_replace). * - * If, after all replacements, the value starts with an '@' the whole - * value is eval()'ed, so you may use all php, phpgw plus your own functions. This is quiet - * powerfull, but circumvents all ACL. Therefor this feature is only availible to - * Adminstrators. - * - * Example using regular expressions and '@'-eval(): - * ||0?([0-9]+)[ .:-]+0?([0-9]*)[ .:-]+0?([0-9]*)[ .:-]+0?([0-9]*)[ .:-]+0?([0-9]*)[ .:-]+0?([0-9]*).*|>@mktime(|#4,|#5,|#6,|#2,|#3,|#1) - * It will read a date of the form '2001-05-20 08:00:00.00000000000000000' (and many more, - * see the regular expr.). The [ .:-]-separated fields are read and assigned in different - * order to @mktime(). Please note to use |# insted of a backslash (I couldn't get backslash - * through all the involved templates and forms.) plus the field-number of the pattern. - * * @param array _record reference with record to do the conversion with * @param array _conversion array with conversion description + * @param object &$cclass calling class to process the '@ evals' (not impelmeted yet) * @return bool - * @static - * @access public - * @todo replace this function with a function dealing with reg expressions! */ - public static function conversion( $_record, $_conversion ) { + public static function conversion( $_record, $_conversion, &$_cclass = null ) { if (empty( $_conversion ) ) return $_record; + $values = $_record; $PSep = '||'; // Pattern-Separator, separats the pattern-replacement-pairs in conversion $ASep = '|>'; // Assignment-Separator, separats pattern and replacesment - $VPre = '|#'; // Value-Prefix, is expanded to \ for ereg_replace - $CPre = '|['; $CPreReg = '\|\['; // |{_record-fieldname} is expanded to the value of the _record-field - $CPos = ']'; $CPosReg = '\]'; // if used together with @ (replacement is eval-ed) value gets autom. quoted + $CPre = '|['; $CPos = ']'; // |[_record-idx] is expanded to the corespondig value - foreach ( $_record as $record_idx => $record_value ) { - $pat_reps = explode($PSep,stripslashes($_conversion[$record_idx])); - $replaces = ''; $rvalues = ''; - if($pat_reps[0] != '') - { - foreach($pat_reps as $k => $pat_rep) - { - list($pattern,$replace) = explode($ASep,$pat_rep,2); - if($replace == '') - { - $replace = $pattern; $pattern = '^.*$'; - } - $rvalues[$pattern] = $replace; // replace two with only one, added by the form - $replaces .= ($replaces != '' ? $PSep : '') . $pattern . $ASep . $replace; + foreach ( $_conversion as $idx => $conversion_string ) { + + if ( empty( $conversion_string ) ) continue; + + // fetch patterns ($rvalues) + $pat_reps = explode( $PSep, stripslashes( $conversion_string ) ); + foreach( $pat_reps as $k => $pat_rep ) { + list( $pattern, $replace ) = explode( $ASep, $pat_rep, 2 ); + if( $replace == '' ) { + $replace = $pattern; $pattern = '^.*$'; } - //$_conversion[$record_idx] = $rvalues; - $conv_record = $rvalues; - } - else - { - //unset($_conversion[$record_idx] ); + $rvalues[$pattern] = $replace; // replace two with only one, added by the form } - $val = $record_value; - if(!empty($_conversion[$record_idx])) - { - //$conv_record = $_conversion[$record_idx]; - while(list($pattern,$replace) = each($conv_record)) - { - if(ereg((string) $pattern,$val)) - { - $val = ereg_replace((string) $pattern,str_replace($VPre,'\\',$replace),(string) $val); + // conversion list may be longer than $_record (no_csv) + $val = array_key_exists( $idx, $_record ) ? $_record[$idx] : ''; - $reg = $CPreReg.'([a-zA-Z_0-9]+)'.$CPosReg; - while(ereg($reg,$val,$vars)) - { // expand all _record fields - $val = str_replace($CPre . $vars[1] . $CPos, $val[0] == '@' ? "'" - . addslashes($fields[array_search($vars[1], array_keys($_record))]) - . "'" : $fields[array_search($vars[1], array_keys($_record))], $val); - } - if($val[0] == '@') - { - if (!$GLOBALS['egw_info']['user']['apps']['admin']) - { - error_log(__FILE__.__LINE__. lang('@-eval() is only availible to admins!!!')); - } - else - { - // removing the $ to close security hole of showing vars, which contain eg. passwords - $val = substr(str_replace('$','',$val),1).';'; - $val = 'return '. (substr($val,0,6) == 'cat_id' ? '$this->'.$val : $val); - // echo "

eval('$val')="; - $val = eval($val); - // echo "'$val'

"; - } - } - if($pattern[0] != '@' || $val) - { - break; - } + foreach ( $rvalues as $pattern => $replace ) { + if( ereg( (string)$pattern, $val) ) { + + $val = ereg_replace( (string)$pattern, $replace, (string)$val ); + + $reg = '\|\[([a-zA-Z_0-9]+)\]'; + while( ereg( $reg, $val, $vars ) ) { + // expand all _record fields + $val = str_replace( + $CPre . $vars[1] . $CPos, + $_record[array_search($vars[1], array_keys($_record))], + $val + ); } + + $val = preg_replace_callback( "/(cat|account)\(([^)]+)\)/i", array( self, 'c2_dispatcher') , $val ); } } - $values[$record_idx] = $val; + $values[$idx] = $val; } return $values; } // end of member function conversion - + + /** + * callback for preg_replace_callback from self::conversion. + * This function gets called when 2nd level conversions are made, + * like the cat() and account() statements in the conversions. + * + * @param array $_matches + */ + private static function c2_dispatcher( $_matches ) { + $action = &$_matches[1]; // cat or account ... + $data = &$_matches[2]; // datas for action + + $method = (string)$action. ( is_int( $data ) ? '_id2name' : '_name2id' ); + return self::$method( $data ); + } + /** * returns a list of importexport plugins * diff --git a/importexport/inc/class.uidefinitions.inc.php b/importexport/inc/class.uidefinitions.inc.php index 2a4e217bfd..ca888161ab 100644 --- a/importexport/inc/class.uidefinitions.inc.php +++ b/importexport/inc/class.uidefinitions.inc.php @@ -128,7 +128,7 @@ class uidefinitions unset($definition); } $content = $definitions; - return $etpl->exec(self::_appname.'.uidefinitions.index',$content,array(),$readonlys,$preserv); + return $etpl->exec( self::_appname.'.uidefinitions.index', $content, array(), $readonlys, $preserv ); } function edit() diff --git a/importexport/inc/class.uiexport.inc.php b/importexport/inc/class.uiexport.inc.php index 8465f55273..114e937b29 100644 --- a/importexport/inc/class.uiexport.inc.php +++ b/importexport/inc/class.uiexport.inc.php @@ -199,7 +199,7 @@ class uiexport { $charset = $GLOBALS['egw']->translation->charset(); } $plugin_object = new $definition->plugin; - $plugin_object->export($file, $charset, $definition); + $plugin_object->export( $file, $definition ); if($_content['export'] == 'pressed') { fclose($file); @@ -224,9 +224,16 @@ class uiexport { fclose($file); unlink($tmpfname); - $preview = $GLOBALS['egw']->translation->convert($preview,'iso-8859-1','utf-8'); + + // NOTE: $definition->plugin_options['charset'] may not be set, + // but it's the best guess atm. + $preview = $GLOBALS['egw']->translation->convert( $preview, + $definition->plugin_options['charset'], + $GLOBALS['egw']->translation->charset() + ); + $response->addAssign('exec[preview-box]','innerHTML',$preview); - $response->addAssign('divPoweredBy','style.display','none'); + //$response->addAssign('divPoweredBy','style.display','none'); $response->addAssign('exec[preview-box]','style.display','inline'); $response->addAssign('exec[preview-box-buttons]','style.display','inline'); diff --git a/importexport/js/export_dialog.js b/importexport/js/export_dialog.js new file mode 100644 index 0000000000..c42e8e27b6 --- /dev/null +++ b/importexport/js/export_dialog.js @@ -0,0 +1,28 @@ +/** + * eGroupWare + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package importexport + * @link http://www.egroupware.org + * @author Cornelius Weiss + * @copyright Cornelius Weiss + * @version $Id:$ + */ + +function export_dialog() { + + this.change_definition = function(sel_obj) { + if(sel_obj.value == 'expert') { + // next two lines fix some strange layout bug + set_style_by_class('tr','select_definition','display','none'); + set_style_by_class('tr','select_definition','display','inline'); + set_style_by_class('tr','select_plugin','display','inline'); + set_style_by_class('tr','save_definition','display','inline'); + } + else { + set_style_by_class('tr','select_plugin','display','none'); + set_style_by_class('tr','save_definition','display','none'); + } + }; +} +var export_dialog = new export_dialog(); \ No newline at end of file diff --git a/importexport/setup/etemplates.inc.php b/importexport/setup/etemplates.inc.php index 3ea6125383..21f62634b0 100644 --- a/importexport/setup/etemplates.inc.php +++ b/importexport/setup/etemplates.inc.php @@ -2,7 +2,7 @@ /** * eGroupWare - eTemplates for Application importexport * http://www.egroupware.org - * generated by soetemplate::dump4setup() 2006-11-16 12:03 + * generated by soetemplate::dump4setup() 2007-06-09 17:40 * * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License * @package importexport @@ -12,28 +12,28 @@ $templ_version=1; -$templ_data[] = array('name' => 'importexport.definition_index','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:1:{s:2:"h1";s:6:",!@msg";}i:1;a:1:{s:1:"A";a:4:{s:4:"span";s:13:"all,redItalic";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}}i:2;a:1:{s:1:"A";a:4:{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:7:"row,top";}i:1;a:6:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:4:"Type";s:4:"span";s:11:",lr_padding";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:5:"label";s:4:"Name";s:4:"span";s:11:",lr_padding";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:5:"label";s:11:"Application";s:4:"span";s:11:",lr_padding";}s:1:"D";a:4:{s:5:"align";s:6:"center";s:4:"type";s:5:"label";s:5:"label";s:13:"Allowed users";s:4:"span";s:11:",lr_padding";}s:1:"E";a:5:{s:5:"label";s:3:"Add";s:5:"align";s:6:"center";s:4:"type";s:6:"button";s:4:"span";s:11:",lr_padding";s:7:"onclick";s:213:"window.open(egw::link(\'/index.php\',\'menuaction=importexport.uidefinitions.wizzard\'),\'\',\'dependent=yes,width=400,height=400,location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes\'); return false; return false;";}s:1:"F";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:6:{s:5:"label";s:6:"Delete";s:4:"name";s:15:"delete_selected";s:4:"type";s:6:"button";s:4:"help";s:31:"delete ALL selected definitions";s:4:"size";s:6:"delete";s:7:"onclick";s:65:"return confirm(\'Do you really want to DELETE this definitions?\');";}i:2;a:5:{s:5:"label";s:6:"Export";s:4:"name";s:15:"export_selected";s:4:"type";s:6:"button";s:4:"help";s:31:"export ALL selected definitions";s:4:"size";s:10:"fileexport";}}}i:2;a:6:{s:1:"A";a:4:{s:7:"no_lang";s:1:"1";s:4:"type";s:5:"image";s:4:"span";s:11:",lr_padding";s:4:"name";s:12:"${row}[type]";}s:1:"B";a:4:{s:7:"no_lang";s:1:"1";s:4:"name";s:12:"${row}[name]";s:4:"type";s:5:"label";s:4:"span";s:11:",lr_padding";}s:1:"C";a:4:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[application]";s:4:"type";s:5:"label";s:4:"span";s:11:",lr_padding";}s:1:"D";a:6:{s:7:"no_lang";s:1:"1";s:4:"type";s:14:"select-account";s:4:"span";s:11:",lr_padding";s:8:"readonly";s:1:"1";s:4:"name";s:21:"${row}[allowed_users]";s:4:"size";s:1:"5";}s:1:"E";a:5:{s:5:"align";s:6:"center";s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:5:"label";s:4:"Edit";s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:7:"onclick";s:237:"window.open(egw::link(\'/index.php\',\'menuaction=importexport.uidefinitions.edit&definition=$row_cont[name]\'),\'\',\'dependent=yes,width=400,height=400,location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes\'); return false; return false;";}i:2;a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:41:"return confirm(\'Delete this definition\');";s:4:"name";s:32:"delete[$row_cont[definition_id]]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:21:"Delete this eTemplate";}}s:1:"F";a:4:{s:5:"align";s:6:"center";s:4:"name";s:34:"selected[$row_cont[definition_id]]";s:4:"type";s:8:"checkbox";s:4:"help";s:34:"select this eTemplate to delete it";}}}s:4:"cols";i:6;s:4:"rows";i:2;}}}s:4:"cols";i:1;s:4:"rows";i:2;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.redItalic { color:red; font-style:italic;} td.lr_padding { padding-left: 5px; padding-right: 5px; }','modified' => '1145972373',); +$templ_data[] = array('name' => 'importexport.definition_index','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:5:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:1:{s:2:"h1";s:6:",!@msg";}i:1;a:1:{s:1:"A";a:4:{s:4:"span";s:13:"all,redItalic";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";s:4:"type";s:5:"label";}}i:2;a:1:{s:1:"A";a:4:{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:7:"row,top";}i:1;a:6:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:5:"label";s:4:"Type";s:4:"span";s:11:",lr_padding";}s:1:"B";a:3:{s:4:"type";s:5:"label";s:5:"label";s:4:"Name";s:4:"span";s:11:",lr_padding";}s:1:"C";a:3:{s:4:"type";s:5:"label";s:5:"label";s:11:"Application";s:4:"span";s:11:",lr_padding";}s:1:"D";a:4:{s:5:"align";s:6:"center";s:4:"type";s:5:"label";s:5:"label";s:13:"Allowed users";s:4:"span";s:11:",lr_padding";}s:1:"E";a:5:{s:5:"label";s:3:"Add";s:5:"align";s:6:"center";s:4:"type";s:6:"button";s:4:"span";s:11:",lr_padding";s:7:"onclick";s:213:"window.open(egw::link(\'/index.php\',\'menuaction=importexport.uidefinitions.wizzard\'),\'\',\'dependent=yes,width=400,height=400,location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes\'); return false; return false;";}s:1:"F";a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:6:{s:5:"label";s:6:"Delete";s:4:"name";s:15:"delete_selected";s:4:"type";s:6:"button";s:4:"help";s:31:"delete ALL selected definitions";s:4:"size";s:6:"delete";s:7:"onclick";s:65:"return confirm(\'Do you really want to DELETE this definitions?\');";}i:2;a:5:{s:5:"label";s:6:"Export";s:4:"name";s:15:"export_selected";s:4:"type";s:6:"button";s:4:"help";s:31:"export ALL selected definitions";s:4:"size";s:10:"fileexport";}}}i:2;a:6:{s:1:"A";a:4:{s:7:"no_lang";s:1:"1";s:4:"type";s:5:"image";s:4:"span";s:11:",lr_padding";s:4:"name";s:12:"${row}[type]";}s:1:"B";a:4:{s:7:"no_lang";s:1:"1";s:4:"name";s:12:"${row}[name]";s:4:"type";s:5:"label";s:4:"span";s:11:",lr_padding";}s:1:"C";a:4:{s:7:"no_lang";s:1:"1";s:4:"name";s:19:"${row}[application]";s:4:"type";s:5:"label";s:4:"span";s:11:",lr_padding";}s:1:"D";a:6:{s:7:"no_lang";s:1:"1";s:4:"type";s:14:"select-account";s:4:"span";s:11:",lr_padding";s:8:"readonly";s:1:"1";s:4:"name";s:21:"${row}[allowed_users]";s:4:"size";s:6:"5,both";}s:1:"E";a:5:{s:5:"align";s:6:"center";s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:5:"label";s:4:"Edit";s:4:"type";s:6:"button";s:4:"size";s:4:"edit";s:7:"onclick";s:237:"window.open(egw::link(\'/index.php\',\'menuaction=importexport.uidefinitions.edit&definition=$row_cont[name]\'),\'\',\'dependent=yes,width=400,height=400,location=no,menubar=no,toolbar=no,scrollbars=yes,status=yes\'); return false; return false;";}i:2;a:6:{s:5:"label";s:6:"Delete";s:7:"onclick";s:41:"return confirm(\'Delete this definition\');";s:4:"name";s:32:"delete[$row_cont[definition_id]]";s:4:"type";s:6:"button";s:4:"size";s:6:"delete";s:4:"help";s:21:"Delete this eTemplate";}}s:1:"F";a:4:{s:5:"align";s:6:"center";s:4:"name";s:34:"selected[$row_cont[definition_id]]";s:4:"type";s:8:"checkbox";s:4:"help";s:34:"select this eTemplate to delete it";}}}s:4:"cols";i:6;s:4:"rows";i:2;}}}s:4:"cols";i:1;s:4:"rows";i:2;s:4:"size";s:4:"100%";}}','size' => '100%','style' => '.redItalic { color:red; font-style:italic;} td.lr_padding { padding-left: 5px; padding-right: 5px; }','modified' => '1145972373',); -$templ_data[] = array('name' => 'importexport.export_dialog','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:1:{s:2:"c3";s:15:"save_definition";}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:3:"tab";s:5:"label";s:25:"General|Selection|Options";s:4:"name";s:37:"general_tab|selection_tab|options_tab";}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:18:"Save as definition";s:4:"name";s:18:"save_as_definition";}}i:4;a:1:{s:1:"A";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:4:"span";s:3:"all";i:1;a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Export";s:4:"name";s:6:"export";s:7:"onclick";s:36:"xajax_eT_wrapper(this);return false;";}i:2;a:4:{s:4:"type";s:6:"button";s:5:"label";s:7:"Preview";s:4:"name";s:7:"preview";s:7:"onclick";s:36:"xajax_eT_wrapper(this);return false;";}}i:2;a:5:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:5:"align";s:5:"right";s:4:"name";s:6:"cancel";s:7:"onclick";s:29:"window.close(); return false;";}}}i:5;a:1:{s:1:"A";a:6:{s:4:"type";s:3:"box";s:4:"size";s:1:"1";s:4:"name";s:11:"preview-box";s:6:"needed";s:1:"1";i:1;a:1:{s:4:"type";s:5:"label";}s:4:"span";s:12:",preview-box";}}i:6;a:1:{s:1:"A";a:7:{s:4:"type";s:3:"box";s:4:"size";s:1:"1";s:4:"span";s:20:",preview-box-buttons";s:4:"name";s:19:"preview-box-buttons";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:2:"OK";s:5:"align";s:6:"center";s:7:"onclick";s:230:"document.getElementById(\'divPoweredBy\').style.display=\'block\'; document.getElementById(form::name(\'preview-box\')).style.display=\'none\'; document.getElementById(form::name(\'preview-box-buttons\')).style.display=\'none\'; return false;";}s:6:"needed";s:1:"1";s:5:"align";s:6:"center";}}}s:4:"rows";i:6;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.preview-box { - position: absolute; - top: 0px; - left: 0px; - width: 400px; - height: 360px; - overflow: scroll; - background-color: white; - z-index: 999; - display: none; -} -.preview-box-buttons { - position: absolute; - top: 365px; - left: 0px; - width: 400px; - height: 20px; - - z-index: 999; - display: none; +$templ_data[] = array('name' => 'importexport.export_dialog','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:7:{i:0;a:1:{s:2:"c3";s:15:"save_definition";}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:4:"name";s:3:"msg";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:3:"tab";s:5:"label";s:25:"General|Selection|Options";s:4:"name";s:37:"general_tab|selection_tab|options_tab";}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:8:"checkbox";s:5:"label";s:18:"Save as definition";s:4:"name";s:18:"save_as_definition";}}i:4;a:1:{s:1:"A";a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:4:"span";s:3:"all";i:1;a:4:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:6:"Export";s:4:"name";s:6:"export";s:7:"onclick";s:36:"xajax_eT_wrapper(this);return false;";}i:2;a:4:{s:4:"type";s:6:"button";s:5:"label";s:7:"Preview";s:4:"name";s:7:"preview";s:7:"onclick";s:36:"xajax_eT_wrapper(this);return false;";}}i:2;a:5:{s:4:"type";s:6:"button";s:5:"label";s:6:"Cancel";s:5:"align";s:5:"right";s:4:"name";s:6:"cancel";s:7:"onclick";s:29:"window.close(); return false;";}}}i:5;a:1:{s:1:"A";a:6:{s:4:"type";s:3:"box";s:4:"size";s:1:"1";s:4:"name";s:11:"preview-box";s:6:"needed";s:1:"1";i:1;a:1:{s:4:"type";s:5:"label";}s:4:"span";s:12:",preview-box";}}i:6;a:1:{s:1:"A";a:7:{s:4:"type";s:3:"box";s:4:"size";s:1:"1";s:4:"span";s:20:",preview-box-buttons";s:4:"name";s:19:"preview-box-buttons";i:1;a:4:{s:4:"type";s:6:"button";s:5:"label";s:2:"OK";s:5:"align";s:6:"center";s:7:"onclick";s:167:"document.getElementById(form::name(\'preview-box\')).style.display=\'none\'; document.getElementById(form::name(\'preview-box-buttons\')).style.display=\'none\'; return false;";}s:6:"needed";s:1:"1";s:5:"align";s:6:"center";}}}s:4:"rows";i:6;s:4:"cols";i:1;s:4:"size";s:4:"100%";s:7:"options";a:1:{i:0;s:4:"100%";}}}','size' => '100%','style' => '.preview-box { + position: absolute; + top: 0px; + left: 0px; + width: 400px; + height: 360px; + overflow: scroll; + background-color: white; + z-index: 999; + display: none; +} +.preview-box-buttons { + position: absolute; + top: 365px; + left: 0px; + width: 400px; + height: 20px; + + z-index: 999; + display: none; }','modified' => '1158220473',); $templ_data[] = array('name' => 'importexport.export_dialog.general_tab','template' => '','lang' => '','group' => '0','version' => '','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:2:{i:0;a:1:{s:2:"c1";s:4:",top";}i:1;a:2:{s:1:"A";a:2:{s:4:"type";s:5:"image";s:4:"name";s:6:"export";}s:1:"B";a:2:{s:4:"type";s:8:"template";s:4:"name";s:46:"importexport.export_dialog.general_tab_content";}}}s:4:"rows";i:1;s:4:"cols";i:2;s:4:"size";s:6:",200px";s:7:"options";a:1:{i:1;s:5:"200px";}}}','size' => ',200px','style' => '','modified' => '1158223670',); @@ -46,11 +46,11 @@ $templ_data[] = array('name' => 'importexport.export_dialog.selection_tab','temp $templ_data[] = array('name' => 'importexport.import_definition','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:4:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:2:{s:4:"type";s:5:"label";s:5:"label";s:92:"Import definitions (Attension: Existing definitions with equal names will be overwritten!!!)";}}i:2;a:1:{s:1:"A";a:2:{s:4:"type";s:4:"file";s:4:"name";s:11:"import_file";}}i:3;a:1:{s:1:"A";a:3:{s:4:"type";s:6:"button";s:5:"label";s:6:"Import";s:4:"name";s:6:"import";}}}s:4:"rows";i:3;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1150533844',); -$templ_data[] = array('name' => 'importexport.wizzardbox','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:2:"c2";s:7:",bottom";s:2:"c1";s:4:",top";s:1:"A";s:4:"100%";}i:1;a:1:{s:1:"A";a:5:{s:4:"type";s:4:"hbox";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"hbox";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"1";i:1;a:3:{s:4:"type";s:5:"image";s:7:"no_lang";s:1:"1";s:4:"name";s:12:"importexport";}}i:2;a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";i:1;a:2:{s:4:"type";s:8:"template";s:4:"name";s:15:"wizzard_content";}s:4:"span";s:16:",wizzard_content";}}}i:2;a:1:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:4:"span";s:3:"all";i:1;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:4:"name";s:16:"button[previous]";s:5:"label";s:8:"previous";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"name";s:12:"button[next]";s:5:"label";s:4:"next";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}i:3;a:4:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[finish]";s:5:"label";s:6:"finish";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}}i:2;a:5:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"cancel";s:7:"onclick";s:29:"window.close(); return false;";s:5:"align";s:5:"right";}s:5:"align";s:5:"right";}}}s:4:"rows";i:2;s:4:"cols";i:1;s:4:"size";s:4:",400";s:7:"options";a:1:{i:1;s:3:"400";}}}','size' => ',400','style' => '.wizzard_content fieldset { - height: 347px; - width: 250px; - max-height:347px; - overflow:auto; +$templ_data[] = array('name' => 'importexport.wizzardbox','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:6:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:3:{s:2:"c2";s:7:",bottom";s:2:"c1";s:4:",top";s:1:"A";s:4:"100%";}i:1;a:1:{s:1:"A";a:5:{s:4:"type";s:4:"hbox";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"2";i:1;a:4:{s:4:"type";s:4:"hbox";s:7:"no_lang";s:1:"1";s:4:"size";s:1:"1";i:1;a:3:{s:4:"type";s:5:"image";s:7:"no_lang";s:1:"1";s:4:"name";s:12:"importexport";}}i:2;a:4:{s:4:"type";s:8:"groupbox";s:4:"size";s:1:"1";i:1;a:2:{s:4:"type";s:8:"template";s:4:"name";s:15:"wizzard_content";}s:4:"span";s:16:",wizzard_content";}}}i:2;a:1:{s:1:"A";a:6:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"2";s:4:"span";s:3:"all";i:1;a:5:{s:4:"type";s:4:"hbox";s:4:"size";s:1:"3";i:1;a:4:{s:4:"type";s:6:"button";s:4:"name";s:16:"button[previous]";s:5:"label";s:8:"previous";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}i:2;a:4:{s:4:"type";s:6:"button";s:4:"name";s:12:"button[next]";s:5:"label";s:4:"next";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}i:3;a:4:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[finish]";s:5:"label";s:6:"finish";s:7:"onclick";s:37:"xajax_eT_wrapper(this); return false;";}}i:2;a:5:{s:4:"type";s:6:"button";s:4:"name";s:14:"button[cancel]";s:5:"label";s:6:"cancel";s:7:"onclick";s:29:"window.close(); return false;";s:5:"align";s:5:"right";}s:5:"align";s:5:"right";}}}s:4:"rows";i:2;s:4:"cols";i:1;s:4:"size";s:4:",400";s:7:"options";a:1:{i:1;s:3:"400";}}}','size' => ',400','style' => '.wizzard_content fieldset { + height: 347px; + width: 250px; + max-height:347px; + overflow:auto; }','modified' => '1145975378',); $templ_data[] = array('name' => 'importexport.wizzard_chooseallowedusers','template' => '','lang' => '','group' => '0','version' => '0.0.1','data' => 'a:1:{i:0;a:4:{s:4:"type";s:4:"grid";s:4:"data";a:3:{i:0;a:0:{}i:1;a:1:{s:1:"A";a:3:{s:4:"type";s:5:"label";s:7:"no_lang";s:1:"1";s:4:"name";s:3:"msg";}}i:2;a:1:{s:1:"A";a:3:{s:4:"type";s:14:"select-account";s:4:"name";s:13:"allowed_users";s:4:"size";s:8:"5,groups";}}}s:4:"rows";i:2;s:4:"cols";i:1;}}','size' => '','style' => '','modified' => '1146312041',); diff --git a/importexport/setup/phpgw_de.lang b/importexport/setup/phpgw_de.lang index 8787d2781a..d99ca3920e 100644 --- a/importexport/setup/phpgw_de.lang +++ b/importexport/setup/phpgw_de.lang @@ -1,7 +1,18 @@ +allowed users importexport de Erlaubte Benutzer choose a name for this definition importexport de Wählen sie einen Namen für diese Definition. choose a plugin importexport de Wählen sie ein Plugin. choose an application importexport de Wählen sei eine Anwendung. +delete all selected definitions importexport de Alle ausgewählten Definitionen löschen +export importexport de Exportieren +export all selected definitions importexport de Alle ausgewählten Definitionen exportieren finish importexport de Fertig +general importexport de Generell +import definitions (attension: existing definitions with equal names will be overwritten!!!) importexport de Definitionen importieren (Achtung: Alle gleichnamigen existierenden Definitionen werden überschrieben!) next importexport de Weiter +preview importexport de Vorschau previous importexport de Zurück +save as definition importexport de Als Definition speichern +select definition importexport de Definition auswählen +select plugin importexport de Plugin auswählen +some nice text importexport de Ein schöner Text which useres are allowed for this definition importexport de Welche Benutzer dürden diese Definition verwenden? diff --git a/importexport/setup/phpgw_en.lang b/importexport/setup/phpgw_en.lang index 865235a5e8..a15374e31d 100644 --- a/importexport/setup/phpgw_en.lang +++ b/importexport/setup/phpgw_en.lang @@ -2,7 +2,17 @@ allowed users importexport en Allowed users choose a name for this definition importexport en Choose a name for this definition choose a plugin importexport en Choose a plugin choose an application importexport en Choose an application +delete all selected definitions importexport en delete ALL selected definitions +export importexport en Export +export all selected definitions importexport en export ALL selected definitions finish importexport en finish +general importexport en General +import definitions (attension: existing definitions with equal names will be overwritten!!!) importexport en Import definitions (Attension: Existing definitions with equal names will be overwritten!!!) next importexport en next +preview importexport en Preview previous importexport en previous +save as definition importexport en Save as definition +select definition importexport en Select definition +select plugin importexport en Select plugin +some nice text importexport en some nice text which useres are allowed for this definition importexport en Which useres are allowed for this definition From e34fb944a54184eefafaa43e7a4271fe1cf9c9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20Wei=C3=9F?= Date: Sat, 9 Jun 2007 15:38:30 +0000 Subject: [PATCH 06/15] - adopted export plugin to new interface - extended outlook export definitions --- .../class.export_contacts_csv.inc.php | 11 +++--- .../class.import_contacts_csv.inc.php | 37 ++++++++----------- .../definitions/outlook_csv_english.xml | 6 ++- .../definitions/outlook_csv_finish.xml | 6 ++- .../definitions/outlook_csv_french.xml | 6 ++- .../definitions/outlook_csv_german.xml | 6 ++- .../definitions/outlook_csv_italian.xml | 6 ++- 7 files changed, 41 insertions(+), 37 deletions(-) diff --git a/addressbook/importexport/class.export_contacts_csv.inc.php b/addressbook/importexport/class.export_contacts_csv.inc.php index d3928b97b7..7a12866d0c 100644 --- a/addressbook/importexport/class.export_contacts_csv.inc.php +++ b/addressbook/importexport/class.export_contacts_csv.inc.php @@ -27,8 +27,8 @@ class export_contacts_csv implements iface_export_plugin { * * @param egw_record $_definition */ - public static function export( $_stream, $_charset, definition $_definition) { - $options = $_definition->options; + public function export( $_stream, definition $_definition) { + $options = $_definition->plugin_options; $uicontacts = new uicontacts(); $selection = array(); @@ -41,8 +41,7 @@ class export_contacts_csv implements iface_export_plugin { $selection = explode(',',$options['selection']); } - $options['begin_with_fieldnames'] = true; - $export_object = new export_csv($_stream, $_charset, (array)$options); + $export_object = new export_csv($_stream, (array)$options); $export_object->set_mapping($options['mapping']); // $options['selection'] is array of identifiers as this plugin doesn't @@ -87,7 +86,7 @@ class export_contacts_csv implements iface_export_plugin { * * @return string html */ - public static function get_options_etpl() { + public function get_options_etpl() { return 'addressbook.export_csv_options'; } @@ -95,7 +94,7 @@ class export_contacts_csv implements iface_export_plugin { * returns slectors of this plugin via xajax * */ - public static function get_selectors_etpl() { + public function get_selectors_etpl() { return 'Selectors:'; } } diff --git a/addressbook/importexport/class.import_contacts_csv.inc.php b/addressbook/importexport/class.import_contacts_csv.inc.php index a915cf2f50..1bdf9892c4 100644 --- a/addressbook/importexport/class.import_contacts_csv.inc.php +++ b/addressbook/importexport/class.import_contacts_csv.inc.php @@ -20,15 +20,12 @@ require_once(EGW_INCLUDE_ROOT.'/importexport/inc/class.import_csv.inc.php'); class import_contacts_csv implements iface_import_plugin { private static $plugin_options = array( - 'fieldsep', //char - 'charset', //string - 'addressbook', //char - 'owner', // comma seperated list of int - 'csv_fields', // array( $csv_col_num => csv_field_name) + 'fieldsep', // char + 'charset', // string + 'contact_owner', // int + 'field_conversion', // array( $csv_col_num => conversion) 'field_mapping', // array( $csv_col_num => adb_filed) - 'field_translation', // array( $csv_col_num => translation) 'has_header_line', //bool - 'max', // int 'conditions', /* => array containing condition arrays: 'type' => 0, // exists 'string' => '#kundennummer', @@ -46,7 +43,7 @@ class import_contacts_csv implements iface_import_plugin { /** * actions wich could be done to data entries */ - private static $actions = array( 'none', 'update', 'insert', 'delte', ); + private static $actions = array( 'none', 'update', 'insert', 'delete', ); /** * conditions for actions @@ -73,7 +70,7 @@ class import_contacts_csv implements iface_import_plugin { * @param string $_charset * @param definition $_definition */ - public function import( $_stream, $_charset, definition $_definition ) { + public function import( $_stream, definition $_definition ) { $import_csv = new import_csv( $_stream, array( 'fieldsep' => $_definition->plugin_options['fieldsep'], 'charset' => $_definition->plugin_options['charset'], @@ -82,23 +79,22 @@ class import_contacts_csv implements iface_import_plugin { // fetch the addressbook bo $this->bocontacts = CreateObject('addressbook.bocontacts'); - // set FieldMapping. Throw away empty / not assigned entrys - $import_csv->mapping = array_diff($_definition->plugin_options['field_mapping'],array('')); + // set FieldMapping. + $import_csv->mapping = $_definition->plugin_options['field_mapping']; - // renamed from translation to conversion - $import_csv->conversion = $_definition->plugin_options['field_conversion'] ? - $_definition->plugin_options['field_conversion'] : - $_definition->plugin_options['field_translation']; + // set FieldConversion + $import_csv->conversion = $_definition->plugin_options['field_conversion']; //check if file has a header line if ($_definition->plugin_options['has_header_line']) { $record = $import_csv->get_record(); } - // TODO: Throw away spechial chars ? - // TODO: check conversion: - // - is not existing cat created? - // - usermapping? + // set contactOwner + if ( isset( $_definition->plugin_options['contact_owner'] ) + && abs( $_definition->plugin_options['contact_owner'] > 0 ) ) { + $record['contact_owner'] = $_definition->plugin_options['contact_owner']; + } while ( $record = $import_csv->get_record() ) { @@ -136,7 +132,7 @@ class import_contacts_csv implements iface_import_plugin { } } else { // unconditional insert - $this->action( 'insert', $values ); + $this->action( 'insert', $record ); } } } @@ -149,7 +145,6 @@ class import_contacts_csv implements iface_import_plugin { * @return bool success or not */ private function action ( $_action, $_data ) { - print_r($_data); switch ($_action) { case 'none' : return true; diff --git a/addressbook/importexport/definitions/outlook_csv_english.xml b/addressbook/importexport/definitions/outlook_csv_english.xml index 8ea1389eb7..5d3caf3f6b 100644 --- a/addressbook/importexport/definitions/outlook_csv_english.xml +++ b/addressbook/importexport/definitions/outlook_csv_english.xml @@ -12,9 +12,11 @@ export_contacts_csv export - -1 + Default + TRUE + iso-8859-15 Title First Name @@ -52,7 +54,7 @@ Notes - 0 + admin Exports selected contacts for english version of MS Outlook diff --git a/addressbook/importexport/definitions/outlook_csv_finish.xml b/addressbook/importexport/definitions/outlook_csv_finish.xml index 63e56b4038..7e6b11c34a 100644 --- a/addressbook/importexport/definitions/outlook_csv_finish.xml +++ b/addressbook/importexport/definitions/outlook_csv_finish.xml @@ -12,9 +12,11 @@ export_contacts_csv export - -1 + Default + TRUE + iso-8859-15 Tehtävänimike Etunimi @@ -51,7 +53,7 @@ Muistilaput - 0 + admin diff --git a/addressbook/importexport/definitions/outlook_csv_french.xml b/addressbook/importexport/definitions/outlook_csv_french.xml index ee2bcf96ef..d749048c57 100644 --- a/addressbook/importexport/definitions/outlook_csv_french.xml +++ b/addressbook/importexport/definitions/outlook_csv_french.xml @@ -12,9 +12,11 @@ export_contacts_csv export - -1 + Default + TRUE + iso-8859-15 Fonction Prénom @@ -51,7 +53,7 @@ Notes - 0 + admin diff --git a/addressbook/importexport/definitions/outlook_csv_german.xml b/addressbook/importexport/definitions/outlook_csv_german.xml index 400cad3455..5ca71584a9 100644 --- a/addressbook/importexport/definitions/outlook_csv_german.xml +++ b/addressbook/importexport/definitions/outlook_csv_german.xml @@ -12,9 +12,11 @@ export_contacts_csv export - -1 + Default + TRUE + iso-8859-15 Anrede Vorname @@ -52,7 +54,7 @@ Notizen - 0 + admin Exportiert ausgewählte Kontakte zur Datenübernahme in die deutsche Version von MS Outlook diff --git a/addressbook/importexport/definitions/outlook_csv_italian.xml b/addressbook/importexport/definitions/outlook_csv_italian.xml index 706ef67438..432a7b1415 100644 --- a/addressbook/importexport/definitions/outlook_csv_italian.xml +++ b/addressbook/importexport/definitions/outlook_csv_italian.xml @@ -12,9 +12,11 @@ export_contacts_csv export - -1 + Default + TRUE + iso-8859-15 Posizione Titolo @@ -52,7 +54,7 @@ Notes - 0 + admin From 1b482d0587a29b3b4c2c2017ad271e21bb9f1fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20Wei=C3=9F?= Date: Sat, 9 Jun 2007 15:39:50 +0000 Subject: [PATCH 07/15] fix for xajax requests --- etemplate/js/etemplate.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etemplate/js/etemplate.js b/etemplate/js/etemplate.js index ec8dc2db45..3a4926092a 100644 --- a/etemplate/js/etemplate.js +++ b/etemplate/js/etemplate.js @@ -217,7 +217,7 @@ function set_style_by_class(t,c,p,v) function xajax_eT_wrapper(obj) { if (typeof(obj) == 'object') { - set_style_by_class('div','popupManual','display','none'); + set_style_by_class('div','popupManual noPrint','display','none'); set_style_by_class('div','ajax-loader','display','inline'); obj.form.submit_button.value = obj.name; var menuaction = obj.form.action.replace(/.+menuaction=/,''); @@ -225,7 +225,7 @@ function xajax_eT_wrapper(obj) { } else { set_style_by_class('div','ajax-loader','display','none'); - set_style_by_class('div','popupManual','display','inline'); + set_style_by_class('div','popupManual noPrint','display','inline'); } } From 349b8c521af4be4250608a7580d7ecedb02e7b3e Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sat, 9 Jun 2007 16:10:20 +0000 Subject: [PATCH 08/15] "fix for bug #866: private InfoLog entries are shown in the calendar day-view, if looking on someone elses calendar" --- infolog/inc/class.soinfolog.inc.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/infolog/inc/class.soinfolog.inc.php b/infolog/inc/class.soinfolog.inc.php index 4c259101a5..58a8b3250d 100644 --- a/infolog/inc/class.soinfolog.inc.php +++ b/infolog/inc/class.soinfolog.inc.php @@ -244,8 +244,7 @@ class soinfolog // DB-Layer if ($filter == 'user' && $f_user > 0) { - $filtermethod = " (info_owner=$f_user AND info_responsible=0 OR $filtermethod AND ".$this->responsible_filter($f_user). - " AND $filtermethod)"; + $filtermethod .= " AND (info_owner=$f_user AND info_responsible=0 OR ".$this->responsible_filter($f_user).')'; } //echo "

aclFilter(filter='$filter_was',user='$user') = '$filtermethod', privat_user_list=".print_r($privat_user_list,True).", public_user_list=".print_r($public_user_list,True)."

\n"; From b6a28b8090f2279c6e2a99e72db9b4e327d743cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cornelius=20Wei=C3=9F?= Date: Sat, 9 Jun 2007 22:23:29 +0000 Subject: [PATCH 09/15] performance fix --- importexport/inc/class.definition.inc.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/importexport/inc/class.definition.inc.php b/importexport/inc/class.definition.inc.php index 1a28875d17..c0ab114978 100644 --- a/importexport/inc/class.definition.inc.php +++ b/importexport/inc/class.definition.inc.php @@ -80,6 +80,8 @@ class definition implements iface_egw_record { if ( !( in_array( $this->user, $this->get_allowed_users() ) || $this->definition['owner'] == $this->user || $this->is_admin)) { throw new Exception('Error: User "'.$this->user.'" is not permitted to get definition with identifier "'.$_identifier.'"!'); } + $options_data = arrayxml::xml2array( $this->definition['plugin_options'] ); + $this->definition['plugin_options'] = $options_data['root']; } } @@ -150,8 +152,7 @@ class definition implements iface_egw_record { * @return array */ private function get_options() { - $options_data = arrayxml::xml2array( $this->definition['plugin_options'] ); - return $options_data['plugin_options']; + return $this->definition['plugin_options']; } /** @@ -160,7 +161,7 @@ class definition implements iface_egw_record { * @param array $options */ private function set_options(array $_plugin_options) { - $this->definition['plugin_options'] = arrayxml::array2xml( $_plugin_options, 'plugin_options' ); + $this->definition['plugin_options'] = $_plugin_options; } /** @@ -204,8 +205,8 @@ class definition implements iface_egw_record { } // convert plugin_options into internal representation - $this->set_allowed_users($this->definition['allowed_users']); - $this->set_options($this->definition['plugin_options']); + $this->set_allowed_users( $this->definition['allowed_users'] ); + $this->set_options( $this->definition['plugin_options'] ); } /** @@ -229,9 +230,11 @@ class definition implements iface_egw_record { } $this->so_sql->data = $this->definition; + $this->so_sql->data['plugin_options'] = arrayxml::array2xml( $this->definition['plugin_options'] ); if ($this->so_sql->save( array( 'definition_id' => $_dst_identifier ))) { throw new Exception('Error: so_sql was not able to save definition: '.$this->get_identifier()); } + return $this->definition['definition_id']; } From 3ddd4a7d178b5f25bd1691b7add1d25655e20d7f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 10 Jun 2007 08:47:14 +0000 Subject: [PATCH 10/15] bugfix: async service "sometimes" misses jobs (db-class was not cloned but copied) --- phpgwapi/inc/class.asyncservice.inc.php | 1245 +++++++++++------------ 1 file changed, 620 insertions(+), 625 deletions(-) diff --git a/phpgwapi/inc/class.asyncservice.inc.php b/phpgwapi/inc/class.asyncservice.inc.php index b2a9ad1377..26767012c2 100644 --- a/phpgwapi/inc/class.asyncservice.inc.php +++ b/phpgwapi/inc/class.asyncservice.inc.php @@ -1,702 +1,697 @@ * - * Class for creating cron-job like timed calls of eGroupWare methods * - * -------------------------------------------------------------------------* - * This library is part of the eGroupWare API * - * 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. * - \**************************************************************************/ +/** + * API - Timed Asynchron Services for eGroupWare + * + * @link http://www.egroupware.org + * @author Ralf Becker + * @copyright Ralf Becker + * + * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License + * @package api + * @access public + * @version $Id$ + */ - /* $Id$ */ +/** + * The class implements a general eGW service to execute callbacks at a given time. + * + * see http://www.egroupware.org/wiki/TimedAsyncServices + */ +class asyncservice +{ + var $php = ''; + var $crontab = ''; + /** + * Our instance of the db-class + * + * @var egw_db + */ + var $db; + var $db_table = 'egw_async'; + var $debug = 0; + + /** + * constructor of the class + */ + function asyncservice() + { + if (is_object($GLOBALS['egw']->db)) + { + $this->db = clone($GLOBALS['egw']->db); + } + else + { + $this->db = clone($GLOBALS['egw_setup']->db); + } + $this->db->set_app('phpgwapi'); + + $this->cronline = EGW_SERVER_ROOT . '/phpgwapi/cron/asyncservices.php '.$GLOBALS['egw_info']['user']['domain']; + + $this->only_fallback = substr(php_uname(), 0, 7) == "Windows"; // atm cron-jobs dont work on win + } /** - * The class implements a general eGW service to execute callbacks at a given time. + * calculates the next run of the timer and puts that with the rest of the data in the db for later execution. * - * see http://www.egroupware.org/wiki/TimedAsyncServices - * - * @author Ralf Becker - * @copyright GPL - GNU General Public License + * @param int/array $times unix timestamp or array('min','hour','dow','day','month','year') with execution time. + * Repeated events are possible to shedule by setting the array only partly, eg. + * array('day' => 1) for first day in each month 0am or array('min' => '* /5', 'hour' => '9-17') + * for every 5mins in the time from 9am to 5pm. + * @param string $id unique id to cancel the request later, if necessary. Should be in a form like + * eg. 'X' where id is the internal id of app and X might indicate the action. + * @param string $method Method to be called via ExecMethod($method,$data). $method has the form + * '..'. + * @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 + * @return boolean False if $id already exists, else True */ - class asyncservice + function set_timer($times,$id,$method,$data,$account_id=False) { - var $public_functions = array( - 'set_timer' => True, - 'check_run' => True, - 'cancel_timer' => True, - 'read' => True, - 'install' => True, - 'installed' => True, - 'last_check_run' => True + if (empty($id) || empty($method) || $this->read($id) || + !($next = $this->next_run($times))) + { + return False; + } + if ($account_id === False) + { + $account_id = $GLOBALS['egw_info']['user']['account_id']; + } + $job = array( + 'id' => $id, + 'next' => $next, + 'times' => $times, + 'method' => $method, + 'data' => $data, + 'account_id' => $account_id ); - var $php = ''; - var $crontab = ''; - var $db; - var $db_table = 'egw_async'; - var $debug = 0; + $this->write($job); - /** - * constructor of the class - */ - function asyncservice() + return True; + } + + /** + * calculates the next execution time for $times + * + * @param int/array $times unix timestamp or array('year'=>$year,'month'=>$month,'dow'=>$dow,'day'=>$day,'hour'=>$hour,'min'=>$min) + * with execution time. Repeated execution is possible to shedule by setting the array only partly, + * eg. array('day' => 1) for first day in each month 0am or array('min' => '/5', 'hour' => '9-17') + * for every 5mins in the time from 9am to 5pm. All not set units before the smallest one set, + * are taken into account as every possible value, all after as the smallest possible value. + * @param boolean $debug if True some debug-messages about syntax-errors in $times are echoed + * @return int a unix timestamp of the next execution time or False if no more executions + */ + function next_run($times,$debug=False) + { + if ($this->debug) { - $this->db = is_object($GLOBALS['egw']->db) ? $GLOBALS['egw']->db : $GLOBALS['phpgw_setup']->db; - $this->db->set_app('phpgwapi'); - - $this->cronline = EGW_SERVER_ROOT . '/phpgwapi/cron/asyncservices.php '.$GLOBALS['egw_info']['user']['domain']; - - $this->only_fallback = substr(php_uname(), 0, 7) == "Windows"; // atm cron-jobs dont work on win + echo "

next_run("; print_r($times); echo ",'$debug')

\n"; + $debug = True; // enable syntax-error messages too } - - /** - * calculates the next run of the timer and puts that with the rest of the data in the db for later execution. - * - * @param int/array $times unix timestamp or array('min','hour','dow','day','month','year') with execution time. - * Repeated events are possible to shedule by setting the array only partly, eg. - * array('day' => 1) for first day in each month 0am or array('min' => '* /5', 'hour' => '9-17') - * for every 5mins in the time from 9am to 5pm. - * @param string $id unique id to cancel the request later, if necessary. Should be in a form like - * eg. 'X' where id is the internal id of app and X might indicate the action. - * @param string $method Method to be called via ExecMethod($method,$data). $method has the form - * '..'. - * @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 - * @return boolean False if $id already exists, else True - */ - function set_timer($times,$id,$method,$data,$account_id=False) + $now = time(); + + // $times is unix timestamp => if it's not expired return it, else False + // + if (!is_array($times)) { - if (empty($id) || empty($method) || $this->read($id) || - !($next = $this->next_run($times))) - { - return False; - } - if ($account_id === False) - { - $account_id = $GLOBALS['egw_info']['user']['account_id']; - } - $job = array( - 'id' => $id, - 'next' => $next, - 'times' => $times, - 'method' => $method, - 'data' => $data, - 'account_id' => $account_id - ); - $this->write($job); - - return True; + $next = (int)$times; + + return $next > $now ? $next : False; } + // If an array is given, we have to enumerate the possible times first + // + $units = array( + 'year' => 'Y', + 'month' => 'm', + 'day' => 'd', + 'dow' => 'w', + 'hour' => 'H', + 'min' => 'i' + ); + $max_unit = array( + 'min' => 59, + 'hour' => 23, + 'dow' => 6, + 'day' => 31, + 'month' => 12, + 'year' => date('Y')+10 // else */[0-9] would never stop returning numbers + ); + $min_unit = array( + 'min' => 0, + 'hour' => 0, + 'dow' => 0, + 'day' => 1, + 'month' => 1, + 'year' => date('Y') + ); - /** - * calculates the next execution time for $times - * - * @param int/array $times unix timestamp or array('year'=>$year,'month'=>$month,'dow'=>$dow,'day'=>$day,'hour'=>$hour,'min'=>$min) - * with execution time. Repeated execution is possible to shedule by setting the array only partly, - * eg. array('day' => 1) for first day in each month 0am or array('min' => '/5', 'hour' => '9-17') - * for every 5mins in the time from 9am to 5pm. All not set units before the smallest one set, - * are taken into account as every possible value, all after as the smallest possible value. - * @param boolean $debug if True some debug-messages about syntax-errors in $times are echoed - * @return int a unix timestamp of the next execution time or False if no more executions - */ - function next_run($times,$debug=False) + // get the number of the first and last pattern set in $times, + // as empty patterns get enumerated before the the last pattern and + // get set to the minimum after + // + $n = $first_set = $last_set = 0; + foreach($units as $u => $date_pattern) { - if ($this->debug) + ++$n; + if (isset($times[$u])) { - echo "

next_run("; print_r($times); echo ",'$debug')

\n"; - $debug = True; // enable syntax-error messages too - } - $now = time(); - - // $times is unix timestamp => if it's not expired return it, else False - // - if (!is_array($times)) - { - $next = (int)$times; + $last_set = $n; - return $next > $now ? $next : False; - } - // If an array is given, we have to enumerate the possible times first - // - $units = array( - 'year' => 'Y', - 'month' => 'm', - 'day' => 'd', - 'dow' => 'w', - 'hour' => 'H', - 'min' => 'i' - ); - $max_unit = array( - 'min' => 59, - 'hour' => 23, - 'dow' => 6, - 'day' => 31, - 'month' => 12, - 'year' => date('Y')+10 // else */[0-9] would never stop returning numbers - ); - $min_unit = array( - 'min' => 0, - 'hour' => 0, - 'dow' => 0, - 'day' => 1, - 'month' => 1, - 'year' => date('Y') - ); - - // get the number of the first and last pattern set in $times, - // as empty patterns get enumerated before the the last pattern and - // get set to the minimum after - // - $n = $first_set = $last_set = 0; - foreach($units as $u => $date_pattern) - { - ++$n; - if (isset($times[$u])) + if (!$first_set) { - $last_set = $n; - - if (!$first_set) - { - $first_set = $n; - } + $first_set = $n; } } + } - // now we go through all units and enumerate all patterns and not set patterns - // (as descript above), enumerations are arrays with unit-values as keys - // - $n = 0; - foreach($units as $u => $date_pattern) + // now we go through all units and enumerate all patterns and not set patterns + // (as descript above), enumerations are arrays with unit-values as keys + // + $n = 0; + foreach($units as $u => $date_pattern) + { + ++$n; + if ($this->debug) { echo "

n=$n, $u: isset(times[$u]="; print_r($times[$u]); echo ")=".(isset($times[$u])?'True':'False')."

\n"; } + if (isset($times[$u])) { - ++$n; - if ($this->debug) { echo "

n=$n, $u: isset(times[$u]="; print_r($times[$u]); echo ")=".(isset($times[$u])?'True':'False')."

\n"; } - if (isset($times[$u])) + $time = explode(',',$times[$u]); + + $times[$u] = array(); + + foreach($time as $t) { - $time = explode(',',$times[$u]); - - $times[$u] = array(); - - foreach($time as $t) + if (strpos($t,'-') !== False && strpos($t,'/') === False) { - if (strpos($t,'-') !== False && strpos($t,'/') === False) + list($min,$max) = $arr = explode('-',$t); + + if (count($arr) != 2 || !is_numeric($min) || !is_numeric($max) || $min > $max) { - list($min,$max) = $arr = explode('-',$t); - - if (count($arr) != 2 || !is_numeric($min) || !is_numeric($max) || $min > $max) - { - if ($debug) echo "

Syntax error in $u='$t', allowed is 'min-max', min <= max, min='$min', max='$max'

\n"; + if ($debug) echo "

Syntax error in $u='$t', allowed is 'min-max', min <= max, min='$min', max='$max'

\n"; + return False; + } + for ($i = (int)$min; $i <= $max; ++$i) + { + $times[$u][$i] = True; + } + } + else + { + if ($t == '*') $t = '*/1'; + + list($one,$inc) = $arr = explode('/',$t); + + if (!(is_numeric($one) && count($arr) == 1 || + count($arr) == 2 && is_numeric($inc))) + { + if ($debug) echo "

Syntax error in $u='$t', allowed is a number or '{*|range}/inc', inc='$inc'

\n"; + + return False; + } + if (count($arr) == 1) + { + $times[$u][(int)$one] = True; + } + else + { + list($min,$max) = $arr = explode('-',$one); + if (empty($one) || $one == '*') + { + $min = $min_unit[$u]; + $max = $max_unit[$u]; + } + elseif (count($arr) != 2 || $min > $max) + { + if ($debug) echo "

Syntax error in $u='$t', allowed is '{*|min-max}/inc', min='$min',max='$max', inc='$inc'

\n"; return False; } - for ($i = (int)$min; $i <= $max; ++$i) + for ($i = $min; $i <= $max; $i += $inc) { $times[$u][$i] = True; } } - else - { - if ($t == '*') $t = '*/1'; - - list($one,$inc) = $arr = explode('/',$t); - - if (!(is_numeric($one) && count($arr) == 1 || - count($arr) == 2 && is_numeric($inc))) - { - if ($debug) echo "

Syntax error in $u='$t', allowed is a number or '{*|range}/inc', inc='$inc'

\n"; - - return False; - } - if (count($arr) == 1) - { - $times[$u][(int)$one] = True; - } - else - { - list($min,$max) = $arr = explode('-',$one); - if (empty($one) || $one == '*') - { - $min = $min_unit[$u]; - $max = $max_unit[$u]; - } - elseif (count($arr) != 2 || $min > $max) - { - if ($debug) echo "

Syntax error in $u='$t', allowed is '{*|min-max}/inc', min='$min',max='$max', inc='$inc'

\n"; - return False; - } - for ($i = $min; $i <= $max; $i += $inc) - { - $times[$u][$i] = True; - } - } - } } } - elseif ($n < $last_set || $u == 'dow') // before last value set (or dow) => empty gets enumerated - { - for ($i = $min_unit[$u]; $i <= $max_unit[$u]; ++$i) - { - $times[$u][$i] = True; - } - } - else // => after last value set => empty is min-value - { - $times[$u][$min_unit[$u]] = True; - } } - if ($this->debug) { echo "enumerated times=
"; print_r($times); echo "
\n"; } - - // now we have the times enumerated, lets find the first not expired one - // - $found = array(); - while (!isset($found['min'])) + elseif ($n < $last_set || $u == 'dow') // before last value set (or dow) => empty gets enumerated { - $future = False; - - foreach($units as $u => $date_pattern) + for ($i = $min_unit[$u]; $i <= $max_unit[$u]; ++$i) { - $unit_now = $u != 'dow' ? (int)date($date_pattern) : - (int)date($date_pattern,mktime(12,0,0,$found['month'],$found['day'],$found['year'])); + $times[$u][$i] = True; + } + } + else // => after last value set => empty is min-value + { + $times[$u][$min_unit[$u]] = True; + } + } + if ($this->debug) { echo "enumerated times=
"; print_r($times); echo "
\n"; } + + // now we have the times enumerated, lets find the first not expired one + // + $found = array(); + while (!isset($found['min'])) + { + $future = False; - if (isset($found[$u])) + foreach($units as $u => $date_pattern) + { + $unit_now = $u != 'dow' ? (int)date($date_pattern) : + (int)date($date_pattern,mktime(12,0,0,$found['month'],$found['day'],$found['year'])); + + if (isset($found[$u])) + { + $future = $future || $found[$u] > $unit_now; + if ($this->debug) echo "--> already have a $u = ".$found[$u].", future='$future'
\n"; + continue; // already set + } + foreach($times[$u] as $unit_value => $nul) + { + switch($u) { - $future = $future || $found[$u] > $unit_now; - if ($this->debug) echo "--> already have a $u = ".$found[$u].", future='$future'
\n"; - continue; // already set - } - foreach($times[$u] as $unit_value => $nul) - { - switch($u) - { - case 'dow': - $valid = $unit_value == $unit_now; - break; - case 'min': - $valid = $future || $unit_value > $unit_now; - break; - default: - $valid = $future || $unit_value >= $unit_now; - break; - - } - if ($valid && ($u != $next || $unit_value > $over)) // valid and not over - { - $found[$u] = $unit_value; - $future = $future || $unit_value > $unit_now; + case 'dow': + $valid = $unit_value == $unit_now; break; - } + case 'min': + $valid = $future || $unit_value > $unit_now; + break; + default: + $valid = $future || $unit_value >= $unit_now; + break; + } - if (!isset($found[$u])) // we have to try the next one, if it exists + if ($valid && ($u != $next || $unit_value > $over)) // valid and not over { - $next = array_keys($units); - if (!isset($next[count($found)-1])) - { - if ($this->debug) echo "

Nothing found, exiting !!!

\n"; - return False; - } - $next = $next[count($found)-1]; - $over = $found[$next]; - unset($found[$next]); - if ($this->debug) echo "

Have to try the next $next, $u's are over for $next=$over !!!

\n"; + $found[$u] = $unit_value; + $future = $future || $unit_value > $unit_now; break; } } - } - if ($this->debug) { echo "

next="; print_r($found); echo "

\n"; } - - return mktime($found['hour'],$found['min'],0,$found['month'],$found['day'],$found['year']); - } - - /** - * cancels a timer - * - * @param string $id has to be the one used to set it. - * @return boolean True if the timer exists and is not expired. - */ - function cancel_timer($id) - { - return $this->delete($id); - } - - /** - * checks when the last check_run was run or set the run-semaphore if $semaphore == True - * - * @param boolean $semaphore if False only check, if true try to set/release the semaphore - * @param boolean $release if $semaphore == True, tells if we should set or release the semaphore - * @return mixed if !$set array('start' => $start,'end' => $end) with timestamps of last check_run start and end, \ - * !$end means check_run is just running. If $set returns True if it was able to get the semaphore, else False - */ - function last_check_run($semaphore=False,$release=False,$run_by='') - { - //echo "

last_check_run(semaphore=".($semaphore?'True':'False').",release=".($release?'True':'False').")

\n"; - if ($semaphore) - { - $this->db->lock($this->db_table,'write'); // this will block til we get exclusive access to the table - - @set_time_limit(0); // dont stop for an execution-time-limit - ignore_user_abort(True); - } - if ($exists = $this->read('##last-check-run##')) - { - list(,$last_run) = each($exists); - } - //echo "last_run (from db)=
"; print_r($last_run); echo "
\n"; - - if (!$semaphore) - { - return $last_run['data']; - } - elseif (!$release && !$last_run['data']['end'] && $last_run['data']['start'] > time()-600) - { - // already one instance running (started not more then 10min ago, else we ignore it) - - $this->db->unlock(); // unlock the table again - - //echo "

An other instance is running !!!

\n"; - return False; - } - // no other instance runs ==> we should run - // - if ($release) - { - $last_run['data']['end'] = time(); - } - else - { - $last_run = array( - 'id' => '##last-check-run##', - 'next' => 0, - 'times' => array(), - 'method' => 'none', - 'data' => array( - 'run_by'=> $run_by, - 'start' => time(), - 'end' => 0 - ) - ); - } - //echo "last_run=
"; print_r($last_run); echo "
\n"; - $this->write($last_run,!!$exits); - - $this->db->unlock(); - - return True; - } - - /** - * checks if there are any jobs ready to run (timer expired) and executes them - */ - function check_run($run_by='') - { - flush(); - - if (!$this->last_check_run(True,False,$run_by)) - { - return False; // cant obtain semaphore - } - if ($jobs = $this->read()) - { - foreach($jobs as $id => $job) + if (!isset($found[$u])) // we have to try the next one, if it exists { - // checking / setting up phpgw_info/user - // - if ($GLOBALS['egw_info']['user']['account_id'] != $job['account_id']) + $next = array_keys($units); + if (!isset($next[count($found)-1])) { - $domain = $GLOBALS['egw_info']['user']['domain']; - $lang = $GLOBALS['egw_info']['user']['preferences']['common']['lang']; - unset($GLOBALS['egw_info']['user']); - - if ($GLOBALS['egw']->session->account_id = $job['account_id']) - { - $GLOBALS['egw']->session->account_lid = $GLOBALS['egw']->accounts->id2name($job['account_id']); - $GLOBALS['egw']->session->account_domain = $domain; - $GLOBALS['egw']->session->read_repositories(False,False); - $GLOBALS['egw_info']['user'] = $GLOBALS['egw']->session->user; - - if ($lang != $GLOBALS['egw_info']['user']['preferences']['common']['lang']) - { - unset($GLOBALS['lang']); - $GLOBALS['egw']->translation->add_app('common'); - } - } - else - { - $GLOBALS['egw_info']['user']['domain'] = $domain; - } - } - list($app) = explode('.',$job['method']); - $GLOBALS['egw']->translation->add_app($app); - - ExecMethod($job['method'],$job['data']); - - // re-read job, in case it had been updated or even deleted in the method - $updated = $this->read($id); - if ($updated && isset($updated[$id])) - { - $job = $updated[$id]; - - if ($job['next'] = $this->next_run($job['times'])) - { - $this->write($job,True); - } - else // no further runs - { - $this->delete($job['id']); - } + if ($this->debug) echo "

Nothing found, exiting !!!

\n"; + return False; } + $next = $next[count($found)-1]; + $over = $found[$next]; + unset($found[$next]); + if ($this->debug) echo "

Have to try the next $next, $u's are over for $next=$over !!!

\n"; + break; } } - $this->last_check_run(True,True,$run_by); // release semaphore - - return $jobs ? count($jobs) : False; } + if ($this->debug) { echo "

next="; print_r($found); echo "

\n"; } - /** - * reads all matching db-rows / jobs - * - * @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) - * @return array/boolean db-rows / jobs as array or False if no matches - */ - function read($id=0) + return mktime($found['hour'],$found['min'],0,$found['month'],$found['day'],$found['year']); + } + + /** + * cancels a timer + * + * @param string $id has to be the one used to set it. + * @return boolean True if the timer exists and is not expired. + */ + function cancel_timer($id) + { + return $this->delete($id); + } + + /** + * checks when the last check_run was run or set the run-semaphore if $semaphore == True + * + * @param boolean $semaphore if False only check, if true try to set/release the semaphore + * @param boolean $release if $semaphore == True, tells if we should set or release the semaphore + * @return mixed if !$set array('start' => $start,'end' => $end) with timestamps of last check_run start and end, \ + * !$end means check_run is just running. If $set returns True if it was able to get the semaphore, else False + */ + function last_check_run($semaphore=False,$release=False,$run_by='') + { + //echo "

last_check_run(semaphore=".($semaphore?'True':'False').",release=".($release?'True':'False').")

\n"; + if ($semaphore) { - if (!is_array($id) && (strpos($id,'%') !== False || strpos($id,'_') !== False)) - { - $id = $this->db->quote($id); - $where = "async_id LIKE $id AND async_id != '##last-check-run##'"; - } - elseif (!$id) - { - $where = 'async_next <= '.time()." AND async_id != '##last-check-run##'"; - } - else - { - $where = array('async_id' => $id); - } - $this->db->select($this->db_table,'*',$where,__LINE__,__FILE__); + $this->db->lock($this->db_table,'write'); // this will block til we get exclusive access to the table - $jobs = array(); - while ($this->db->next_record()) - { - $id = $this->db->f('async_id'); - - $jobs[$id] = array( - 'id' => $id, - 'next' => $this->db->f('async_next'), - 'times' => unserialize($this->db->f('async_times')), - 'method' => $this->db->f('async_method'), - 'data' => unserialize($this->db->f('async_data')), - 'account_id' => $this->db->f('async_account_id') - ); - //echo "job id='$id'
"; print_r($jobs[$id]); echo "
\n"; - } - if (!count($jobs)) - { - return False; - } - return $jobs; + @set_time_limit(0); // dont stop for an execution-time-limit + ignore_user_abort(True); } - - /** - * write a job / db-row to the db - * - * @param array $job db-row as array - * @param boolean $exits if True, we do an update, else we check if update or insert necesary - */ - function write($job,$exists = False) + if ($exists = $this->read('##last-check-run##')) { - $data = array( - 'async_next' => $job['next'], - 'async_times' => serialize($job['times']), - 'async_method' => $job['method'], - 'async_data' => serialize($job['data']), - 'async_account_id'=> $job['account_id'], + list(,$last_run) = each($exists); + } + //echo "last_run (from db)=
"; print_r($last_run); echo "
\n"; + + if (!$semaphore) + { + return $last_run['data']; + } + elseif (!$release && !$last_run['data']['end'] && $last_run['data']['start'] > time()-600) + { + // already one instance running (started not more then 10min ago, else we ignore it) + + $this->db->unlock(); // unlock the table again + + //echo "

An other instance is running !!!

\n"; + return False; + } + // no other instance runs ==> we should run + // + if ($release) + { + $last_run['data']['end'] = time(); + } + else + { + $last_run = array( + 'id' => '##last-check-run##', + 'next' => 0, + 'times' => array(), + 'method' => 'none', + 'data' => array( + 'run_by'=> $run_by, + 'start' => time(), + 'end' => 0 + ) ); - if ($exists) - { - $this->db->update($this->db_table,$data,array('async_id' => $job['id']),__LINE__,__FILE__); - } - else - { - $this->db->insert($this->db_table,$data,array('async_id' => $job['id']),__LINE__,__FILE__); - } } + //echo "last_run=
"; print_r($last_run); echo "
\n"; + $this->write($last_run,!!$exits); + + $this->db->unlock(); - /** - * delete db-row / job with $id - * - * @return boolean False if $id not found else True - */ - function delete($id) + return True; + } + + /** + * checks if there are any jobs ready to run (timer expired) and executes them + */ + function check_run($run_by='') + { + flush(); + + if (!$this->last_check_run(True,False,$run_by)) { - $this->db->delete($this->db_table,array('async_id' => $id),__LINE__,__FILE__); - - return $this->db->affected_rows(); + return False; // cant obtain semaphore } - - function find_binarys() + if ($jobs = $this->read()) { - static $run = False; - if ($run) + foreach($jobs as $id => $job) { - return; - } - $run = True; + // checking / setting up egw_info/user + // + if ($GLOBALS['egw_info']['user']['account_id'] != $job['account_id']) + { + $domain = $GLOBALS['egw_info']['user']['domain']; + $lang = $GLOBALS['egw_info']['user']['preferences']['common']['lang']; + unset($GLOBALS['egw_info']['user']); - if (substr(php_uname(), 0, 7) == "Windows") - { - // ToDo: find php-cgi on windows - } - else - { - $binarys = array( - 'php' => '/usr/bin/php', - 'php4' => '/usr/bin/php4', // this is for debian - 'php5' => '/usr/bin/php5', // SuSE 9.3 with php5 - 'crontab' => '/usr/bin/crontab' - ); - foreach ($binarys as $name => $path) - { - $this->$name = $path; // a reasonable default for *nix - - if (!($Ok = @is_executable($this->$name))) + if ($GLOBALS['egw']->session->account_id = $job['account_id']) { - if (file_exists($this->$name)) + $GLOBALS['egw']->session->account_lid = $GLOBALS['egw']->accounts->id2name($job['account_id']); + $GLOBALS['egw']->session->account_domain = $domain; + $GLOBALS['egw']->session->read_repositories(False,False); + $GLOBALS['egw_info']['user'] = $GLOBALS['egw']->session->user; + + if ($lang != $GLOBALS['egw_info']['user']['preferences']['common']['lang']) { - echo '

'.lang('%1 is not executable by the webserver !!!',$this->$name)."

\n"; - $perms = fileperms($this->$name); - if (!($perms & 0x0001) && ($perms & 0x0008)) // only executable by group - { - $group = posix_getgrgid(filegroup($this->$name)); - $webserver = posix_getpwuid(posix_getuid ()); - echo '

'.lang("You need to add the webserver user '%1' to the group '%2'.",$webserver['name'],$group['name'])."

\n"; } + unset($GLOBALS['lang']); + $GLOBALS['egw']->translation->add_app('common'); } - if ($fd = popen('/bin/sh -c "type -p '.$name.'"','r')) - { - $this->$name = fgets($fd,256); - @pclose($fd); - } - if ($pos = strpos($this->$name,"\n")) - { - $this->$name = substr($this->$name,0,$pos); - } - } - if (!$Ok && !@is_executable($this->$name)) - { - $this->$name = $name; // hopefully its in the path - } - //echo "

$name = '".$this->$name."'

\n"; - } - if ($this->php4[0] == '/') // we found a php4 binary - { - $this->php = $this->php4; - } - if ($this->php5[0] == '/') // we found a php5 binary - { - $this->php = $this->php5; - } - } - - } - - /** - * checks if phpgwapi/cron/asyncservices.php is installed as cron-job - * - * @return array the times asyncservices are run (normaly 'min'=>'* /5') or False if not installed or 0 if crontab not found - * Not implemented for Windows at the moment, always returns 0 - */ - function installed() - { - if ($this->only_fallback) { - return 0; - } - $this->find_binarys(); - - if (!is_executable($this->crontab)) - { - //echo "

Error: $this->crontab not found !!!

"; - return 0; - } - $times = False; - $this->other_cronlines = array(); - if (($crontab = popen('/bin/sh -c "'.$this->crontab.' -l" 2>&1','r')) !== False) - { - while ($line = fgets($crontab,256)) - { - if ($this->debug) echo 'line '.++$n.": $line
\n"; - $parts = split(' ',$line,6); - - if ($line[0] == '#' || count($parts) < 6 || ($parts[5][0] != '/' && substr($parts[5],0,3) != 'php')) - { - // ignore comments - if ($line[0] != '#') - { - $times['error'] .= $line; - } - } - elseif (strpos($line,$this->cronline) !== False) - { - $cron_units = array('min','hour','day','month','dow'); - foreach($cron_units as $n => $u) - { - $times[$u] = $parts[$n]; - } - $times['cronline'] = $line; } else { - $this->other_cronlines[] = $line; + $GLOBALS['egw_info']['user']['domain'] = $domain; + } + } + list($app) = explode('.',$job['method']); + $GLOBALS['egw']->translation->add_app($app); + ExecMethod($job['method'],$job['data']); + + // re-read job, in case it had been updated or even deleted in the method + $updated = $this->read($id); + if ($updated && isset($updated[$id])) + { + $job = $updated[$id]; + + if ($job['next'] = $this->next_run($job['times'])) + { + $this->write($job,True); + } + else // no further runs + { + $this->delete($job['id']); } } - @pclose($crontab); } - return $times; } - - /** - * installs /phpgwapi/cron/asyncservices.php as cron-job - * - * Not implemented for Windows at the moment, always returns 0 - * - * @param array $times array with keys 'min','hour','day','month','dow', not set is equal to '*'. - * False means de-install our own crontab line - * @return mixed the times asyncservices are run, False if they are not installed, - * 0 if crontab not found and ' ' if crontab is deinstalled - */ - function install($times) - { - if ($this->only_fallback && $times !== False) { - return 0; - } - $this->installed(); // find other installed cronlines + $this->last_check_run(True,True,$run_by); // release semaphore - if (($crontab = popen('/bin/sh -c "'.$this->crontab.' -" 2>&1','w')) !== False) - { - if (is_array($this->other_cronlines)) - { - foreach ($this->other_cronlines as $cronline) - { - fwrite($crontab,$cronline); // preserv the other lines on install - } - } - if ($times !== False) - { - $cron_units = array('min','hour','day','month','dow'); - $cronline = ''; - foreach($cron_units as $cu) - { - $cronline .= (isset($times[$cu]) ? $times[$cu] : '*') . ' '; - } - $cronline .= $this->php.' -qC '.$this->cronline."\n"; - //echo "

Installing: '$cronline'

\n"; - fwrite($crontab,$cronline); - } - @pclose($crontab); - } - return $times !== False ? $this->installed() : ' '; + return $jobs ? count($jobs) : False; + } + + /** + * reads all matching db-rows / jobs + * + * @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) + * @return array/boolean db-rows / jobs as array or False if no matches + */ + function read($id=0) + { + if (!is_array($id) && (strpos($id,'%') !== False || strpos($id,'_') !== False)) + { + $id = $this->db->quote($id); + $where = "async_id LIKE $id AND async_id != '##last-check-run##'"; + } + elseif (!$id) + { + $where = 'async_next <= '.time()." AND async_id != '##last-check-run##'"; + } + else + { + $where = array('async_id' => $id); + } + $this->db->select($this->db_table,'*',$where,__LINE__,__FILE__); + + $jobs = array(); + while ($this->db->next_record()) + { + $id = $this->db->f('async_id'); + + $jobs[$id] = array( + 'id' => $id, + 'next' => $this->db->f('async_next'), + 'times' => unserialize($this->db->f('async_times')), + 'method' => $this->db->f('async_method'), + 'data' => unserialize($this->db->f('async_data')), + 'account_id' => $this->db->f('async_account_id') + ); + //echo "job id='$id'
"; print_r($jobs[$id]); echo "
\n"; + } + if (!count($jobs)) + { + return False; + } + return $jobs; + } + + /** + * write a job / db-row to the db + * + * @param array $job db-row as array + * @param boolean $exits if True, we do an update, else we check if update or insert necesary + */ + function write($job,$exists = False) + { + $data = array( + 'async_next' => $job['next'], + 'async_times' => serialize($job['times']), + 'async_method' => $job['method'], + 'async_data' => serialize($job['data']), + 'async_account_id'=> $job['account_id'], + ); + if ($exists) + { + $this->db->update($this->db_table,$data,array('async_id' => $job['id']),__LINE__,__FILE__); + } + else + { + $this->db->insert($this->db_table,$data,array('async_id' => $job['id']),__LINE__,__FILE__); } } + + /** + * delete db-row / job with $id + * + * @return boolean False if $id not found else True + */ + function delete($id) + { + $this->db->delete($this->db_table,array('async_id' => $id),__LINE__,__FILE__); + + return $this->db->affected_rows(); + } + + function find_binarys() + { + static $run = False; + if ($run) + { + return; + } + $run = True; + + if (substr(php_uname(), 0, 7) == "Windows") + { + // ToDo: find php-cgi on windows + } + else + { + $binarys = array( + 'php' => '/usr/bin/php', + 'php4' => '/usr/bin/php4', // this is for debian + 'php5' => '/usr/bin/php5', // SuSE 9.3 with php5 + 'crontab' => '/usr/bin/crontab' + ); + foreach ($binarys as $name => $path) + { + $this->$name = $path; // a reasonable default for *nix + + if (!($Ok = @is_executable($this->$name))) + { + if (file_exists($this->$name)) + { + echo '

'.lang('%1 is not executable by the webserver !!!',$this->$name)."

\n"; + $perms = fileperms($this->$name); + if (!($perms & 0x0001) && ($perms & 0x0008)) // only executable by group + { + $group = posix_getgrgid(filegroup($this->$name)); + $webserver = posix_getpwuid(posix_getuid ()); + echo '

'.lang("You need to add the webserver user '%1' to the group '%2'.",$webserver['name'],$group['name'])."

\n"; } + } + if ($fd = popen('/bin/sh -c "type -p '.$name.'"','r')) + { + $this->$name = fgets($fd,256); + @pclose($fd); + } + if ($pos = strpos($this->$name,"\n")) + { + $this->$name = substr($this->$name,0,$pos); + } + } + if (!$Ok && !@is_executable($this->$name)) + { + $this->$name = $name; // hopefully its in the path + } + //echo "

$name = '".$this->$name."'

\n"; + } + if ($this->php4{0} == '/') // we found a php4 binary + { + $this->php = $this->php4; + } + if ($this->php5{0} == '/') // we found a php5 binary + { + $this->php = $this->php5; + } + } + } + + /** + * checks if phpgwapi/cron/asyncservices.php is installed as cron-job + * + * @return array the times asyncservices are run (normaly 'min'=>'* /5') or False if not installed or 0 if crontab not found + * Not implemented for Windows at the moment, always returns 0 + */ + function installed() + { + if ($this->only_fallback) { + return 0; + } + $this->find_binarys(); + + if (!is_executable($this->crontab)) + { + //echo "

Error: $this->crontab not found !!!

"; + return 0; + } + $times = False; + $this->other_cronlines = array(); + if (($crontab = popen('/bin/sh -c "'.$this->crontab.' -l" 2>&1','r')) !== False) + { + while ($line = fgets($crontab,256)) + { + if ($this->debug) echo 'line '.++$n.": $line
\n"; + $parts = split(' ',$line,6); + + if ($line{0} == '#' || count($parts) < 6 || ($parts[5]{0} != '/' && substr($parts[5],0,3) != 'php')) + { + // ignore comments + if ($line{0} != '#') + { + $times['error'] .= $line; + } + } + elseif (strpos($line,$this->cronline) !== False) + { + $cron_units = array('min','hour','day','month','dow'); + foreach($cron_units as $n => $u) + { + $times[$u] = $parts[$n]; + } + $times['cronline'] = $line; + } + else + { + $this->other_cronlines[] = $line; + } + } + @pclose($crontab); + } + return $times; + } + + /** + * installs /phpgwapi/cron/asyncservices.php as cron-job + * + * Not implemented for Windows at the moment, always returns 0 + * + * @param array $times array with keys 'min','hour','day','month','dow', not set is equal to '*'. + * False means de-install our own crontab line + * @return mixed the times asyncservices are run, False if they are not installed, + * 0 if crontab not found and ' ' if crontab is deinstalled + */ + function install($times) + { + if ($this->only_fallback && $times !== False) { + return 0; + } + $this->installed(); // find other installed cronlines + + if (($crontab = popen('/bin/sh -c "'.$this->crontab.' -" 2>&1','w')) !== False) + { + if (is_array($this->other_cronlines)) + { + foreach ($this->other_cronlines as $cronline) + { + fwrite($crontab,$cronline); // preserv the other lines on install + } + } + if ($times !== False) + { + $cron_units = array('min','hour','day','month','dow'); + $cronline = ''; + foreach($cron_units as $cu) + { + $cronline .= (isset($times[$cu]) ? $times[$cu] : '*') . ' '; + } + $cronline .= $this->php.' -qC '.$this->cronline."\n"; + //echo "

Installing: '$cronline'

\n"; + fwrite($crontab,$cronline); + } + @pclose($crontab); + } + return $times !== False ? $this->installed() : ' '; + } +} From fc3c7a40adf24ae5aa7fe2934245e5eb212fdcb0 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 10 Jun 2007 08:50:03 +0000 Subject: [PATCH 11/15] bugfixes and some enhancements to support async notifications (infolog) --- etemplate/inc/class.bo_tracking.inc.php | 146 +++++++++++++++++++----- 1 file changed, 119 insertions(+), 27 deletions(-) diff --git a/etemplate/inc/class.bo_tracking.inc.php b/etemplate/inc/class.bo_tracking.inc.php index a171f50ccb..82528a4594 100644 --- a/etemplate/inc/class.bo_tracking.inc.php +++ b/etemplate/inc/class.bo_tracking.inc.php @@ -11,6 +11,8 @@ * @version $Id$ */ +require_once(EGW_API_INC.'/class.html.inc.php'); + /** * Abstract base class for trackering: * - logging all modifications of an entry @@ -69,6 +71,14 @@ class bo_tracking * @var boolean */ var $prefer_user_as_sender = true; + /** + * Should the current user be email-notified (about change he made himself) + * + * Popup notifications are never send to the current user! + * + * @var boolean + */ + var $notify_current_user = false; /** * Array with error-messages if track($data,$old) returns false @@ -111,6 +121,22 @@ class bo_tracking * @var int */ var $tz_offset_s; + /** + * Reference to the html class + * + * @var html + */ + var $html; + + /** + * Constructor + * + * @return bo_tracking + */ + function bo_tracking() + { + $this->html =& html::singleton(); + } /** * Get a config value, which can depend on $data and $old @@ -195,6 +221,12 @@ class bo_tracking { $this->errors = $email_sent = array(); + if (!$this->notify_current_user) // should we notify the current user about his own changes + { + //error_log("do_notificaton() adding user=$this->user to email_sent, to not notify him"); + $email_sent[] = $GLOBALS['egw']->accounts->id2name($this->user,'account_email'); + } + // entry creator if ($this->creator_field && ($email = $GLOBALS['egw']->accounts->id2name($data[$this->creator_field],'account_email')) && !in_array($email, $email_sent)) @@ -293,8 +325,9 @@ class bo_tracking /** * Sending a notification to the given email-address + * + * Called by track() or externally for sending async notifications * - * @internal use only track($data,$old,$user) * @param array $data current entry * @param array $old=null old/last state of the entry or null for a new entry * @param string $email address to send the notification to @@ -323,8 +356,6 @@ class bo_tracking { return false; // no notification requested } - // notification via notification app. - $this->popup_notification($user_or_lang,$this->get_subject($data,$old)); } else { @@ -332,15 +363,16 @@ class bo_tracking $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->default; $GLOBALS['egw_info']['user']['preferences']['common']['lang'] = $user_or_lang; } - $this->datetime_format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat'].' '. - ($GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] != 12 ? 'H:i' : 'h:i a'); - $this->tz_offset_s = 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset']; - if ($lang != $GLOBALS['egw']->translation->userlang) // load the right language if needed { $GLOBALS['egw']->translation->init(); } - + // send popup notification + if (is_numeric($user_or_lang) && $this->user != $user_or_lang) // no popup for own actions + { + //die('sending popup notification'.$this->html->htmlspecialchars($this->get_popup_message($data,$old))); + $this->popup_notification($user_or_lang,$this->get_popup_message($data,$old)); + } // PHPMailer aka send-class, seems not to be able to send more then one mail, IF we need to authenticate to the SMTP server // There for the object is newly created for ever mail, 'til this get fixed in PHPMailer. //if(!is_object($GLOBALS['egw']->send)) @@ -403,14 +435,23 @@ class bo_tracking } /** - * Return date+time formatted for the currently notified user (send_notification) + * Return date+time formatted for the currently notified user (prefs in $GLOBALS['egw_info']['user']['preferences']) * * @param int $timestamp + * @param boolean $do_time=true true=allways (default), false=never print the time, null=print time if != 00:00 * @return string */ - function datetime($timestamp) + function datetime($timestamp,$do_time=true) { - return date($this->datetime_format,$timestamp+$this->tz_offset_s); + if (is_null($do_time)) + { + $do_time = date('H:i',$timestamp+$this->tz_offset_s) != '00:00'; + } + $format = $GLOBALS['egw_info']['user']['preferences']['common']['dateformat']; + if ($do_time) $format .= ' '.($GLOBALS['egw_info']['user']['preferences']['common']['timeformat'] != 12 ? 'H:i' : 'h:i a'); + + //error_log("bo_tracking::datetime($timestamp,$do_time)=date('$format',$timestamp+$this->tz_offset_s)='".date($format,$timestamp+$this->tz_offset_s).'\')'); + return date($format,$timestamp+3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset']); } /** @@ -484,9 +525,10 @@ class bo_tracking * * @param array $data * @param array $old - * @return string + * @param string $allow_popup=false if true return array(link,popup-size) incl. session info an evtl. partial url (no host-part) + * @return string/array string with link (!$allow_popup) or array(link,popup-size), popup size is something like '640x480' */ - function get_link($data,$old) + function get_link($data,$old,$allow_popup=false) { if (($link = $this->get_config('link',$data,$old))) { @@ -497,16 +539,22 @@ class bo_tracking } elseif (($view = $GLOBALS['egw']->link->view($this->app,$data[$this->id_field]))) { - $link = preg_replace('/(sessionid|kp3|domain)=[^&]+&?/','',$GLOBALS['egw']->link('/index.php',$view)); - - if ($link{0} == '/') - { - $link = ($_SERVER['HTTPS'] || $GLOBALS['egw_info']['server']['enforce_ssl'] ? 'https://' : 'http://'). - ($GLOBALS['egw_info']['server']['hostname'] ? $GLOBALS['egw_info']['server']['hostname'] : $_SERVER['HTTP_HOST']).$link; - } - if ($GLOBALS['egw']->link->is_popup($this->app,'view')) $link .= '&nopopup=1'; + $link = $GLOBALS['egw']->link('/index.php',$view); + $popup = $GLOBALS['egw']->link->is_popup($this->app,'view'); } - return $link; + if ($link{0} == '/') + { + $link = ($_SERVER['HTTPS'] || $GLOBALS['egw_info']['server']['enforce_ssl'] ? 'https://' : 'http://'). + ($GLOBALS['egw_info']['server']['hostname'] ? $GLOBALS['egw_info']['server']['hostname'] : $_SERVER['HTTP_HOST']).$link; + } + if (!$allow_popup) + { + // remove the session-id in the notification mail! + $link = preg_replace('/(sessionid|kp3|domain)=[^&]+&?/','',$link); + + if ($popup) $link .= '&nopopup=1'; + } + return $allow_popup ? array($link,$popup) : $link; } /** @@ -535,7 +583,9 @@ class bo_tracking } foreach($this->get_details($data) as $name => $detail) { - $modified = $old && $data[$name] != $old[$name]; + // if there's no old entry, the entry is not modified by definition + // if both values are '', 0 or null, we count them as equal too + $modified = $old && $data[$name] != $old[$name] && !(!$data[$name] && !$old[$name]); //if ($modified) error_log("data[$name]='{$data[$name]}', old[$name]='{$old[$name]}' --> modified=".(int)$modified); if (empty($detail['value']) && !$modified) continue; // skip unchanged, empty values @@ -566,6 +616,8 @@ class bo_tracking if ($html_mail) { + $line = $this->html->htmlspecialchars($line); // XSS + $color = $modified ? 'red' : false; $size = 'small'; $bold = false; @@ -599,16 +651,23 @@ class bo_tracking else // text-mail { if ($type == 'reply') $content = str_repeat('-',64)."\n"; + if ($modified) $content .= '> '; } $content .= $line; - + if ($link) { $content .= ' '; - if ($html_mail) $content .= ''; - $content .= $link; - if ($html_mail) $content .= ''; + + if ($html_mail) + { + $content .= $this->html->a_href($link,$link,'','target="_blank"'); + } + else + { + $content .= $link; + } } if ($html_mail) $content .= ''; @@ -628,4 +687,37 @@ class bo_tracking { return array(); } + + /** + * Get the message for the popup + * + * Default implementation uses get_subject() with get_link() and get_message() as an extra line + * + * @param array $data + * @param array $old + * @return string + */ + function get_popup_message($data,$old) + { + $message = $this->html->htmlspecialchars($this->get_subject($data,$old)); + + if ((list($link,$popup) = $this->get_link($data,$old,true))) + { + if ($popup) + { + list($width,$height) = explode('x',$popup); + $options = 'onclick="egw_openWindowCentered2(this.href,\'_blank\', \''.$width.'\', \''.$height.'\', \'yes\'); return false;"'; + } + else + { + $options = 'target="_blank"'; + } + $message = $this->html->a_href($this->html->htmlspecialchars($message),$link,'',$options); + } + if (($extra = $this->get_message($data,$old))) + { + $message .= '
'.$this->html->htmlspecialchars($extra); + } + return $message; + } } \ No newline at end of file From 4b72cd5bb0fd0e631304057be8c5b0dfbc297588 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 10 Jun 2007 08:51:42 +0000 Subject: [PATCH 12/15] modification because of changes in bo_tracking (calling the constructor now neccessary) --- addressbook/inc/class.addressbook_tracking.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addressbook/inc/class.addressbook_tracking.inc.php b/addressbook/inc/class.addressbook_tracking.inc.php index 09ba1b0083..cc8aa71319 100644 --- a/addressbook/inc/class.addressbook_tracking.inc.php +++ b/addressbook/inc/class.addressbook_tracking.inc.php @@ -70,6 +70,8 @@ class addressbook_tracking extends bo_tracking */ function addressbook_tracking(&$bocontacts) { + $this->bo_tracking(); // calling the constructor of the extended class + $this->contacts =& $bocontacts; } From 2d0eac55b7533ef87fa606218b42c20d8379a8ca Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 10 Jun 2007 09:21:04 +0000 Subject: [PATCH 13/15] InfoLog notifcations for due or to start entries (You need to run Admin>>Register hooks!) --- .../class.admin_prefs_sidebox_hooks.inc.php | 24 +++ infolog/inc/class.boinfolog.inc.php | 81 +++++++++ infolog/inc/class.infolog_tracking.inc.php | 8 +- infolog/inc/class.soinfolog.inc.php | 156 ++++++++++++------ infolog/inc/class.uiinfolog.inc.php | 48 ++++-- infolog/inc/hook_settings.inc.php | 53 +++++- infolog/setup/phpgw_de.lang | 29 +++- infolog/setup/phpgw_en.lang | 26 ++- infolog/setup/setup.inc.php | 3 +- 9 files changed, 357 insertions(+), 71 deletions(-) diff --git a/infolog/inc/class.admin_prefs_sidebox_hooks.inc.php b/infolog/inc/class.admin_prefs_sidebox_hooks.inc.php index fd164bba83..7f007d557b 100644 --- a/infolog/inc/class.admin_prefs_sidebox_hooks.inc.php +++ b/infolog/inc/class.admin_prefs_sidebox_hooks.inc.php @@ -76,4 +76,28 @@ class admin_prefs_sidebox_hooks } } } + + /** + * Verification hook called if settings / preferences get stored + * + * Installs a task to send async infolog notifications at 2h everyday + * + * @param array $data + */ + function verify_settings($data) + { + if ($data['prefs']['notify_due_delegated'] || $data['prefs']['notify_due_responsible'] || + $data['prefs']['notify_start_delegated'] || $data['prefs']['notify_start_responsible']) + { + require_once(EGW_API_INC.'/class.asyncservice.inc.php'); + + $async =& new asyncservice(); + //$async->cancel_timer('infolog-async-notification'); + + if (!$async->read('infolog-async-notification')) + { + $async->set_timer(array('hour' => 2),'infolog-async-notification','infolog.boinfolog.async_notification',null); + } + } + } } diff --git a/infolog/inc/class.boinfolog.inc.php b/infolog/inc/class.boinfolog.inc.php index 2553a58ae9..d2133be2ed 100644 --- a/infolog/inc/class.boinfolog.inc.php +++ b/infolog/inc/class.boinfolog.inc.php @@ -1164,4 +1164,85 @@ class boinfolog } return $icons; } + + /** + * Send all async infolog notification + * + * Called via the async service job 'infolog-async-notification' + */ + function async_notification() + { + if (!($users = $this->so->users_with_open_entries())) + { + return; + } + error_log("boinfolog::async_notification() users with open entries: ".implode(', ',$users)); + + $save_account_id = $GLOBALS['egw_info']['user']['account_id']; + $save_prefs = $GLOBALS['egw_info']['user']['preferences']; + foreach($users as $user) + { + if (!($email = $GLOBALS['egw']->accounts->id2name($user,'account_email'))) continue; + // create the enviroment for $user + $this->user = $GLOBALS['egw_info']['user']['account_id'] = $user; + $GLOBALS['egw']->preferences->preferences($user); + $GLOBALS['egw_info']['user']['preferences'] = $GLOBALS['egw']->preferences->read_repository(); + $GLOBALS['egw']->acl->acl($user); + $GLOBALS['egw']->acl->read_repository(); + $this->grants = $GLOBALS['egw']->acl->get_grants('infolog',$this->group_owners ? $this->group_owners : true); + $this->so =& new soinfolog($this->grants); // so caches it's filters + + $notified_info_ids = array(); + foreach(array( + 'notify_due_responsible' => 'open-responsible-enddate', + 'notify_due_delegated' => 'open-delegated-enddate', + 'notify_start_responsible' => 'open-responsible-date', + 'notify_start_delegated' => 'open-delegated-date', + ) as $pref => $filter) + { + if (!($pref_value = $GLOBALS['egw_info']['user']['preferences']['infolog'][$pref])) continue; + + $filter .= date('Y-m-d',time()+24*60*60*(int)$pref_value); + error_log("boinfolog::async_notification() checking with filter '$filter' ($pref_value) for user $user ($email)"); + + $params = array('filter' => $filter); + foreach($this->so->search($params) as $info) + { + // check if we already send a notification for that infolog entry, eg. starting and due on same day + if (in_array($info['info_id'],$notified_info_ids)) continue; + + if (is_null($tracking) || $tracking->user != $user) + { + require_once(EGW_INCLUDE_ROOT.'/infolog/inc/class.infolog_tracking.inc.php'); + $tracking = new infolog_tracking($this); + } + switch($pref) + { + case 'notify_due_responsible': + $info['message'] = lang('%1 you are responsible for is due at %2',$this->enums['type'][$info['info_type']], + $tracking->datetime($info['info_enddate']-$this->tz_offset_s,false)); + break; + case 'notify_due_delegated': + $info['message'] = lang('%1 you delegated is due at %2',$this->enums['type'][$info['info_type']], + $tracking->datetime($info['info_enddate']-$this->tz_offset_s,false)); + break; + case 'notify_start_responsible': + $info['message'] = lang('%1 you are responsible for is starting at %2',$this->enums['type'][$info['info_type']], + $tracking->datetime($info['info_startdate']-$this->tz_offset_s,null)); + break; + case 'notify_start_delegated': + $info['message'] = lang('%1 you delegated is starting at %2',$this->enums['type'][$info['info_type']], + $tracking->datetime($info['info_startdate']-$this->tz_offset_s,null)); + break; + } + error_log("notifiying $user($email) about $info[info_subject]: $info[message]"); + $tracking->send_notification($info,null,$email,$user,$pref); + + $notified_info_ids[] = $info['info_id']; + } + } + } + $GLOBALS['egw_info']['user']['account_id'] = $save_account_id; + $GLOBALS['egw_info']['user']['preferences'] = $save_prefs; + } } diff --git a/infolog/inc/class.infolog_tracking.inc.php b/infolog/inc/class.infolog_tracking.inc.php index f94d3b7bc3..ed38b2b799 100644 --- a/infolog/inc/class.infolog_tracking.inc.php +++ b/infolog/inc/class.infolog_tracking.inc.php @@ -85,6 +85,8 @@ class infolog_tracking extends bo_tracking */ function infolog_tracking(&$boinfolog) { + $this->bo_tracking(); // calling the constructor of the extended class + $this->infolog =& $boinfolog; } @@ -136,6 +138,8 @@ class infolog_tracking extends bo_tracking */ function get_message($data,$old) { + if ($data['message']) return $data['message']; // async notification + if (!$data['info_datemodified'] || !$old) { return lang('New %1 created by %2 at %3',lang($this->infolog->enums['type'][$data['info_type']]), @@ -184,8 +188,8 @@ class infolog_tracking extends bo_tracking 'info_percent' => (int)$data['info_percent'].'%', 'info_datecompleted' => $data['info_datecomplete'] ? $this->datetime($data['info_datecompleted']-$this->infolog->tz_offset_s) : '', 'info_location' => $data['info_location'], - 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate']-$this->infolog->tz_offset_s) : '', - 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate']-$this->infolog->tz_offset_s) : '', + 'info_startdate' => $data['info_startdate'] ? $this->datetime($data['info_startdate']-$this->infolog->tz_offset_s,null) : '', + 'info_enddate' => $data['info_enddate'] ? $this->datetime($data['info_enddate']-$this->infolog->tz_offset_s,false) : '', 'info_responsible' => implode(', ',$responsible), 'info_subject' => $data['info_subject'], ) as $name => $value) diff --git a/infolog/inc/class.soinfolog.inc.php b/infolog/inc/class.soinfolog.inc.php index 58a8b3250d..8380cc8777 100644 --- a/infolog/inc/class.soinfolog.inc.php +++ b/infolog/inc/class.soinfolog.inc.php @@ -178,13 +178,15 @@ class soinfolog // DB-Layer /** * generate sql to be AND'ed into a query to ensure ACL is respected (incl. _PRIVATE) * - * @param $filter: none|all - list all entrys user have rights to see
- * private|own - list only his personal entrys (incl. those he is responsible for !!!), my = entries the user is responsible for + * @param string $filter: none|all - list all entrys user have rights to see
+ * private|own - list only his personal entrys (incl. those he is responsible for !!!), + * responsible|my = entries the user is responsible for + * delegated = entries the user delegated to someone else * @return string the necesary sql */ function aclFilter($filter = False) { - preg_match('/(my|own|privat|all|none|user)([0-9]*)/',$filter_was=$filter,$vars); + preg_match('/(my|responsible|delegated|own|privat|all|none|user)([0-9]*)/',$filter_was=$filter,$vars); $filter = $vars[1]; $f_user = intval($vars[2]); @@ -193,61 +195,67 @@ class soinfolog // DB-Layer return $this->acl_filter[$filter.$f_user]; // used cached filter if found } - if (is_array($this->grants)) - { - foreach($this->grants as $user => $grant) - { - // echo "

grants: user=$user, grant=$grant

"; - if ($grant & (EGW_ACL_READ|EGW_ACL_EDIT)) - { - $public_user_list[] = $user; - } - if ($grant & EGW_ACL_PRIVATE) - { - $private_user_list[] = $user; - } - } - if (count($private_user_list)) - { - $has_private_access = 'info_owner IN ('.implode(',',$private_user_list).')'; - } - } $filtermethod = " (info_owner=$this->user"; // user has all rights - if ($filter == 'my') + if ($filter == 'my' || $filter == 'responsible') { $filtermethod .= " AND info_responsible='0'"; } - // implicit read-rights for responsible user - $filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')"; - - // private: own entries plus the one user is responsible for - if ($filter == 'private' || $filter == 'own') + if ($filter == 'delegated') { - $filtermethod .= " OR (".$this->responsible_filter($this->user). - ($filter == 'own' && count($public_user_list) ? // offer's should show up in own, eg. startpage, but need read-access - " OR info_status = 'offer' AND info_owner IN(" . implode(',',$public_user_list) . ')' : '').")". - " AND (info_access='public'".($has_private_access?" OR $has_private_access":'').')'; + $filtermethod .= " AND info_responsible<>'0')"; } - elseif ($filter != 'my') // none --> all entrys user has rights to see + else { - if ($has_private_access) + if (is_array($this->grants)) { - $filtermethod .= " OR $has_private_access"; + foreach($this->grants as $user => $grant) + { + // echo "

grants: user=$user, grant=$grant

"; + if ($grant & (EGW_ACL_READ|EGW_ACL_EDIT)) + { + $public_user_list[] = $user; + } + if ($grant & EGW_ACL_PRIVATE) + { + $private_user_list[] = $user; + } + } + if (count($private_user_list)) + { + $has_private_access = 'info_owner IN ('.implode(',',$private_user_list).')'; + } } - if (count($public_user_list)) - { - $filtermethod .= " OR (info_access='public' AND info_owner IN(" . implode(',',$public_user_list) . '))'; - } - } - $filtermethod .= ') '; + // implicit read-rights for responsible user + $filtermethod .= " OR (".$this->responsible_filter($this->user)." AND info_access='public')"; - if ($filter == 'user' && $f_user > 0) - { - $filtermethod .= " AND (info_owner=$f_user AND info_responsible=0 OR ".$this->responsible_filter($f_user).')'; + // private: own entries plus the one user is responsible for + if ($filter == 'private' || $filter == 'own') + { + $filtermethod .= " OR (".$this->responsible_filter($this->user). + ($filter == 'own' && count($public_user_list) ? // offer's should show up in own, eg. startpage, but need read-access + " OR info_status = 'offer' AND info_owner IN(" . implode(',',$public_user_list) . ')' : '').")". + " AND (info_access='public'".($has_private_access?" OR $has_private_access":'').')'; + } + elseif ($filter != 'my' && $filter != 'responsible') // none --> all entrys user has rights to see + { + if ($has_private_access) + { + $filtermethod .= " OR $has_private_access"; + } + if (count($public_user_list)) + { + $filtermethod .= " OR (info_access='public' AND info_owner IN(" . implode(',',$public_user_list) . '))'; + } + } + $filtermethod .= ') '; + + if ($filter == 'user' && $f_user > 0) + { + $filtermethod .= " AND (info_owner=$f_user AND info_responsible=0 OR ".$this->responsible_filter($f_user).')'; + } } //echo "

aclFilter(filter='$filter_was',user='$user') = '$filtermethod', privat_user_list=".print_r($privat_user_list,True).", public_user_list=".print_r($public_user_list,True)."

\n"; - return $this->acl_filter[$filter.$f_user] = $filtermethod; // cache the filter } @@ -274,15 +282,17 @@ class soinfolog // DB-Layer /** * generate sql to filter based on the start- and enddate of the log-entry * - * @param $filter upcoming = startdate is in the future
- * today startdate < tomorrow
- * overdue enddate < tomorrow
+ * @param string $filter upcoming = startdate is in the future + * today: startdate < tomorrow + * overdue: enddate < tomorrow + * date: today <= startdate && startdate < tomorrow + * enddate: today <= enddate && enddate < tomorrow * limitYYYY/MM/DD not older or open * @return string the necesary sql */ function dateFilter($filter = '') { - preg_match('/(upcoming|today|overdue|date)([-\\/.0-9]*)/',$filter,$vars); + preg_match('/(upcoming|today|overdue|date|enddate)([-\\/.0-9]*)/',$filter,$vars); $filter = $vars[1]; if (isset($vars[2]) && !empty($vars[2]) && ($date = split('[-/.]',$vars[2]))) @@ -309,6 +319,12 @@ class soinfolog // DB-Layer return ''; } return " AND ($today <= info_startdate AND info_startdate < $tomorrow)"; + case 'enddate': + if (!$today || !$tomorrow) + { + return ''; + } + return " AND ($today <= info_enddate AND info_enddate < $tomorrow)"; case 'limit': return " AND (info_modified >= '$today' OR NOT (info_status IN ('done','billed','cancelled')))"; } @@ -705,4 +721,46 @@ class soinfolog // DB-Layer } return $ids; } + + /** + * Query infolog for users with open entries, either own or responsible, with start or end within 4 days + * + * This functions tries to minimize the users really checked with the complete filters, as creating a + * user enviroment and running the specific check costs ... + * + * @return array with acount_id's groups get resolved to there memebers + */ + function users_with_open_entries() + { + $users = array(); + + $this->db->select($this->info_table,'DISTINCT info_owner',array( + str_replace(' AND ','',$this->statusFilter('open')), + '(ABS(info_startdate-'.time().')<'.(4*24*60*60).' OR '. // start_day within 4 days + 'ABS(info_enddate-'.time().')<'.(4*24*60*60).')', // end_day within 4 days + ),__LINE__,__FILE__); + while ($this->db->next_record()) + { + $users[] = $this->db->f(0); + } + $this->db->select($this->info_table,'DISTINCT info_responsible',str_replace(' AND ','',$this->statusFilter('open')),__LINE__,__FILE__); + while ($this->db->next_record()) + { + foreach(explode(',',$this->db->f(0)) as $responsible) + { + if ($GLOBALS['egw']->accounts->get_type($responsible) == 'g') + { + $responsible = $GLOBALS['egw']->accounts->members($responsible,true); + } + if ($responsible) + { + foreach(is_array($responsible) ? $responsible : array($responsible) as $user) + { + if ($user && !in_array($user,$users)) $users[] = $user; + } + } + } + } + return $users; + } } diff --git a/infolog/inc/class.uiinfolog.inc.php b/infolog/inc/class.uiinfolog.inc.php index 0bf81de6d4..82fa80f359 100644 --- a/infolog/inc/class.uiinfolog.inc.php +++ b/infolog/inc/class.uiinfolog.inc.php @@ -90,19 +90,23 @@ class uiinfolog 'offer' => 'offer.gif', 'offer_alt' => 'offer' ) ); var $filters = array( - 'none' => 'no Filter', - 'done' => 'done', - 'my' => 'responsible', - 'my-open-today' => 'responsible open', - 'my-open-overdue' => 'responsible overdue', - 'my-upcoming' => 'responsible upcoming', - 'own' => 'own', - 'own-open-today' => 'own open', - 'own-open-overdue' => 'own overdue', - 'own-upcoming' => 'own upcoming', - 'open-today' => 'open', - 'open-overdue' => 'overdue', - 'upcoming' => 'upcoming' + 'none' => 'no Filter', + 'done' => 'done', + 'responsible' => 'responsible', + 'responsible-open-today' => 'responsible open', + 'responsible-open-overdue' => 'responsible overdue', + 'responsible-upcoming' => 'responsible upcoming', + 'delegated' => 'delegated', + 'delegated-open-today' => 'delegated open', + 'delegated-open-overdue' => 'delegated overdue', + 'delegated-upcoming' => 'delegated upcomming', + 'own' => 'own', + 'own-open-today' => 'own open', + 'own-open-overdue' => 'own overdue', + 'own-upcoming' => 'own upcoming', + 'open-today' => 'open', + 'open-overdue' => 'overdue', + 'upcoming' => 'upcoming', ); var $messages = array( 'edit' => 'InfoLog - Edit', @@ -138,6 +142,24 @@ class uiinfolog $this->duration_format = str_replace(',','',$pm_config->config_data['duration_units']).','.$pm_config->config_data['hours_per_workday']; unset($pm_config); } + /* these are just for testing of the notifications + for($i = -1; $i <= 3; ++$i) + { + $this->filters['delegated-open-enddate'.date('Y-m-d',time()+$i*24*60*60)] = "delegated due in $i day(s)"; + } + for($i = -1; $i <= 3; ++$i) + { + $this->filters['responsible-open-enddate'.date('Y-m-d',time()+$i*24*60*60)] = "responsible due in $i day(s)"; + } + for($i = -1; $i <= 3; ++$i) + { + $this->filters['delegated-open-date'.date('Y-m-d',time()+$i*24*60*60)] = "delegated starting in $i day(s)"; + } + for($i = -1; $i <= 3; ++$i) + { + $this->filters['responsible-open-date'.date('Y-m-d',time()+$i*24*60*60)] = "responsible starting in $i day(s)"; + } + */ $GLOBALS['uiinfolog'] =& $this; // make ourself availible for ExecMethod of get_rows function } diff --git a/infolog/inc/hook_settings.inc.php b/infolog/inc/hook_settings.inc.php index 4313863d29..85a5f6efcf 100644 --- a/infolog/inc/hook_settings.inc.php +++ b/infolog/inc/hook_settings.inc.php @@ -21,7 +21,7 @@ $have_custom_fields = count($ui->bo->customfields) > 0; unset($ui); // migrage old filter-pref 1,2 to the filter one 'own-open-today' -if (in_array($GLOBALS['egw']->preferences->{$GLOBALS['type']}['homeShowEvents'],array('1','2'))) +if (isset($GLOBALS['type']) && in_array($GLOBALS['egw']->preferences->{$GLOBALS['type']}['homeShowEvents'],array('1','2'))) { $GLOBALS['egw']->preferences->add('infolog','homeShowEvents','own-open-today',$GLOBALS['type']); $GLOBALS['egw']->preferences->save_repository(); @@ -136,7 +136,7 @@ $GLOBALS['settings']['notify_creator'] = array( 'type' => 'check', 'label' => 'Receive notifications about own items', 'name' => 'notify_creator', - 'help' => 'Do you want a notification mail, if items you created get updated?', + 'help' => 'Do you want a notification, if items you created get updated?', 'xmlrpc' => True, 'admin' => False, ); @@ -144,10 +144,57 @@ $GLOBALS['settings']['notify_assigned'] = array( 'type' => 'check', 'label' => 'Receive notifications about items assigned to you', 'name' => 'notify_assigned', - 'help' => 'Do you want a notification mails, if items get assigned to you or assigned items get updated?', + 'help' => 'Do you want a notification, if items get assigned to you or assigned items get updated?', 'xmlrpc' => True, 'admin' => False, ); + +// to add options for more then 3 days back or in advance, you need to update soinfolog::users_with_open_entries()! +$options = array( + '0' => lang('No'), + '-1d' => lang('one day after'), + '0d' => lang('same day'), + '1d' => lang('one day in advance'), + '2d' => lang('%1 days in advance',2), + '3d' => lang('%1 days in advance',3), +); +$GLOBALS['settings']['notify_due_delegated'] = array( + 'type' => 'select', + 'label' => 'Receive notifications about due entries you delegated', + 'name' => 'notify_due_delegated', + 'help' => 'Do you want a notification, if items you delegated are due?', + 'values' => $options, + 'xmlrpc' => True, + 'admin' => False, +); +$GLOBALS['settings']['notify_due_responsible'] = array( + 'type' => 'select', + 'label' => 'Receive notifications about due entries you are responsible for', + 'name' => 'notify_due_responsible', + 'help' => 'Do you want a notification, if items you are responsible for are due?', + 'values' => $options, + 'xmlrpc' => True, + 'admin' => False, +); +$GLOBALS['settings']['notify_start_delegated'] = array( + 'type' => 'select', + 'label' => 'Receive notifications about starting entries you delegated', + 'name' => 'notify_start_delegated', + 'help' => 'Do you want a notification, if items you delegated are about to start?', + 'values' => $options, + 'xmlrpc' => True, + 'admin' => False, +); +$GLOBALS['settings']['notify_start_responsible'] = array( + 'type' => 'select', + 'label' => 'Receive notifications about starting entries you are responsible for', + 'name' => 'notify_start_responsible', + 'help' => 'Do you want a notification, if items you are responsible for are about to start?', + 'values' => $options, + 'xmlrpc' => True, + 'admin' => False, +); + $GLOBALS['settings']['notify_html'] = array( 'type' => 'check', 'label' => 'Receive notifications as html-mails', diff --git a/infolog/setup/phpgw_de.lang b/infolog/setup/phpgw_de.lang index 5bf7f1128c..b229cbacd8 100644 --- a/infolog/setup/phpgw_de.lang +++ b/infolog/setup/phpgw_de.lang @@ -1,5 +1,11 @@ +%1 days in advance infolog de %1 Tage im Vorraus +%1 modified by %2 at %3 infolog de %1 wurde von %2 am %3 geändert %1 records imported infolog de %1 Datensätze importiert %1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog de %1 Datensätze gelesen (noch nicht importiert, sie können %2zurück%3 gehen und Test Import ausschalten) +%1 you are responsible for is due at %2 infolog de %1 für die Sie verantwortlich sind ist am %2 fällig +%1 you are responsible for is starting at %2 infolog de %1 für die Sie verantwortlich sind startet am %2 +%1 you delegated is due at %2 infolog de %1 die Sie delegierten ist am %2 fällig +%1 you delegated is starting at %2 infolog de %1 die Sie delegierten startet am %2 - subprojects from infolog de - Untereinträge von 0% infolog de 0% 10% infolog de 10% @@ -36,7 +42,7 @@ apply the changes infolog de are you shure you want to delete this entry ? infolog de Sind Sie sicher, dass Sie diesen Eintrag löschen wollen? attach a file infolog de Datei anhängen attach file infolog de Datei anhängen -attension: no contact with address %1 found. infolog de Achtung: Kein Kontakt mit der Adresse %1 gefunden! +attention: no contact with address %1 found. infolog de Achtung: Kein Kontakt mit der Adresse %1 gefunden! back to main list infolog de Zurück zur Gesamtliste billed infolog de abgerechnet both infolog de Annahme+erledigt @@ -93,7 +99,14 @@ deletes this status infolog de l description infolog de Beschreibung determines the order the fields are displayed infolog de legt die Reihenfolge fest in der die Felder angezeigt werden disables a status without deleting it infolog de deaktiviert einen Status ohne ihn zu löschen -do you want a confirmation of the responsible on: accepting, finishing the task or both infolog de wollen Sie eine Bestätigung des Verantwortlichen bei: Annahme, Beendigung der Aufgabe oder bei beidem +do you want a confirmation of the responsible on: accepting, finishing the task or both infolog de Wollen Sie eine Bestätigung des Verantwortlichen bei: Annahme, Beendigung der Aufgabe oder bei beidem +do you want a notification, if items get assigned to you or assigned items get updated? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge Ihnen zugewiesen werden oder zugewiesene Einträge geändert werden? +do you want a notification, if items you are responsible for are about to start? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge für die Sie verantwortlich sind beginnen sollen? +do you want a notification, if items you are responsible for are due? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge für die Sie verantwortlich sind fällig werden? +do you want a notification, if items you created get updated? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie angelegt haben aktualisiert werden? +do you want a notification, if items you delegated are about to start? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie delegiert haben beginnen sollen? +do you want a notification, if items you delegated are due? infolog de Wollen Sie eine Benachrichtigung, wenn Einträge die Sie delegiert haben fällig werden? +do you want to receive notifications as html-mails or plain text? infolog de Wollen Sie die Benachrichtigungen als HTML Mail oder reinen Text empfangen? do you want to see custom infolog types in the calendar? infolog de Wollen Sie benutzerdefinierte Typen auch im Kalender sehen? don't show infolog infolog de InfoLog NICHT anzeigen done infolog de erledigt @@ -164,6 +177,7 @@ max length of the input [, length of the inputfield (optional)] infolog de max. name must not be empty !!! infolog de Name darf nicht leer sein !!! name of new type to create infolog de Name des neu anzulegenden Types never hide search and filters infolog de Suche und Filter niemals ausblenden +new %1 created by %2 at %3 infolog de Neue %1 wurde von %2 am %3 angelegt new name infolog de neuer name new search infolog de Neue Suche no - cancel infolog de Nein - Abbruch @@ -181,6 +195,8 @@ note infolog de Notiz number of records to read (%1) infolog de Anzahl Datensätze lesen (%1) number of row for a multiline inputfield or line of a multi-select-box infolog de Anzahl Zeilen für ein mehrzeiliges Eingabefeld oder eines mehrfachen Auswahlfeldes offer infolog de Angebot +one day after infolog de am nächsten Tag +one day in advance infolog de am Vortag ongoing infolog de in Arbeit only for details infolog de Nur bei Details only the attachments infolog de nur die Anhänge @@ -212,6 +228,13 @@ project settings: price, times infolog de Einstellungen zum Projekt: Preis, Zeit re: infolog de Re: read one record by passing its id. infolog de Einen Datensatz spezifiziert durch seine id lesen. read rights (default) infolog de Leserechte (Vorgabe) +receive notifications about due entries you are responsible for infolog de Benachrichtigungen über fällige Einträge für die Sie verantwortlich sind +receive notifications about due entries you delegated infolog de Benachrichtigungen über fällige Einträge die Sie delegiert haben +receive notifications about items assigned to you infolog de Benachrichtigungen über Einträge für die Sie verantwortlich sind +receive notifications about own items infolog de Benachrichtigungen über eigene Einträge +receive notifications about starting entries you are responsible for infolog de Benachrichtigungen über zu startende Einträge für die Sie verantwortlich sind +receive notifications about starting entries you delegated infolog de Benachrichtigungen über zu startende Einträge die Sie delegiert haben +receive notifications as html-mails infolog de Benachrichtigungen als HTML Mails reg. expr. for local ip's
eg. ^192.168.1. infolog de reg. Ausdr. für lokale IP's
^192\.168\.1\. reg. expr. for local ip's
eg. ^192\.168\.1\. infolog de reg. Ausdr. für lokale IP's
^192\.168\.1\. remark infolog de Bemerkung @@ -223,6 +246,7 @@ responsible upcoming infolog de verantwortlich zuk responsible user, priority infolog de Verantwortlicher, Priorität returns a list / search for records. infolog de Liefert eine Liste von / sucht nach Datensätzen. rights for the responsible infolog de Rechte für den Verantwortlichen +same day infolog de gleichen Tag save infolog de Speichern saves the changes made and leaves infolog de speichert die Änderungen und beendet saves this entry infolog de diesen Eintrag speichern @@ -294,6 +318,7 @@ urgent infolog de Dringend used time infolog de benötigte Zeit valid path on clientside
eg. \\server\share or e:\ infolog de gültiger Pfad clientseitig
zB. \\Server\Share oder e:\ valid path on clientside
eg. \servershare or e: infolog de gültiger Pfad clientseitig
zB. \\Server\Share oder e:\ +valid path on clientside
eg. servershare or e: infolog de gültiger Pfad clientseitig
zB. \\Server\Share oder e:\ values for selectbox infolog de Werte für die Auswahlbox view all subs of this entry infolog de alle Untereinträge dieses Eintrag anzeigen view other subs infolog de andere Untereinträge anzeigen diff --git a/infolog/setup/phpgw_en.lang b/infolog/setup/phpgw_en.lang index edf355147a..ae59640f84 100644 --- a/infolog/setup/phpgw_en.lang +++ b/infolog/setup/phpgw_en.lang @@ -1,5 +1,11 @@ +%1 days in advance infolog en %1 days in advance +%1 modified by %2 at %3 infolog en %1 modified by %2 at %3 %1 records imported infolog en %1 records imported %1 records read (not yet imported, you may go %2back%3 and uncheck test import) infolog en %1 records read (not yet imported, you may go %2back%3 and uncheck Test Import) +%1 you are responsible for is due at %2 infolog en %1 you are responsible for is due at %2 +%1 you are responsible for is starting at %2 infolog en %1 you are responsible for is starting at %2 +%1 you delegated is due at %2 infolog en %1 you delegated is due at %2 +%1 you delegated is starting at %2 infolog en %1 you delegated is starting at %2 - subprojects from infolog en - Subprojects from 0% infolog en 0% 10% infolog en 10% @@ -36,7 +42,7 @@ apply the changes infolog en Apply the changes are you shure you want to delete this entry ? infolog en Are you sure you want to delete this entry ? attach a file infolog en Attach a file attach file infolog en Attach file -attension: no contact with address %1 found. infolog en Attension: No Contact with address %1 found. +attention: no contact with address %1 found. infolog en Attention: No Contact with address %1 found. back to main list infolog en Back to main list billed infolog en billed both infolog en both @@ -94,6 +100,13 @@ description infolog en Description determines the order the fields are displayed infolog en determines the order the fields are displayed disables a status without deleting it infolog en disables a status without deleting it do you want a confirmation of the responsible on: accepting, finishing the task or both infolog en do you want a confirmation of the responsible on: accepting, finishing the task or both +do you want a notification, if items get assigned to you or assigned items get updated? infolog en Do you want a notification, if items get assigned to you or assigned items get updated? +do you want a notification, if items you are responsible for are about to start? infolog en Do you want a notification, if items you are responsible for are about to start? +do you want a notification, if items you are responsible for are due? infolog en Do you want a notification, if items you are responsible for are due? +do you want a notification, if items you created get updated? infolog en Do you want a notification, if items you created get updated? +do you want a notification, if items you delegated are about to start? infolog en Do you want a notification, if items you delegated are about to start? +do you want a notification, if items you delegated are due? infolog en Do you want a notification, if items you delegated are due? +do you want to receive notifications as html-mails or plain text? infolog en Do you want to receive notifications as html-mails or plain text? do you want to see custom infolog types in the calendar? infolog en Do you want to see custom InfoLog types in the calendar? don't show infolog infolog en DON'T show InfoLog done infolog en done @@ -164,6 +177,7 @@ max length of the input [, length of the inputfield (optional)] infolog en max l name must not be empty !!! infolog en Name must not be empty !!! name of new type to create infolog en name of new type to create never hide search and filters infolog en Never hide search and filters +new %1 created by %2 at %3 infolog en New %1 created by %2 at %3 new name infolog en new name new search infolog en New search no - cancel infolog en No - Cancel @@ -181,6 +195,8 @@ note infolog en Note number of records to read (%1) infolog en Number of records to read (%1) number of row for a multiline inputfield or line of a multi-select-box infolog en number of row for a multiline inputfield or line of a multi-select-box offer infolog en offer +one day after infolog en one day after +one day in advance infolog en one day in advance ongoing infolog en ongoing only for details infolog en Only for details only the attachments infolog en only the attachments @@ -212,6 +228,13 @@ project settings: price, times infolog en Project settings: price, times re: infolog en Re: read one record by passing its id. infolog en Read one record by passing its id. read rights (default) infolog en read rights (default) +receive notifications about due entries you are responsible for infolog en Receive notifications about due entries you are responsible for +receive notifications about due entries you delegated infolog en Receive notifications about due entries you delegated +receive notifications about items assigned to you infolog en Receive notifications about items assigned to you +receive notifications about own items infolog en Receive notifications about own items +receive notifications about starting entries you are responsible for infolog en Receive notifications about starting entries you are responsible for +receive notifications about starting entries you delegated infolog en Receive notifications about starting entries you delegated +receive notifications as html-mails infolog en Receive notifications as html-mails reg. expr. for local ip's
eg. ^192\.168\.1\. infolog en reg. expr. for local IP's
eg. ^192\.168\.1\. remark infolog en Remark remove this link (not the entry itself) infolog en Remove this link (not the entry itself) @@ -222,6 +245,7 @@ responsible upcoming infolog en responsible upcoming responsible user, priority infolog en responsible user, priority returns a list / search for records. infolog en Returns a list / search for records. rights for the responsible infolog en Rights for the responsible +same day infolog en same day save infolog en Save saves the changes made and leaves infolog en saves the changes made and leaves saves this entry infolog en Saves this entry diff --git a/infolog/setup/setup.inc.php b/infolog/setup/setup.inc.php index 06e8472fa1..42f0eeb6c4 100755 --- a/infolog/setup/setup.inc.php +++ b/infolog/setup/setup.inc.php @@ -44,11 +44,12 @@ $setup_info['infolog']['note'] = expressions and direct calls to php-functions (e.g. to link the phone calls (again) to the addressbook entrys).

More information about InfoLog and the current development-status can be found on the - InfoLog page on our Website.

'; + InfoLog page on our Website.

'; /* The hooks this app includes, needed for hooks registration */ $setup_info['infolog']['hooks']['preferences'] = 'infolog.admin_prefs_sidebox_hooks.all_hooks'; $setup_info['infolog']['hooks'][] = 'settings'; +$setup_info['infolog']['hooks']['verify_settings'] = 'infolog.admin_prefs_sidebox_hooks.verify_settings'; $setup_info['infolog']['hooks']['admin'] = 'infolog.admin_prefs_sidebox_hooks.all_hooks'; $setup_info['infolog']['hooks'][] = 'deleteaccount'; $setup_info['infolog']['hooks'][] = 'home'; From 5e9b8efdb6961ace462f27c46bccf1b618bb69eb Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 10 Jun 2007 10:26:50 +0000 Subject: [PATCH 14/15] a few more missing translations --- infolog/setup/phpgw_de.lang | 4 ++++ infolog/setup/phpgw_en.lang | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/infolog/setup/phpgw_de.lang b/infolog/setup/phpgw_de.lang index b229cbacd8..8ffc6e0871 100644 --- a/infolog/setup/phpgw_de.lang +++ b/infolog/setup/phpgw_de.lang @@ -87,6 +87,10 @@ dates, status, access infolog de Daten, Status, Zugriff days infolog de Tage default filter for infolog infolog de Standard-Filter für InfoLog default status for a new log entry infolog de Vorgabe für den Status eines neuen Eintrags +delegated infolog de delegiert +delegated open infolog de delegiert offen +delegated overdue infolog de delegiert überfällig +delegated upcomming infolog de delegiert zukünftig delegation infolog de Delegation delete infolog de Löschen delete one record by passing its id. infolog de Einen Datensatz spezifiziert durch seine id löschen. diff --git a/infolog/setup/phpgw_en.lang b/infolog/setup/phpgw_en.lang index ae59640f84..8bad9a7290 100644 --- a/infolog/setup/phpgw_en.lang +++ b/infolog/setup/phpgw_en.lang @@ -87,6 +87,10 @@ dates, status, access infolog en Dates, Status, Access days infolog en days default filter for infolog infolog en Default Filter for InfoLog default status for a new log entry infolog en default status for a new log entry +delegated infolog en delegated +delegated open infolog en delegated open +delegated overdue infolog en delegated overdue +delegated upcomming infolog en delegated upcomming delegation infolog en Delegation delete infolog en Delete delete one record by passing its id. infolog en Delete one record by passing its id. From f03dce0a4d42e32d695a73a8ea6564111da7e50b Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Mon, 11 Jun 2007 12:17:03 +0000 Subject: [PATCH 15/15] "fixed not working account-mirgration from LDAP --> SQL" --- setup/account_migration.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup/account_migration.php b/setup/account_migration.php index 920cf04ce2..f56f63c845 100644 --- a/setup/account_migration.php +++ b/setup/account_migration.php @@ -97,6 +97,11 @@ if (!$_POST['migrate']) // fetch the complete data (search reads not everything), plus the members(hips) foreach($accounts as $account_id => $account) { + if ($account_id != $account['account_id']) // not all backends have as key the account_id + { + unset($accounts[$account_id]); + $account_id = $account['account_id']; + } $accounts[$account_id] = $GLOBALS['egw']->accounts->read($account_id); if ($account['account_type'] == 'g')