From f0e391220f9cbe5d4e1bc1ffc1be99eb5dfc7c55 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Wed, 28 May 2014 10:03:57 +0000 Subject: [PATCH] * Calendar: adding default alarm for regular or whole-day events, preference is shared with CalDAV clients --- calendar/inc/class.calendar_bo.inc.php | 45 ++++++ calendar/inc/class.calendar_hooks.inc.php | 133 +++++++++++++++++- calendar/inc/class.calendar_uiforms.inc.php | 7 +- calendar/lang/egw_de.lang | 8 ++ calendar/lang/egw_en.lang | 8 ++ calendar/setup/setup.inc.php | 1 + .../inc/class.preferences_settings.inc.php | 18 +-- 7 files changed, 203 insertions(+), 17 deletions(-) diff --git a/calendar/inc/class.calendar_bo.inc.php b/calendar/inc/class.calendar_bo.inc.php index 1977d5bc4d..eb6403d85c 100644 --- a/calendar/inc/class.calendar_bo.inc.php +++ b/calendar/inc/class.calendar_bo.inc.php @@ -146,6 +146,23 @@ class calendar_bo 'OPT-PARTICIPANT' => 'Optional', 'NON-PARTICIPANT' => 'None', ); + /** + * Alarm times + * + * @var array + */ + var $alarms = array( + 300 => '5 Minutes', + 600 => '10 Minutes', + 900 => '15 Minutes', + 1800 => '30 Minutes', + 3600 => '1 Hour', + 7200 => '2 Hours', + 43200 => '12 Hours', + 86400 => '1 Day', + 172800 => '2 Days', + 604800 => '1 Week', + ); /** * @var array $resources registered scheduling resources of the calendar (gets cached in the session for performance reasons) */ @@ -253,6 +270,34 @@ class calendar_bo $this->categories = new categories($this->user,'calendar'); $this->customfields = config::get_customfields('calendar'); + + foreach($this->alarms as $secs => &$label) + { + $label = self::secs2label($secs); + } + } + + /** + * Generate translated label for a given number of seconds + * + * @param int $secs + * @return string + */ + static public function secs2label($secs) + { + if ($secs <= 3600) + { + $label = lang('%1 minutes', $secs/60); + } + elseif($secs <= 86400) + { + $label = lang('%1 hours', $secs/3600); + } + else + { + $label = lang('%1 days', $secs/86400); + } + return $label; } /** diff --git a/calendar/inc/class.calendar_hooks.inc.php b/calendar/inc/class.calendar_hooks.inc.php index c0d1e11be1..17dcd304ae 100644 --- a/calendar/inc/class.calendar_hooks.inc.php +++ b/calendar/inc/class.calendar_hooks.inc.php @@ -154,7 +154,8 @@ class calendar_hooks { if (!$hook_data['setup']) // does not work on setup time { - ExecMethod('calendar.calendar_bo.check_set_default_prefs'); + $bo = new calendar_bo(); + $bo->check_set_default_prefs(); } $mainscreen = array( '0' => lang('None'), @@ -276,7 +277,7 @@ class calendar_hooks } $settings = array( - array( + '1.section' => array( 'type' => 'section', 'title' => lang('General settings'), 'no_lang'=> true, @@ -411,7 +412,7 @@ class calendar_hooks 'admin' => False, 'forced' => 'all', ), - array( + '2.section' => array( 'type' => 'section', 'title' => lang('appointment settings'), 'no_lang'=> true, @@ -429,6 +430,47 @@ class calendar_hooks 'admin' => False, 'default'=> 60, ), + 'default-alarm' => array( + 'type' => 'select', + 'label' => 'Default alarm for regular events', + 'name' => 'default-alarm', + 'values' => isset($bo) ? array(0 => lang('None'))+$bo->alarms : array(), + 'help' => 'Alarm added automatic to new events before event start-time', + 'xmlrpc' => True, + 'admin' => False, + 'default' => 0, + ), + 'default-alarm-wholeday' => array( + 'type' => 'select', + 'label' => 'Default alarm for whole-day events', + 'name' => 'default-alarm-wholeday', + 'values' => isset($bo) ? array(0 => lang('None'))+$bo->alarms : array(), + 'help' => lang('Alarm added automatic to new events before event start-time').' ('.lang('Midnight').')', + 'xmlrpc' => True, + 'admin' => False, + 'default' => 0, + ) + ); + if (isset($bo)) // add custom time-spans set by CalDAV clients, not in our prefs + { + $prefs = $GLOBALS['egw_info']['user']['preferences']['calendar']; + $data = array( + 'prefs' => &$prefs, // use reference to get preference value back + 'preprocess' => true, + 'type' => 'user', + ); + self::verify_settings($data); + foreach(array('default-alarm', 'default-alarm-wholeday') as $name) + { + $value = $prefs[$name]; + if ($value > 0 && !isset($bo->alarms[$value])) + { + $settings[$name]['values'][$value] = calendar_bo::secs2label($value); + ksort($settings[$name]['values']); + } + } + } + $settings += array( 'defaultresource_sel' => array( 'type' => 'select', 'label' => 'default type of resources selection', @@ -458,7 +500,7 @@ class calendar_hooks 'xmlrpc' => True, 'admin' => False, ), - array( + '3.section' => array( 'type' => 'section', 'title' => lang('notification settings'), 'no_lang'=> true, @@ -579,7 +621,7 @@ class calendar_hooks 'xmlrpc' => True, 'admin' => False, ), - array( + '4.section' => array( 'type' => 'section', 'title' => lang('Data exchange settings'), 'no_lang'=> true, @@ -696,6 +738,87 @@ class calendar_hooks return $settings; } + /** + * Verify settings hook called to generate errors about settings used here to store default alarms in CalDAV prefs + * + * @param array $data + * array $data['prefs'] + * string $data['type'] 'user', 'default' or 'forced' + * boolean $data['preprocess'] true data just shown to user, false: data stored by user + */ + public static function verify_settings(array $data) + { + //error_log(__METHOD__."(".array2string($data).")"); + // caldav perfs are always user specific and cant by switched off + if ($data['type'] == 'user') + { + $account_lid = $GLOBALS['egw_info']['user']['account_lid']; + foreach(array( + 'default-alarm' => 'default-alarm-vevent-datetime:/'.$account_lid.'/:urn:ietf:params:xml:ns:caldav', + 'default-alarm-wholeday' => 'default-alarm-vevent-date:/'.$account_lid.'/:urn:ietf:params:xml:ns:caldav', + ) as $name => $dav) + { + $pref =& $GLOBALS['egw_info']['user']['preferences']['groupdav'][$dav]; + if (true) $pref = str_replace("\r", '', $pref); // remove CR messing up multiline preg_match + $val = $data['prefs'][$name]; + //error_log(__METHOD__."() groupdav[$dav]=$pref, calendar[$name]=$val"); + + if ($data['preprocess']) // showing preferences + { + if ((string)$data['prefs'][$name] === '') // no calendar pref --> read value from caldav + { + $matches = null; + if (preg_match('/^ACTION:NONE$/i', $pref)) + { + $data['prefs'][$name] = '0'; + } + elseif (preg_match('/^TRIGGER:-PT(\d+(M|H|D))$/mi', $pref, $matches)) + { + static $factors = array( + 'M' => 60, + 'H' => 3600, + 'D' => 86400, + ); + $factor = $factors[strtoupper($matches[2])]; + $data['prefs'][$name] = $factor*(int)$matches[1]; + } + else + { + $data['prefs'][$name] = '0'; + } + error_log(__METHOD__."() setting $name={$data['prefs'][$name]} from $dav='$pref'"); + } + } + else // storing preferences + { + if (empty($pref) || !preg_match('/^TRIGGER:/m', $pref)) + { + $pref = 'BEGIN:VALARM +TRIGGER:-PT1H +ATTACH;VALUE=URI:Basso +ACTION:AUDIO +END:VALARM'; + } + if (!$val) + { + $pref = preg_replace('/^ACTION:.*$/m', 'ACTION:NONE', $pref); + } + elseif ($val < 3600) + { + $pref = preg_replace('/^TRIGGER:.*$/m', 'TRIGGER:-PT'.number_format($val/60, 0).'M', $pref); + } + else + { + $pref = preg_replace('/^TRIGGER:.*$/m', 'TRIGGER:-PT'.number_format($val/3600, 0).'H', $pref); + } + $GLOBALS['egw']->preferences->add('groupdav', $dav, $pref, 'user'); + error_log(__METHOD__."() storing $name=$val --> $dav='$pref'"); + } + } + $GLOBALS['egw']->preferences->save_repository(); + } + } + public static function config_validate() { $GLOBALS['egw_info']['server']['found_validation_hook'] = array('calendar_purge_old'); diff --git a/calendar/inc/class.calendar_uiforms.inc.php b/calendar/inc/class.calendar_uiforms.inc.php index 9083560f43..b97003f8d4 100644 --- a/calendar/inc/class.calendar_uiforms.inc.php +++ b/calendar/inc/class.calendar_uiforms.inc.php @@ -326,7 +326,7 @@ class calendar_uiforms extends calendar_ui foreach($this->bo->resources as $type => $data) { if ($data['app'] == $app) break; - } + } $uid = $this->bo->resources[$type]['app'] == $app ? $type.$id : false; if ($app == 'home-accounts') { @@ -891,7 +891,7 @@ class calendar_uiforms extends calendar_ui case 'add_alarm': $time = ($content['actual_date'] ? $content['actual_date'] : $content['start']); $offset = $time - $content['new_alarm']['date']; - + if ($event['recur_type'] != MCAL_RECUR_NONE && ($next_occurrence = $this->bo->read($event['id'], $this->bo->now_su + $offset, true)) && $time < $next_occurrence['start']) @@ -1125,8 +1125,7 @@ class calendar_uiforms extends calendar_ui 'status' => $this->bo->verbose_status, 'duration' => $this->durations, 'role' => $this->bo->roles, - 'new_alarm[options]' =>array(300 => lang('5 Minutes'), 600 => lang('10 Minutes'), 900 => lang('15 Minutes'), 1800 => lang('30 Minutes'), 3600 => lang('1 Hour'), 7200 => lang('2 Hours'), - 43200 => lang('12 Hours'), 86400 => lang('1 Day'), 172800 => lang('2 Days'), 604800 => lang('1 Week'), 0 => lang('Custom')), + 'new_alarm[options]' => $this->bo->alarms+array(0 => lang('Custom')), 'before_after'=>array(0 => lang('Before'), 1 => lang('After')), 'action' => array( 'copy' => array('label' => 'Copy', 'title' => 'Copy this event'), diff --git a/calendar/lang/egw_de.lang b/calendar/lang/egw_de.lang index f821d487b9..e86caf8f22 100644 --- a/calendar/lang/egw_de.lang +++ b/calendar/lang/egw_de.lang @@ -1,6 +1,9 @@ %1 %2 in %3 calendar de %1 %2 im %3 +%1 days calendar de %1 Tage %1 event(s) %2 calendar de %1 Termin(e) %2 %1 event(s) %2, %3 failed because of insufficent rights !!! calendar de %1 Termin(e) %2, %3 wegen fehlender Rechte !!! +%1 hours calendar de %1 Stunden +%1 minutes calendar de %1 Minuten %1 participants removed because of missing invite grants calendar de %1 Teilnehmer entfernt wegen fehlender Einladungsrechte %1 records imported calendar de %1 Datensätze importiert %1 records read (not yet imported, you may go back and uncheck test import) calendar de %1 Datensätze gelesen (noch nicht importiert, Sie können zurück gehen und Test Import ausschalten) @@ -33,6 +36,7 @@ after current date calendar de Nach dem aktuellen Datum age: calendar de Alter: alarm calendar de Alarm alarm added calendar de Alarm zugefügt +alarm added automatic to new events before event start-time calendar de Der Alarm wird automatisch bei neuen Termin vor dem Startdatum eingefügt alarm deleted calendar de Alarm gelöscht alarm for %1 at %2 in %3 calendar de Alarm für %1 am %2 in %3 alarm management calendar de Alarm Management @@ -113,6 +117,7 @@ creator calendar de Ersteller csv calendar de CSV csv-fieldname calendar de CSV-Feldname csv-filename calendar de CSV-Dateiname +custom calendar de Benutzerdefiniert custom fields common de Benutzerdefinierte Felder custom_2 common de frei / besetzt daily calendar de Täglich @@ -125,6 +130,8 @@ days calendar de Tage days of the week for a weekly repeated event calendar de Wochentage für wöchentlich wiederholten Termin days repeated calendar de wiederholte Tage dayview calendar de Tagesansicht +default alarm for regular events calendar de Default-Alarm für normale Termine +default alarm for whole-day events calendar de Default-Alarm für ganztägige Termine default appointment length (in minutes) calendar de Standardlänge eines neuen Kalendereintrags (in Minuten) default calendar filter calendar de Standard-Filter des Kalenders default calendar view calendar de Standard-Ansicht des Kalenders @@ -314,6 +321,7 @@ maximum available quantity of %1 exceeded! calendar de Maximale Anzahl von %1 er meeting request calendar de Terminanfrage meetingrequest to all participants calendar de Terminanforderung an alle Teilnehmer merge document... calendar de Dokument einfügen... +midnight calendar de Mitternacht minutes calendar de Minuten modified calendar de Geändert modifier calendar de Geändert von diff --git a/calendar/lang/egw_en.lang b/calendar/lang/egw_en.lang index fab66ed2c4..5d5e353872 100644 --- a/calendar/lang/egw_en.lang +++ b/calendar/lang/egw_en.lang @@ -1,6 +1,9 @@ %1 %2 in %3 calendar en %1 %2 in %3 +%1 days calendar en %1 days %1 event(s) %2 calendar en %1 event(s) %2 %1 event(s) %2, %3 failed because of insufficent rights !!! calendar en %1 event(s) %2, %3 failed because of insufficient rights! +%1 hours calendar en %1 hours +%1 minutes calendar en %1 minutes %1 participants removed because of missing invite grants calendar en %1 participants removed because of missing invite grants. %1 records imported calendar en %1 records imported. %1 records read (not yet imported, you may go back and uncheck test import) calendar en %1 records read. Not yet imported, go back and uncheck Test import. @@ -33,6 +36,7 @@ after current date calendar en After current date age: calendar en Age: alarm calendar en Alarm alarm added calendar en Alarm added. +alarm added automatic to new events before event start-time calendar en Alarm added automatic to new events before event start-time alarm deleted calendar en Alarm deleted. alarm for %1 at %2 in %3 calendar en Alarm for %1 at %2 in %3 alarm management calendar en Alarm management @@ -113,6 +117,7 @@ creator calendar en Creator csv calendar en CSV csv-fieldname calendar en CSV field name csv-filename calendar en CSV file name +custom calendar en Custom custom fields common en Custom fields custom_2 common en Free/Busy daily calendar en Daily @@ -125,6 +130,8 @@ days calendar en Days days of the week for a weekly repeated event calendar en Days of the week for a weekly repeated event days repeated calendar en Days repeated dayview calendar en Day view +default alarm for regular events calendar en Default alarm for regular events +default alarm for whole-day events calendar en Default alarm for whole-day events default appointment length (in minutes) calendar en Default appointment length in minutes default calendar filter calendar en Default calendar filter default calendar view calendar en Default calendar view @@ -314,6 +321,7 @@ maximum available quantity of %1 exceeded! calendar en Maximum available quantit meeting request calendar en Meeting request meetingrequest to all participants calendar en Meetingrequest to all participants merge document... calendar en Merge document... +midnight calendar en Midnight minutes calendar en Minutes modified calendar en Modified modifier calendar en Modifier diff --git a/calendar/setup/setup.inc.php b/calendar/setup/setup.inc.php index 4597aaade2..7ca0721410 100755 --- a/calendar/setup/setup.inc.php +++ b/calendar/setup/setup.inc.php @@ -38,6 +38,7 @@ $setup_info['calendar']['hooks']['admin'] = 'calendar_hooks::admin'; $setup_info['calendar']['hooks']['deleteaccount'] = 'calendar.calendar_so.deleteaccount'; $setup_info['calendar']['hooks']['home'] = 'calendar_hooks::home'; $setup_info['calendar']['hooks']['settings'] = 'calendar_hooks::settings'; +$setup_info['calendar']['hooks']['verify_settings'] = 'calendar_hooks::verify_settings'; $setup_info['calendar']['hooks']['sidebox_menu'] = 'calendar.calendar_ui.sidebox_menu'; $setup_info['calendar']['hooks']['search_link'] = 'calendar_hooks::search_link'; $setup_info['calendar']['hooks']['config_validate'] = 'calendar_hooks::config_validate'; diff --git a/preferences/inc/class.preferences_settings.inc.php b/preferences/inc/class.preferences_settings.inc.php index 117b6c9330..08336e8a33 100644 --- a/preferences/inc/class.preferences_settings.inc.php +++ b/preferences/inc/class.preferences_settings.inc.php @@ -140,12 +140,6 @@ class preferences_settings 'preferences' : $content['current_app']; egw_framework::includeCSS('preferences','app'); - $sel_options = $readonlys = null; - $content = $this->get_content($appname, $type, $sel_options, $readonlys, $preserve['types'], $tpl); - $preserve['appname'] = $preserve['old_appname'] = $content['appname']; - $preserve['type'] = $preserve['old_type'] = $content['type']; - if (isset($old_tab)) $content['tabs'] = $old_tab; - // if not just saved, call validation before, to be able to show failed validation of current prefs if (!isset($button)) { @@ -153,6 +147,13 @@ class preferences_settings $msg = $this->process_array($GLOBALS['egw']->preferences->$attribute, (array)$GLOBALS['egw']->preferences->{$attribute}[$appname], $preserve['types'], $appname, $attribute, true); } + + $sel_options = $readonlys = null; + $content = $this->get_content($appname, $type, $sel_options, $readonlys, $preserve['types'], $tpl); + $preserve['appname'] = $preserve['old_appname'] = $content['appname']; + $preserve['type'] = $preserve['old_type'] = $content['type']; + if (isset($old_tab)) $content['tabs'] = $old_tab; + if ($msg) egw_framework::message($msg, $msg_type ? $msg_type : 'error'); $tpl->exec('preferences.preferences_settings.index', $content, $sel_options, $readonlys, $preserve, 2); @@ -250,8 +251,9 @@ class preferences_settings // if(($error .= $GLOBALS['egw']->hooks->single(array( 'location' => 'verify_settings', - 'prefs' => $repository[$appname], - 'type' => $type + 'prefs' => &$repository[$appname], + 'type' => $type, + 'preprocess' => $only_verify, ), $appname )))