First shot at the birthdays calendar feature
This commit is contained in:
parent
01c6a75d16
commit
4112437fe9
5 changed files with 215 additions and 21 deletions
|
@ -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(
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2010, Lazlo Westerhof <hello@lazlo.me>
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2010, Lazlo Westerhof <hello@lazlo.me>
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@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;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
* @author Thomas Bruederli <bruederli@kolabsys.com>
|
||||
* @author Aleksander Machniak <machniak@kolabsys.com>
|
||||
*
|
||||
* Copyright (C) 2012, Kolab Systems AG <contact@kolabsys.com>
|
||||
* Copyright (C) 2012-2014, Kolab Systems AG <contact@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);
|
||||
|
||||
// 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;
|
||||
|
||||
if ($prefs['kolab_calendars'][$id])
|
||||
$this->rc->user->save_prefs($prefs);
|
||||
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
$id = $prop['id'];
|
||||
}
|
||||
|
||||
return 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 (isset($prop['color']))
|
||||
$prefs['kolab_calendars'][$id]['color'] = $prop['color'];
|
||||
if (isset($prop['showalarms']))
|
||||
$prefs['kolab_calendars'][$id]['showalarms'] = $prop['showalarms'] ? true : false;
|
||||
|
||||
if (!empty($prefs['kolab_calendars'][$id]))
|
||||
$this->rc->user->save_prefs($prefs);
|
||||
|
||||
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)))) {
|
||||
|
|
|
@ -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';
|
||||
|
||||
?>
|
||||
|
|
Loading…
Add table
Reference in a new issue