From b8a8cc09271aa5aa6bcfc511d9467238b879807a Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Mon, 28 Apr 2014 13:23:11 +0200 Subject: [PATCH] Support recurring tasks in database driver (#2713) --- .../database/tasklist_database_driver.php | 61 +++++++++++++++++-- plugins/tasklist/drivers/tasklist_driver.php | 19 +++++- 2 files changed, 75 insertions(+), 5 deletions(-) diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php index dfb2c1bc..d9bf4145 100644 --- a/plugins/tasklist/drivers/database/tasklist_database_driver.php +++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php @@ -499,6 +499,11 @@ class tasklist_database_driver extends tasklist_driver unset($rec['alarms']); } + // decode serialze recurrence rules + if ($rec['recurrence']) { + $rec['recurrence'] = $this->unserialize_recurrence($rec['recurrence']); + } + unset($rec['task_id'], $rec['tasklist_id'], $rec['created']); return $rec; } @@ -520,8 +525,11 @@ class tasklist_database_driver extends tasklist_driver if (is_array($prop['valarms'])) { $prop['alarms'] = $this->serialize_alarms($prop['valarms']); } + if (is_array($prop['recurrence'])) { + $prop['recurrence'] = $this->serialize_recurrence($prop['recurrence']); + } - foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms') as $col) { + foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence') as $col) { if (empty($prop[$col])) $prop[$col] = null; } @@ -529,8 +537,8 @@ class tasklist_database_driver extends tasklist_driver $notify_at = $this->_get_notification($prop); $result = $this->rc->db->query(sprintf( "INSERT INTO " . $this->db_tasks . " - (tasklist_id, uid, parent_id, created, changed, title, date, time, startdate, starttime, description, tags, alarms, notify) - VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + (tasklist_id, uid, parent_id, created, changed, title, date, time, startdate, starttime, description, tags, flagged, complete, alarms, recurrence, notify) + VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", $this->rc->db->now(), $this->rc->db->now() ), @@ -544,7 +552,10 @@ class tasklist_database_driver extends tasklist_driver $prop['starttime'], strval($prop['description']), join(',', (array)$prop['tags']), + $prop['flagged'] ? 1 : 0, + intval($prop['complete']), $prop['alarms'], + $prop['recurrence'], $notify_at ); @@ -566,13 +577,16 @@ class tasklist_database_driver extends tasklist_driver if (is_array($prop['valarms'])) { $prop['alarms'] = $this->serialize_alarms($prop['valarms']); } + if (is_array($prop['recurrence'])) { + $prop['recurrence'] = $this->serialize_recurrence($prop['recurrence']); + } $sql_set = array(); foreach (array('title', 'description', 'flagged', 'complete') as $col) { if (isset($prop[$col])) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($prop[$col]); } - foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms') as $col) { + foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence') as $col) { if (isset($prop[$col])) $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col])); } @@ -734,6 +748,45 @@ class tasklist_database_driver extends tasklist_driver return $valarms; } + /** + * Helper method to serialize task recurrence properties + */ + private function serialize_recurrence($recurrence) + { + foreach ((array)$recurrence as $k => $val) { + if ($val instanceof DateTime) { + $recurrence[$k] = '@' . $val->format('c'); + } + } + + return $recurrence ? json_encode($recurrence) : null; + } + + /** + * Helper method to decode a serialized task recurrence struct + */ + private function unserialize_recurrence($ser) + { + if (strlen($ser)) { + $recurrence = json_decode($ser, true); + foreach ((array)$recurrence as $k => $val) { + if ($val[0] == '@') { + try { + $recurrence[$k] = new DateTime(substr($val, 1)); + } + catch (Exception $e) { + unset($recurrence[$k]); + } + } + } + } + else { + $recurrence = ''; + } + + return $recurrence; + } + /** * Handler for user_delete plugin hook */ diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php index c3b14231..6c31fa7a 100644 --- a/plugins/tasklist/drivers/tasklist_driver.php +++ b/plugins/tasklist/drivers/tasklist_driver.php @@ -42,7 +42,24 @@ * 'flagged' => 'Boolean value whether this record is flagged', * 'complete' => 'Float value representing the completeness state (range 0..1)', * 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential) - * 'alarms' => '-15M:DISPLAY', // Reminder settings inspired by valarm definition (e.g. display alert 15 minutes before due time) + * 'valarms' => array( // List of reminders (new format), each represented as a hash array: + * array( + * 'trigger' => '-PT90M', // ISO 8601 period string prefixed with '+' or '-', or DateTime object + * 'action' => 'DISPLAY|EMAIL|AUDIO', + * 'duration' => 'PT15M', // ISO 8601 period string + * 'repeat' => 0, // number of repetitions + * 'description' => '', // text to display for DISPLAY actions + * 'summary' => '', // message text for EMAIL actions + * 'attendees' => array(), // list of email addresses to receive alarm messages + * ), + * ), + * 'recurrence' => array( // Recurrence definition according to iCalendar (RFC 2445) specification as list of key-value pairs + * 'FREQ' => 'DAILY|WEEKLY|MONTHLY|YEARLY', + * 'INTERVAL' => 1...n, + * 'UNTIL' => DateTime, + * 'COUNT' => 1..n, // number of times + * 'RDATE' => array(), // complete list of DateTime objects denoting individual repeat dates + * ), * '_fromlist' => 'List identifier where the task was stored before', * ); */