plugins/calendar
Thomas Brüderli
bruederli at kolabsys.com
Mon Jan 27 19:13:23 CET 2014
plugins/calendar/config.inc.php.dist | 3
plugins/calendar/drivers/calendar_driver.php | 119 +++++++++++++++++-
plugins/calendar/drivers/database/database_driver.php | 42 ++++++
plugins/calendar/drivers/kolab/kolab_driver.php | 63 +++++++--
plugins/calendar/localization/en_US.inc | 5
5 files changed, 213 insertions(+), 19 deletions(-)
New commits:
commit 4112437fe983c51b5640a4a15beed00599b50089
Author: Thomas Bruederli <thomas at roundcube.net>
Date: Mon Jan 27 19:12:29 2014 +0100
First shot at the birthdays calendar feature
diff --git a/plugins/calendar/config.inc.php.dist b/plugins/calendar/config.inc.php.dist
index 34b1009..9a472a7 100644
--- a/plugins/calendar/config.inc.php.dist
+++ b/plugins/calendar/config.inc.php.dist
@@ -31,6 +31,9 @@ $rcmail_config['calendar_driver'] = "database";
// default calendar view (agendaDay, agendaWeek, month)
$rcmail_config['calendar_default_view'] = "agendaWeek";
+// show a birthdays calendar from the user's address book(s)
+$rcmail_config['calendar_contact_birthdays'] = false;
+
// mapping of Roundcube date formats to calendar formats (long/short/agenda)
// should be in sync with 'date_formats' in main config
$rcmail_config['calendar_date_format_sets'] = array(
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index c09d8b9..0eedf7f 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -8,7 +8,7 @@
* @author Thomas Bruederli <bruederli at kolabsys.com>
*
* Copyright (C) 2010, Lazlo Westerhof <hello at lazlo.me>
- * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2012-2014, Kolab Systems AG <contact at kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -81,6 +81,8 @@
*/
abstract class calendar_driver
{
+ const BIRTHDAY_CALENDAR_ID = '__bdays__';
+
// features supported by backend
public $alarms = false;
public $attendees = false;
@@ -398,7 +400,120 @@ abstract class calendar_driver
*/
public function get_color_values()
{
- return false;
+ return false;
+ }
+
+ /**
+ * Compose a list of birthday events from the contact records in the user's address books.
+ *
+ * This is a default implementation using Roundcube's address book API.
+ * It can be overriden with a more optimized version by the individual drivers.
+ *
+ * @param integer Event's new start (unix timestamp)
+ * @param integer Event's new end (unix timestamp)
+ * @param string Search query (optional)
+ * @return array A list of event records
+ */
+ public function load_birthday_events($start, $end, $search = null)
+ {
+ // convert to DateTime for comparisons
+ $start = new DateTime('@'.$start);
+ $end = new DateTime('@'.$end);
+ // extract the current year
+ $year = $start->format('Y');
+ $year2 = $end->format('Y');
+
+ $events = array();
+ $search = mb_strtolower($search);
+ $rcmail = rcmail::get_instance();
+ $cache = $rcmail->get_cache('calendar.birthdays', 'db', 3600);
+ $cache->expunge();
+
+ // TODO: let the user select the address books to consider in prefs
+ foreach ($rcmail->get_address_sources(false, true) as $source) {
+ $abook = $rcmail->get_address_book($source['id']);
+ $abook->set_pagesize(10000);
+
+ // skip LDAP address books (really?)
+ if ($abook instanceof rcube_ldap) {
+ continue;
+ }
+
+ // check for cached results
+ $cache_records = array();
+ $cached = $cache->get($source['id']);
+
+ // iterate over (cached) contacts
+ foreach ((array)($cached ?: $abook->list_records()) as $contact) {
+ if (!empty($contact['birthday'])) {
+ try {
+ if (is_array($contact['birthday']))
+ $contact['birthday'] = reset($contact['birthday']);
+
+ $bday = $contact['birthday'] instanceof DateTime ? $contact['birthday'] :
+ new DateTime($contact['birthday'], new DateTimezone('UTC'));
+ $birthyear = $bday->format('Y');
+ }
+ catch (Exception $e) {
+ // console('BIRTHDAY PARSE ERROR: ' . $e);
+ continue;
+ }
+
+ $display_name = rcube_addressbook::compose_display_name($contact);
+ $event_title = $rcmail->gettext(array('name' => 'birthdayeventtitle', 'vars' => array('name' => $display_name)), 'calendar');
+
+ // add stripped record to cache
+ if (empty($cached)) {
+ $cache_records[] = array(
+ 'id' => $contact['ID'],
+ 'name' => $display_name,
+ 'birthday' => $bday->format('Y-m-d'),
+ );
+ }
+
+ // filter by search term (only name is involved here)
+ if (!empty($search) && strpos(mb_strtolower($event_title), $search) === false) {
+ continue;
+ }
+
+ // quick-and-dirty recurrence computation: just replace the year
+ $bday->setDate($year, $bday->format('n'), $bday->format('j'));
+ $bday->setTime(12, 0, 0);
+
+ // date range reaches over multiple years: use end year if not in range
+ if (($bday > $end || $bday < $start) && $year2 != $year) {
+ $bday->setDate($year2, $bday->format('n'), $bday->format('j'));
+ $year = $year2;
+ }
+
+ // birthday is within requested range
+ if ($bday <= $end && $bday >= $start) {
+ $age = $year - $birthyear;
+ $event = array(
+ 'id' => md5('bday_' . $contact['id']),
+ 'calendar' => self::BIRTHDAY_CALENDAR_ID,
+ 'title' => $event_title,
+ 'description' => $rcmail->gettext(array('name' => 'birthdayage', 'vars' => array('age' => $age)), 'calendar'),
+ // Add more contact information to description block?
+ 'allday' => true,
+ 'start' => $bday,
+ // TODO: add alarms (configurable?)
+ );
+ $event['end'] = clone $bday;
+ $event['end']->add(new DateInterval('PT1H'));
+
+ $events[] = $event;
+ }
+ }
+ }
+
+ // store collected contacts in cache
+ if (empty($cached)) {
+ $cache->write($source['id'], $cache_records);
+ }
+ }
+
+ return $events;
}
}
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 43c2e1b..18fed04 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -8,7 +8,7 @@
* @author Thomas Bruederli <bruederli at kolabsys.com>
*
* Copyright (C) 2010, Lazlo Westerhof <hello at lazlo.me>
- * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2012-2014, Kolab Systems AG <contact at kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -127,6 +127,28 @@ class database_driver extends calendar_driver
// 'personal' is unsupported in this driver
+ // append the virtual birthdays calendar
+ if ($this->rc->config->get('calendar_contact_birthdays', false)) {
+ $prefs = $this->rc->config->get('birthday_calendar', array('color' => '87CEFA'));
+ $hidden = array_filter(explode(',', $this->rc->config->get('hidden_calendars', '')));
+
+ $id = self::BIRTHDAY_CALENDAR_ID;
+ if (!$active || !in_array($id, $hidden)) {
+ $calendars[$id] = array(
+ 'id' => $id,
+ 'name' => $this->cal->gettext('birthdays'),
+ 'listname' => $this->cal->gettext('birthdays'),
+ 'color' => $prefs['color'],
+ 'showalarms' => $prefs['showalarms'],
+ 'active' => !in_array($id, $hidden),
+ 'class_name' => 'birthdays',
+ 'readonly' => true,
+ 'default' => false,
+ 'children' => false,
+ );
+ }
+ }
+
return $calendars;
}
@@ -163,6 +185,17 @@ class database_driver extends calendar_driver
*/
public function edit_calendar($prop)
{
+ // birthday calendar properties are saved in user prefs
+ if ($prop['id'] == self::BIRTHDAY_CALENDAR_ID) {
+ $prefs = $this->rc->config->get('birthday_calendar', array('color' => '87CEFA'));
+ if (isset($prop['color']))
+ $prefs['color'] = $prop['color'];
+ if (isset($prop['showalarms']))
+ $prefs['showalarms'] = $prop['showalarms'] ? true : false;
+ $this->rc->user->save_prefs(array('birthday_calendar' => $prefs));
+ return true;
+ }
+
$query = $this->rc->db->query(
"UPDATE " . $this->db_calendars . "
SET name=?, color=?, showalarms=?
@@ -777,7 +810,12 @@ class database_driver extends calendar_driver
$events[] = $this->_read_postprocess($event);
}
}
-
+
+ // add events from the address books birthday calendar
+ if (in_array(self::BIRTHDAY_CALENDAR_ID, $calendars)) {
+ $events = array_merge($events, $this->load_birthday_events($start, $end, $search));
+ }
+
return $events;
}
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index f673f6c..87a5f02 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -7,7 +7,7 @@
* @author Thomas Bruederli <bruederli at kolabsys.com>
* @author Aleksander Machniak <machniak at kolabsys.com>
*
- * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2012-2014, Kolab Systems AG <contact at kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -145,6 +145,26 @@ class kolab_driver extends calendar_driver
}
}
+ // append the virtual birthdays calendar
+ if ($this->rc->config->get('calendar_contact_birthdays', false)) {
+ $id = self::BIRTHDAY_CALENDAR_ID;
+ $prefs = $this->rc->config->get('kolab_calendars', array()); // read local prefs
+ if (!$active || $prefs[$id]['active']) {
+ $calendars[$id] = array(
+ 'id' => $id,
+ 'name' => $this->cal->gettext('birthdays'),
+ 'listname' => $this->cal->gettext('birthdays'),
+ 'color' => $prefs[$id]['color'],
+ 'showalarms' => $prefs[$id]['showalarms'],
+ 'active' => $prefs[$id]['active'],
+ 'class_name' => 'birthdays',
+ 'readonly' => true,
+ 'default' => false,
+ 'children' => false,
+ );
+ }
+ }
+
return $calendars;
}
@@ -245,23 +265,24 @@ class kolab_driver extends calendar_driver
// create ID
$id = kolab_storage::folder_id($newfolder);
+ }
+ else {
+ $id = $prop['id'];
+ }
- // fallback to local prefs
- $prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
- unset($prefs['kolab_calendars'][$prop['id']]);
-
- if (isset($prop['color']))
- $prefs['kolab_calendars'][$id]['color'] = $prop['color'];
- if (isset($prop['showalarms']))
- $prefs['kolab_calendars'][$id]['showalarms'] = $prop['showalarms'] ? true : false;
+ // fallback to local prefs
+ $prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
+ unset($prefs['kolab_calendars'][$prop['id']]['color'], $prefs['kolab_calendars'][$prop['id']]['showalarms']);
- if ($prefs['kolab_calendars'][$id])
- $this->rc->user->save_prefs($prefs);
+ if (isset($prop['color']))
+ $prefs['kolab_calendars'][$id]['color'] = $prop['color'];
+ if (isset($prop['showalarms']))
+ $prefs['kolab_calendars'][$id]['showalarms'] = $prop['showalarms'] ? true : false;
- return true;
- }
+ if (!empty($prefs['kolab_calendars'][$id]))
+ $this->rc->user->save_prefs($prefs);
- return false;
+ return true;
}
@@ -275,6 +296,13 @@ class kolab_driver extends calendar_driver
if ($prop['id'] && ($cal = $this->calendars[$prop['id']])) {
return $cal->storage->activate($prop['active']);
}
+ else {
+ // save state in local prefs
+ $prefs['kolab_calendars'] = $this->rc->config->get('kolab_calendars', array());
+ $prefs['kolab_calendars'][$prop['id']]['active'] = (bool)$prop['active'];
+ $this->rc->user->save_prefs($prefs);
+ return true;
+ }
return false;
}
@@ -724,7 +752,12 @@ class kolab_driver extends calendar_driver
$events = array_merge($events, $this->calendars[$cid]->list_events($start, $end, $search, $virtual, $query));
$categories += $this->calendars[$cid]->categories;
}
-
+
+ // add events from the address books birthday calendar
+ if (in_array(self::BIRTHDAY_CALENDAR_ID, $calendars)) {
+ $events = array_merge($events, $this->load_birthday_events($start, $end, $search));
+ }
+
// add new categories to user prefs
$old_categories = $this->rc->config->get('calendar_categories', $this->default_categories);
if ($newcats = array_diff(array_map('strtolower', array_keys($categories)), array_map('strtolower', array_keys($old_categories)))) {
diff --git a/plugins/calendar/localization/en_US.inc b/plugins/calendar/localization/en_US.inc
index 5c41ed1..c582db9 100644
--- a/plugins/calendar/localization/en_US.inc
+++ b/plugins/calendar/localization/en_US.inc
@@ -238,4 +238,9 @@ $labels['futurevents'] = 'Future';
$labels['allevents'] = 'All';
$labels['saveasnew'] = 'Save as new';
+// birthdays calendar
+$labels['birthdays'] = 'Birthdays';
+$labels['birthdayeventtitle'] = '$name\'s Birthday';
+$labels['birthdayage'] = 'Age $age';
+
?>
More information about the commits
mailing list