From 3be3065941f993df74d4112a6c3aeb9f03dbe780 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak Date: Thu, 13 Dec 2012 13:10:07 +0100 Subject: [PATCH] Delegation support in calendar event invitations handling --- plugins/calendar/calendar.php | 22 ++- .../calendar/drivers/kolab/kolab_driver.php | 10 ++ plugins/calendar/lib/calendar_itip.php | 6 +- plugins/kolab_delegation/kolab_delegation.js | 21 +++ plugins/kolab_delegation/kolab_delegation.php | 90 +++++++++++- .../kolab_delegation_engine.php | 128 +++++++++++++++++- 6 files changed, 261 insertions(+), 16 deletions(-) diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php index 24d929a3..eafb0154 100644 --- a/plugins/calendar/calendar.php +++ b/plugins/calendar/calendar.php @@ -209,9 +209,13 @@ class calendar extends rcube_plugin { if (!$this->itip) { require_once($this->home . '/lib/calendar_itip.php'); - $this->itip = new calendar_itip($this); + + $plugin = $this->rc->plugins->exec_hook('calendar_load_itip', + array('identity' => null)); + + $this->itip = new calendar_itip($this, $plugin['identity']); } - + return $this->itip; } @@ -727,7 +731,7 @@ class calendar extends rcube_plugin if ($numcals <= 1) $calendar_select = null; } - + if ($status == 'unknown') { $html = html::div('rsvp-status', $this->gettext('notanattendee')); $action = 'import'; @@ -738,7 +742,7 @@ class calendar extends rcube_plugin $action = ''; // nothing to do here } } - + $default_calendar = $calendar_select ? $this->get_default_calendar(true) : null; $this->rc->output->command('plugin.update_event_rsvp_status', array( 'uid' => $event['uid'], @@ -1974,7 +1978,15 @@ class calendar extends rcube_plugin */ private function get_user_emails() { - $emails = array($this->rc->user->get_username()); + $emails = array(); + $plugin = $this->rc->plugins->exec_hook('calendar_user_emails', array('emails' => $emails)); + $emails = $plugin['emails']; + + if ($plugin['abort']) { + return $emails; + } + + $emails[] = $this->rc->user->get_username(); foreach ($this->rc->user->list_identities() as $identity) $emails[] = $identity['email']; diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php index 4787445a..0d913a36 100644 --- a/plugins/calendar/drivers/kolab/kolab_driver.php +++ b/plugins/calendar/drivers/kolab/kolab_driver.php @@ -147,6 +147,16 @@ class kolab_driver extends calendar_driver protected function filter_calendars($writable = false, $active = false, $personal = false) { $calendars = array(); + + $plugin = $this->rc->plugins->exec_hook('calendar_list_filter', array( + 'list' => $this->calendars, 'calendars' => $calendars, + 'writable' => $writable, 'active' => $active, 'personal' => $personal, + )); + + if ($plugin['abort']) { + return $plugin['calendars']; + } + foreach ($this->calendars as $cal) { if (!$cal->ready) { continue; diff --git a/plugins/calendar/lib/calendar_itip.php b/plugins/calendar/lib/calendar_itip.php index dc4cfa0f..40d97869 100644 --- a/plugins/calendar/lib/calendar_itip.php +++ b/plugins/calendar/lib/calendar_itip.php @@ -28,14 +28,14 @@ class calendar_itip { private $rc; private $cal; - private $event; + private $sender; private $itip_send = false; - function __construct($cal) + function __construct($cal, $identity = null) { $this->cal = $cal; $this->rc = $cal->rc; - $this->sender = $this->rc->user->get_identity(); + $this->sender = $identity ? $identity : $this->rc->user->get_identity(); $this->cal->add_hook('smtp_connect', array($this, 'smtp_connect_hook')); } diff --git a/plugins/kolab_delegation/kolab_delegation.js b/plugins/kolab_delegation/kolab_delegation.js index 2462fa3c..75e77b87 100644 --- a/plugins/kolab_delegation/kolab_delegation.js +++ b/plugins/kolab_delegation/kolab_delegation.js @@ -22,6 +22,14 @@ */ window.rcmail && rcmail.addEventListener('init', function(evt) { + if (rcmail.env.task == 'mail') { + // set delegator context for calendar requests on invitation message + rcmail.addEventListener('requestcalendar/event', function(o) { rcmail.event_delegator_request(o); }); + rcmail.addEventListener('requestcalendar/mailimportevent', function(o) { rcmail.event_delegator_request(o); }); + } + else if (rcmail.env.task != 'settings') + return; + // add Delegation section to the list var tab = $('').attr('id', 'settingstabplugindelegation').addClass('tablink'), button = $('').attr('href', rcmail.env.comm_path+'&_action=plugin.delegation') @@ -218,3 +226,16 @@ rcube_webmail.prototype.delegate_save_complete = function(p) this.enable_command('delegate-delete', false); } }; + +rcube_webmail.prototype.event_delegator_request = function(data) +{ + if (!this.env.delegator_context) + return; + + if (typeof data === 'object') + data._context = this.env.delegator_context; + else + data += '&_context=' + this.env.delegator_context; + + return data; +}; diff --git a/plugins/kolab_delegation/kolab_delegation.php b/plugins/kolab_delegation/kolab_delegation.php index b7abfd42..6b37e3f3 100644 --- a/plugins/kolab_delegation/kolab_delegation.php +++ b/plugins/kolab_delegation/kolab_delegation.php @@ -25,7 +25,7 @@ class kolab_delegation extends rcube_plugin { - public $task = 'login|mail|settings'; + public $task = 'login|mail|settings|calendar'; private $rc; private $engine; @@ -41,10 +41,19 @@ class kolab_delegation extends rcube_plugin $this->require_plugin('libkolab'); $this->require_plugin('kolab_auth'); - $this->add_hook('login_after', array($this, 'login_hook')); + // on-login delegation initialization + $this->add_hook('login_after', array($this, 'login_hook')); + // on-check-recent delegation support $this->add_hook('check_recent', array($this, 'check_recent_hook')); + // delegation support in Calendar plugin + $this->add_hook('message_load', array($this, 'message_load')); + $this->add_hook('calendar_user_emails', array($this, 'calendar_user_emails')); + $this->add_hook('calendar_list_filter', array($this, 'calendar_list_filter')); + $this->add_hook('calendar_load_itip', array($this, 'calendar_load_itip')); + if ($this->rc->task == 'settings') { + // delegation management interface $this->register_action('plugin.delegation', array($this, 'controller_ui')); $this->register_action('plugin.delegation-delete', array($this, 'controller_action')); $this->register_action('plugin.delegation-save', array($this, 'controller_action')); @@ -105,7 +114,7 @@ class kolab_delegation extends rcube_plugin return $args; } - if (empty($_SESSION['delegator_uids'])) { + if (empty($_SESSION['delegators'])) { return $args; } @@ -113,7 +122,7 @@ class kolab_delegation extends rcube_plugin $other_ns = $storage->get_namespace('other'); $folders = $storage->list_folders_subscribed('', '*', 'mail'); - foreach ($_SESSION['delegator_uids'] as $uid) { + foreach (array_keys($_SESSION['delegators']) as $uid) { foreach ($other_ns as $ns) { $folder = $ns[0] . $uid; if (in_array($folder, $folders) && !in_array($folder, $args['folders'])) { @@ -125,6 +134,79 @@ class kolab_delegation extends rcube_plugin return $args; } + /** + * E-mail message loading action + */ + public function message_load($args) + { + // This is a place where we detect delegate context + // So we can handle event invitations on behalf of delegator + // @TODO: should we do this only in delegators' folders? + + $engine = $this->engine(); + $context = $engine->delegator_context_from_message($args['object']); + + if ($context) { + $this->rc->output->set_env('delegator_context', $context); + $this->include_script('kolab_delegation.js'); + } + + return $args; + } + + /** + * calendar::get_user_emails() handler + */ + public function calendar_user_emails($args) + { + // In delegator context we'll use delegator's addresses + // instead of current user addresses + + $engine = $this->engine(); + + if ($context = $engine->delegator_context()) { + $args['emails'] = $_SESSION['delegators'][$context]; + $args['abort'] = true; + } + + return $args; + } + + /** + * calendar_driver::list_calendars() handler + */ + public function calendar_list_filter($args) + { + // In delegator context we'll use delegator's folders + // instead of current user folders + + $engine = $this->engine(); + + if ($engine->delegator_context()) { + $args['calendars'] = $engine->delegator_folder_filter($args); + $args['abort'] = true; + } + + return $args; + } + + /** + * calendar::load_itip() handler + */ + public function calendar_load_itip($args) + { + // In delegator context we'll use delegator's address/name + // for invitation responses + + $engine = $this->engine(); + + if ($engine->delegator_context()) { + $args['identity'] = $engine->delegator_identity(); + } + + return $args; + } + /** * Delegation UI handler */ diff --git a/plugins/kolab_delegation/kolab_delegation_engine.php b/plugins/kolab_delegation/kolab_delegation_engine.php index 2bb6b707..f2a71253 100644 --- a/plugins/kolab_delegation/kolab_delegation_engine.php +++ b/plugins/kolab_delegation/kolab_delegation_engine.php @@ -25,6 +25,8 @@ class kolab_delegation_engine { + public $context; + private $rc; private $ldap_filter; private $ldap_delegate_field; @@ -532,9 +534,8 @@ class kolab_delegation_engine // for every delegator... foreach ($delegators as $delegator) { - $uids[] = $delegator['imap_uid']; - $email_arr = $delegator['email']; - $diff = array_intersect($emails, $email_arr); + $uids[$delegator['imap_uid']] = $email_arr = $delegator['email']; + $diff = array_intersect($emails, $email_arr); // identity with delegator's email already exist, do nothing if (count($diff)) { @@ -545,6 +546,8 @@ class kolab_delegation_engine // create identities for delegator emails foreach ($email_arr as $email) { $default['email'] = $email; + // @TODO: "Username" or "Delegatorname" or "Username on behalf of Delegatorname" + //$default['name'] = $delegator['email']; $this->rc->user->insert_identity($default); } @@ -584,7 +587,124 @@ class kolab_delegation_engine } } - $_SESSION['delegator_uids'] = $uids; + $_SESSION['delegators'] = $uids; + } + + /** + * Sets delegator context according to email message recipient + * + * @param rcube_message $message Email message object + */ + public function delegator_context_from_message($message) + { + if (empty($_SESSION['delegators'])) { + return; + } + + // Match delegators' addresses with message To: address + // @TODO: Is this reliable enough? + // Roundcube sends invitations to every attendee separately, + // but maybe there's a software which sends with CC header or many addresses in To: + + $emails = $message->get_header('to'); + $emails = rcube_mime::decode_address_list($emails, null, false); + + foreach ($emails as $email) { + foreach ($_SESSION['delegators'] as $uid => $addresses) { + if (in_array($email['mailto'], $addresses)) { + return $this->context = $uid; + } + } + } + } + + /** + * Return (set) current delegator context + * + * @return string Delegator UID + */ + public function delegator_context() + { + if (!$this->context && !empty($_SESSION['delegators'])) { + $context = rcube_utils::get_input_value('_context', rcube_utils::INPUT_GPC); + if ($context && isset($_SESSION['delegators'][$context])) { + $this->context = $context; + } + } + + return $this->context; + } + + /** + * Return identity of the current delegator + * + * @return array Identity data (name and email) + */ + public function delegator_identity() + { + if (!$this->context) { + return; + } + + $identities = $this->rc->user->list_identities(); + $emails = $_SESSION['delegators'][$this->context]; + + foreach ($identities as $ident) { + if (in_array($ident['email'], $emails)) { + return $ident; + } + } + } + + /** + * Filters list of calendars according to delegator context + * + * @param array $args Plugin hook arguments + * + * @return array List of calendars + */ + public function delegator_folder_filter($args) + { + if (empty($this->context)) { + return; + } + + $storage = $this->rc->get_storage(); + $other_ns = $storage->get_namespace('other'); + $delim = $storage->get_hierarchy_delimiter(); + $calendars = array(); + + // code parts derived from kolab_driver::filter_calendars() + foreach ($args['list'] as $cal) { + if (!$cal->ready) { + continue; + } + if ($args['writeable'] && $cal->readonly) { + continue; + } + if ($args['active'] && !$cal->storage->is_active()) { + continue; + } + if ($args['personal']) { + $ns = $cal->get_namespace(); + $name = $cal->get_realname(); // UTF-7 IMAP folder name + + if ($ns != 'other') { + continue; + } + + foreach ($other_ns as $ns) { + $folder = $ns[0] . $this->context . $delim; + if (strpos($name, $folder) !== 0) { + continue; + } + } + } + + $calendars[$cal->id] = $cal; + } + + return $calendars; } /**