plugins/calendar plugins/libcalendaring plugins/tasklist
Thomas Brüderli
bruederli at kolabsys.com
Thu Jul 31 18:22:21 CEST 2014
plugins/calendar/calendar.php | 138 +++++----------------------
plugins/libcalendaring/libcalendaring.php | 138 +++++++++++++++++++++++++++
plugins/tasklist/tasklist.php | 153 +++++-------------------------
3 files changed, 193 insertions(+), 236 deletions(-)
New commits:
commit 978c9023e5a2a94be4a1e1569d134a64ba9e474b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Jul 31 18:21:53 2014 +0200
Move iTip message parsing functionality to libcalendaring. Only parse iCal attachments once although used by calendar and tasks
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index ae1c4ce..9bc6be5 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -173,7 +173,6 @@ class calendar extends rcube_plugin
else if ($args['task'] == 'mail') {
// hooks to catch event invitations on incoming mails
if ($args['action'] == 'show' || $args['action'] == 'preview') {
- $this->add_hook('message_load', array($this, 'mail_message_load'));
$this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html'));
}
@@ -2287,7 +2286,7 @@ class calendar extends rcube_plugin
foreach ($p['messages'] as $i => $header) {
$part = new StdClass;
$part->mimetype = $header->ctype;
- if ($this->is_vcalendar($part)) {
+ if (libcalendaring::part_is_vcalendar($part)) {
$header->list_flags['attachmentClass'] = 'ical';
}
else if (in_array($header->ctype, array('multipart/alternative', 'multipart/mixed'))) {
@@ -2295,7 +2294,7 @@ class calendar extends rcube_plugin
if (!empty($header->structure) && is_array($header->structure->parts)) {
foreach ($header->structure->parts as $part) {
- if ($this->is_vcalendar($part) && !empty($part->ctype_parameters['method'])) {
+ if (libcalendaring::part_is_vcalendar($part) && !empty($part->ctype_parameters['method'])) {
$header->list_flags['attachmentClass'] = 'ical';
break;
}
@@ -2305,29 +2304,6 @@ class calendar extends rcube_plugin
}
}
}
-
- /**
- * Check mail message structure of there are .ics files attached
- */
- public function mail_message_load($p)
- {
- $this->message = $p['object'];
- $itip_part = null;
-
- // check all message parts for .ics files
- foreach ((array)$this->message->mime_parts as $part) {
- if ($this->is_vcalendar($part)) {
- if ($part->ctype_parameters['method'])
- $itip_part = $part->mime_id;
- else
- $this->ics_parts[] = $part->mime_id;
- }
- }
-
- // priorize part with method parameter
- if ($itip_part)
- $this->ics_parts = array($itip_part);
- }
/**
* Add UI element to copy event invitations or updates to the calendar
@@ -2335,47 +2311,38 @@ class calendar extends rcube_plugin
public function mail_messagebody_html($p)
{
// load iCalendar functions (if necessary)
- if (!empty($this->ics_parts)) {
+ if (!empty($this->lib->ical_parts)) {
$this->get_ical();
$this->load_itip();
}
$html = '';
$has_events = false;
- foreach ($this->ics_parts as $mime_id) {
- $part = $this->message->mime_parts[$mime_id];
- $charset = $part->ctype_parameters['charset'] ? $part->ctype_parameters['charset'] : RCMAIL_CHARSET;
- $events = $this->ical->import($this->message->get_part_content($mime_id), $charset);
- $title = $this->gettext('title');
-
- // successfully parsed events?
- if (empty($events))
- continue;
-
- // show a box for every event in the file
- foreach ($events as $idx => $event) {
- if ($event['_type'] != 'event') // skip non-event objects (#2928)
- continue;
-
- $has_events = true;
-
- // get prepared inline UI for this event object
- if ($this->ical->method) {
- $html .= html::div('calendar-invitebox',
- $this->itip->mail_itip_inline_ui(
- $event,
- $this->ical->method,
- $mime_id.':'.$idx,
- 'calendar',
- rcube_utils::anytodatetime($this->message->headers->date)
- )
- );
- }
+ $ical_objects = $this->lib->get_mail_ical_objects();
- // limit listing
- if ($idx >= 3)
- break;
+ // show a box for every event in the file
+ foreach ($ical_objects as $idx => $event) {
+ if ($event['_type'] != 'event') // skip non-event objects (#2928)
+ continue;
+
+ $has_events = true;
+
+ // get prepared inline UI for this event object
+ if ($ical_objects->method) {
+ $html .= html::div('calendar-invitebox',
+ $this->itip->mail_itip_inline_ui(
+ $event,
+ $ical_objects->method,
+ $ical_objects->mime_id . ':' . $idx,
+ 'calendar',
+ rcube_utils::anytodatetime($ical_objects->message_date)
+ )
+ );
}
+
+ // limit listing
+ if ($idx >= 3)
+ break;
}
// prepend event boxes to message body
@@ -2403,40 +2370,6 @@ class calendar extends rcube_plugin
return $p;
}
- /**
- * Read the given mime message from IMAP and parse ical data
- */
- private function mail_get_itip_event($mbox, $uid, $mime_id)
- {
- $charset = RCMAIL_CHARSET;
-
- // establish imap connection
- $imap = $this->rc->get_storage();
- $imap->set_mailbox($mbox);
-
- if ($uid && $mime_id) {
- list($mime_id, $index) = explode(':', $mime_id);
- $part = $imap->get_message_part($uid, $mime_id);
- if ($part->ctype_parameters['charset'])
- $charset = $part->ctype_parameters['charset'];
- $headers = $imap->get_message_headers($uid);
-
- if ($part) {
- $events = $this->get_ical()->import($part, $charset);
- }
- }
-
- // successfully parsed events?
- if (!empty($events) && ($event = $events[$index])) {
- // store the message's sender address for comparisons
- $event['_sender'] = preg_match('/([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))/', $headers->from, $m) ? $m[1] : '';
- $event['_sender_utf'] = rcube_idn_to_utf8($event['_sender']);
-
- return $event;
- }
-
- return null;
- }
/**
* Handler for POST request to import an event attached to a mail message
@@ -2454,7 +2387,7 @@ class calendar extends rcube_plugin
$success = false;
// successfully parsed events?
- if ($event = $this->mail_get_itip_event($mbox, $uid, $mime_id)) {
+ if ($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) {
// find writeable calendar to store event
$cal_id = !empty($_REQUEST['_folder']) ? get_input_value('_folder', RCUBE_INPUT_POST) : null;
$calendars = $this->driver->list_calendars(false, true);
@@ -2635,7 +2568,7 @@ class calendar extends rcube_plugin
$mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
$mime_id = get_input_value('_part', RCUBE_INPUT_POST);
- if (($event = $this->mail_get_itip_event($mbox, $uid, $mime_id)) && $this->ical->method == 'REPLY') {
+ if (($event = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'event')) && $event['_method'] == 'REPLY') {
$event['comment'] = get_input_value('_comment', RCUBE_INPUT_POST);
foreach ($event['attendees'] as $_attendee) {
@@ -2807,21 +2740,6 @@ class calendar extends rcube_plugin
return $args;
}
- /**
- * Checks if specified message part is a vcalendar data
- *
- * @param rcube_message_part Part object
- * @return boolean True if part is of type vcard
- */
- private function is_vcalendar($part)
- {
- return (
- in_array($part->mimetype, array('text/calendar', 'text/x-vcalendar', 'application/ics')) ||
- // Apple sends files as application/x-any (!?)
- ($part->mimetype == 'application/x-any' && $part->filename && preg_match('/\.ics$/i', $part->filename))
- );
- }
-
/**
* Get a list of email addresses of the current user (from login and identities)
diff --git a/plugins/libcalendaring/libcalendaring.php b/plugins/libcalendaring/libcalendaring.php
index a8b70cb..6eae2df 100644
--- a/plugins/libcalendaring/libcalendaring.php
+++ b/plugins/libcalendaring/libcalendaring.php
@@ -35,6 +35,8 @@ class libcalendaring extends rcube_plugin
public $gmt_offset;
public $dst_active;
public $timezone_offset;
+ public $ical_parts = array();
+ public $ical_message;
public $defaults = array(
'calendar_date_format' => "yyyy-MM-dd",
@@ -57,6 +59,8 @@ class libcalendaring extends rcube_plugin
private static $instance;
+ private $mail_ical_parser;
+
/**
* Singleton getter to allow direct access from other plugins
*/
@@ -102,6 +106,21 @@ class libcalendaring extends rcube_plugin
$this->add_hook('refresh', array($this, 'refresh'));
$this->register_action('plugin.alarms', array($this, 'alarms_action'));
}
+
+ // proceed initialization in startup hook
+ $this->add_hook('startup', array($this, 'startup'));
+ }
+
+ /**
+ * Startup hook
+ */
+ public function startup($args)
+ {
+ if ($args['task'] == 'mail') {
+ if ($args['action'] == 'show' || $args['action'] == 'preview') {
+ $this->add_hook('message_load', array($this, 'mail_message_load'));
+ }
+ }
}
/**
@@ -1220,6 +1239,125 @@ class libcalendaring extends rcube_plugin
}
+ /********* iTip message detection *********/
+
+ /**
+ * Check mail message structure of there are .ics files attached
+ */
+ public function mail_message_load($p)
+ {
+ $this->ical_message = $p['object'];
+ $itip_part = null;
+
+ // check all message parts for .ics files
+ foreach ((array)$this->ical_message->mime_parts as $part) {
+ if (self::part_is_vcalendar($part)) {
+ if ($part->ctype_parameters['method'])
+ $itip_part = $part->mime_id;
+ else
+ $this->ical_parts[] = $part->mime_id;
+ }
+ }
+
+ // priorize part with method parameter
+ if ($itip_part) {
+ $this->ical_parts = array($itip_part);
+ }
+ }
+
+ /**
+ * Getter for the parsed iCal objects attached to the current email message
+ *
+ * @return object libvcalendar parser instance with the parsed objects
+ */
+ public function get_mail_ical_objects()
+ {
+ // create parser and load ical objects
+ if (!$this->mail_ical_parser) {
+ $this->mail_ical_parser = $this->get_ical();
+
+ foreach ($this->ical_parts as $mime_id) {
+ $part = $this->ical_message->mime_parts[$mime_id];
+ $charset = $part->ctype_parameters['charset'] ?: RCMAIL_CHARSET;
+ $this->mail_ical_parser->import($this->ical_message->get_part_content($mime_id), $charset);
+
+ // stop on the part that has an iTip method specified
+ if (count($this->mail_ical_parser->objects) && $this->mail_ical_parser->method) {
+ $this->mail_ical_parser->message_date = $this->ical_message->headers->date;
+ $this->mail_ical_parser->mime_id = $mime_id;
+ break;
+ }
+ }
+ }
+
+ return $this->mail_ical_parser;
+ }
+
+ /**
+ * Read the given mime message from IMAP and parse ical data
+ *
+ * @param string Mailbox name
+ * @param string Message UID
+ * @param string Message part ID and object index (e.g. '1.2:0')
+ * @param string Object type filter (optional)
+ *
+ * @return array Hash array with the parsed iCal
+ */
+ public function mail_get_itip_object($mbox, $uid, $mime_id, $type = null)
+ {
+ $charset = RCMAIL_CHARSET;
+
+ // establish imap connection
+ $imap = $this->rc->get_storage();
+ $imap->set_mailbox($mbox);
+
+ if ($uid && $mime_id) {
+ list($mime_id, $index) = explode(':', $mime_id);
+
+ $part = $imap->get_message_part($uid, $mime_id);
+ $headers = $imap->get_message_headers($uid);
+ $parser = $this->get_ical();
+
+ if ($part->ctype_parameters['charset']) {
+ $charset = $part->ctype_parameters['charset'];
+ }
+
+ if ($part) {
+ $objects = $parser->import($part, $charset);
+ }
+ }
+
+ // successfully parsed events/tasks?
+ if (!empty($objects) && ($object = $objects[$index]) && (!$type || $object['_type'] == $type)) {
+ if ($parser->method)
+ $object['_method'] = $parser->method;
+
+ // store the message's sender address for comparisons
+ $object['_sender'] = preg_match('/([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))/', $headers->from, $m) ? $m[1] : '';
+ $object['_sender_utf'] = rcube_idn_to_utf8($object['_sender']);
+
+ return $object;
+ }
+
+ return null;
+ }
+
+ /**
+ * Checks if specified message part is a vcalendar data
+ *
+ * @param rcube_message_part Part object
+ * @return boolean True if part is of type vcard
+ */
+ public static function part_is_vcalendar($part)
+ {
+ return (
+ in_array($part->mimetype, array('text/calendar', 'text/x-vcalendar', 'application/ics')) ||
+ // Apple sends files as application/x-any (!?)
+ ($part->mimetype == 'application/x-any' && $part->filename && preg_match('/\.ics$/i', $part->filename))
+ );
+ }
+
+
/********* Static utility functions *********/
/**
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 9aca0df..29d30c8 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -123,7 +123,6 @@ class tasklist extends rcube_plugin
}
else if ($args['task'] == 'mail') {
if ($args['action'] == 'show' || $args['action'] == 'preview') {
- $this->add_hook('message_load', array($this, 'mail_message_load'));
$this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html'));
}
@@ -1337,84 +1336,44 @@ class tasklist extends rcube_plugin
}
/**
- * Check mail message structure of there are .ics files attached
- *
- * @todo move to libcalendaring
- */
- public function mail_message_load($p)
- {
- $this->message = $p['object'];
- $itip_part = null;
-
- // check all message parts for .ics files
- foreach ((array)$this->message->mime_parts as $part) {
- if ($this->is_vcalendar($part)) {
- if ($part->ctype_parameters['method'])
- $itip_part = $part->mime_id;
- else
- $this->ics_parts[] = $part->mime_id;
- }
- }
-
- // priorize part with method parameter
- if ($itip_part) {
- $this->ics_parts = array($itip_part);
- }
- }
-
- /**
- * Add UI element to copy event invitations or updates to the calendar
- *
- * @todo move to libcalendaring
+ * Add UI element to copy task invitations or updates to the tasklist
*/
public function mail_messagebody_html($p)
{
// load iCalendar functions (if necessary)
- if (!empty($this->ics_parts)) {
+ if (!empty($this->lib->ical_parts)) {
$this->get_ical();
$this->load_itip();
}
- // @todo: Calendar plugin does the same, which means the
- // attachment body is fetched twice, this is not optimal
$html = '';
$has_tasks = false;
- foreach ($this->ics_parts as $mime_id) {
- $part = $this->message->mime_parts[$mime_id];
- $charset = $part->ctype_parameters['charset'] ? $part->ctype_parameters['charset'] : RCMAIL_CHARSET;
- $objects = $this->ical->import($this->message->get_part_content($mime_id), $charset);
- $title = $this->gettext('title');
-
- // successfully parsed events?
- if (empty($objects)) {
+ $ical_objects = $this->lib->get_mail_ical_objects();
+
+ // show a box for every task in the file
+ foreach ($ical_objects as $idx => $task) {
+ if ($task['_type'] != 'task') {
continue;
}
- // show a box for every task in the file
- foreach ($objects as $idx => $task) {
- if ($task['_type'] != 'task') {
- continue;
- }
-
- $has_tasks = true;
-
- // get prepared inline UI for this event object
- if ($this->ical->method) {
- $html .= html::div('tasklist-invitebox',
- $this->itip->mail_itip_inline_ui(
- $task,
- $this->ical->method,
- $mime_id . ':' . $idx,
- 'tasks',
- rcube_utils::anytodatetime($this->message->headers->date)
- )
- );
- }
+ $has_tasks = true;
+
+ // get prepared inline UI for this event object
+ if ($ical_objects->method) {
+ $html .= html::div('tasklist-invitebox',
+ $this->itip->mail_itip_inline_ui(
+ $task,
+ $ical_objects->method,
+ $ical_objects->mime_id . ':' . $idx,
+ 'tasks',
+ rcube_utils::anytodatetime($ical_objects->message_date)
+ )
+ );
+ }
- // limit listing
- if ($idx >= 3) {
- break;
- }
+ // limit listing
+ if ($idx >= 3) {
+ break;
}
}
@@ -1447,66 +1406,6 @@ class tasklist extends rcube_plugin
}
/**
- * Read the given mime message from IMAP and parse ical data
- *
- * @todo move to libcalendaring
- */
- private function mail_get_itip_task($mbox, $uid, $mime_id)
- {
- $charset = RCMAIL_CHARSET;
-
- // establish imap connection
- $imap = $this->rc->get_storage();
- $imap->set_mailbox($mbox);
-
- if ($uid && $mime_id) {
- list($mime_id, $index) = explode(':', $mime_id);
-
- $part = $imap->get_message_part($uid, $mime_id);
- $headers = $imap->get_message_headers($uid);
-
- if ($part->ctype_parameters['charset']) {
- $charset = $part->ctype_parameters['charset'];
- }
-
- if ($part) {
- $tasks = $this->get_ical()->import($part, $charset);
- }
- }
-
- // successfully parsed events?
- if (!empty($tasks) && ($task = $tasks[$index])) {
- $task = $this->from_ical($task);
-
- // store the message's sender address for comparisons
- $task['_sender'] = preg_match('/([a-z0-9][a-z0-9\-\.\+\_]*@[^&@"\'.][^@&"\']*\\.([^\\x00-\\x40\\x5b-\\x60\\x7b-\\x7f]{2,}|xn--[a-z0-9]{2,}))/', $headers->from, $m) ? $m[1] : '';
- $askt['_sender_utf'] = rcube_idn_to_utf8($task['_sender']);
-
- return $task;
- }
-
- return null;
- }
-
- /**
- * Checks if specified message part is a vcalendar data
- *
- * @param rcube_message_part Part object
- *
- * @return boolean True if part is of type vcard
- *
- * @todo move to libcalendaring
- */
- private function is_vcalendar($part)
- {
- return (
- in_array($part->mimetype, array('text/calendar', 'text/x-vcalendar', 'application/ics')) ||
- // Apple sends files as application/x-any (!?)
- ($part->mimetype == 'application/x-any' && $part->filename && preg_match('/\.ics$/i', $part->filename))
- );
- }
-
- /**
* Load iCalendar functions
*/
public function get_ical()
@@ -1624,7 +1523,9 @@ class tasklist extends rcube_plugin
$success = false;
// successfully parsed tasks?
- if ($task = $this->mail_get_itip_task($mbox, $uid, $mime_id)) {
+ if ($task = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'task')) {
+ $task = $this->from_ical($task);
+
// find writeable list to store the task
$list_id = !empty($_REQUEST['_list']) ? rcube_utils::get_input_value('_list', rcube_utils::INPUT_POST) : null;
$lists = $this->driver->get_lists();
More information about the commits
mailing list