diff --git a/plugins/libcalendaring/libcalendaring.js b/plugins/libcalendaring/libcalendaring.js
index f06b55a0..d04a024c 100644
--- a/plugins/libcalendaring/libcalendaring.js
+++ b/plugins/libcalendaring/libcalendaring.js
@@ -863,7 +863,7 @@ rcube_libcalendaring.itip_delegate_dialog = function(callback, selector)
rcmail.gettext('itip.itipcomment') + '">' +
'' +
'
' +
- (selector ? selector.html() : '') +
+ (selector && selector.length ? selector.html() : '') +
'
' +
'';
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index 8593fe5b..68741210 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -153,6 +153,8 @@ $labels['itipmailbodydeclined'] = "\$sender has declined the assignment to the f
$labels['itipmailbodycancel'] = "\$sender has rejected your assignment to the following task:\n\n*\$title*\n\nDue: \$date";
$labels['itipmailbodyin-process'] = "\$sender has set the status of the following task to in-process:\n\n*\$title*\n\nDue: \$date";
$labels['itipmailbodycompleted'] = "\$sender has completed the following task:\n\n*\$title*\n\nDue: \$date";
+$labels['itipmailbodydelegated'] = "\$sender has delegated the following task:\n\n*\$title*\n\nDue: \$date";
+$labels['itipmailbodydelegatedto'] = "\$sender has delegated the following task to you:\n\n*\$title*\n\nDue: \$date";
$labels['attendeeaccepted'] = 'Assignee has accepted';
$labels['attendeetentative'] = 'Assignee has tentatively accepted';
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 7683cc5f..f107a39c 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -533,17 +533,41 @@ function rcube_tasklist_ui(settings)
}
});
- // init RSVP widget
- $('#task-rsvp input.button').click(function(e) {
- var response = $(this).attr('rel');
-
+ /**
+ *
+ */
+ function task_rsvp(response, delegate)
+ {
if (me.selected_task && me.selected_task.attendees && response) {
+ // bring up delegation dialog
+ if (response == 'delegated' && !delegate) {
+ rcube_libcalendaring.itip_delegate_dialog(function(data) {
+ $('#reply-comment-task-rsvp').val(data.comment);
+ data.rsvp = data.rsvp ? 1 : '';
+ task_rsvp('delegated', data);
+ });
+ return;
+ }
+
// update attendee status
for (var data, i=0; i < me.selected_task.attendees.length; i++) {
data = me.selected_task.attendees[i];
if (settings.identity.emails.indexOf(';'+String(data.email).toLowerCase()) >= 0) {
data.status = response.toUpperCase();
- delete data.rsvp; // unset RSVP flag
+
+ if (data.status == 'DELEGATED') {
+ data['delegated-to'] = delegate.to;
+ }
+ else {
+ delete data.rsvp; // unset RSVP flag
+
+ if (data['delegated-to']) {
+ delete data['delegated-to'];
+ if (data.role == 'NON-PARTICIPANT' && data.status != 'DECLINED') {
+ data.role = 'REQ-PARTICIPANT';
+ }
+ }
+ }
}
}
@@ -551,7 +575,7 @@ function rcube_tasklist_ui(settings)
saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
rcmail.http_post('tasks/task', {
action: 'rsvp',
- t: me.selected_task,
+ t: $.extend({}, me.selected_task, (delegate || {})),
filter: filtermask,
status: response,
noreply: $('#noreply-task-rsvp:checked').length ? 1 : 0,
@@ -560,6 +584,11 @@ function rcube_tasklist_ui(settings)
task_show_dialog(me.selected_task.id);
}
+ }
+
+ // init RSVP widget
+ $('#task-rsvp input.button').click(function(e) {
+ task_rsvp($(this).attr('rel'))
});
// register click handler for message links
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 5f34e86c..0ddba7dc 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -120,6 +120,7 @@ class tasklist extends rcube_plugin
$this->register_action('itip-status', array($this, 'task_itip_status'));
$this->register_action('itip-remove', array($this, 'task_itip_remove'));
$this->register_action('itip-decline-reply', array($this, 'mail_itip_decline_reply'));
+ $this->register_action('itip-delegate', array($this, 'mail_itip_delegate'));
$this->add_hook('refresh', array($this, 'refresh'));
$this->collapsed_tasks = array_filter(explode(',', $this->rc->config->get('tasklist_collapsed_tasks', '')));
@@ -245,6 +246,7 @@ class tasklist extends rcube_plugin
}
case 'edit':
+ $oldrec = $this->driver->get_task($rec);
$rec = $this->prepare_task($rec);
$clone = $this->handle_recurrence($rec, $this->driver->get_task($rec));
if ($success = $this->driver->edit_task($rec)) {
@@ -357,13 +359,24 @@ class tasklist extends rcube_plugin
case 'rsvp':
$status = rcube_utils::get_input_value('status', rcube_utils::INPUT_GPC);
+ $noreply = intval(rcube_utils::get_input_value('noreply', rcube_utils::INPUT_GPC)) || $status == 'needs-action';
$task = $this->driver->get_task($rec);
$task['attendees'] = $rec['attendees'];
+ $task['_type'] = 'task';
+
+ // send invitation to delegatee + add it as attendee
+ if ($status == 'delegated' && $rec['to']) {
+ $itip = $this->load_itip();
+ if ($itip->delegate_to($task, $rec['to'], (bool)$rec['rsvp'])) {
+ $this->rc->output->show_message('tasklist.itipsendsuccess', 'confirmation');
+ $refresh[] = $task;
+ $noreply = false;
+ }
+ }
+
$rec = $task;
if ($success = $this->driver->edit_task($rec)) {
- $noreply = intval(rcube_utils::get_input_value('noreply', rcube_utils::INPUT_GPC)) || $status == 'needs-action';
-
if (!$noreply) {
// let the reply clause further down send the iTip message
$rec['_reportpartstat'] = $status;
@@ -439,7 +452,7 @@ class tasklist extends rcube_plugin
if (!$this->itip) {
require_once realpath(__DIR__ . '/../libcalendaring/lib/libcalendaring_itip.php');
$this->itip = new libcalendaring_itip($this, 'tasklist');
- $this->itip->set_rsvp_actions(array('accepted','declined'));
+ $this->itip->set_rsvp_actions(array('accepted','declined','delegated'));
$this->itip->set_rsvp_status(array('accepted','tentative','declined','delegated','in-process','completed'));
}
@@ -1701,15 +1714,45 @@ class tasklist extends rcube_plugin
$error_msg = $this->gettext('errorimportingtask');
$success = false;
+ $delegate = null;
+
+ if ($status == 'delegated') {
+ $delegates = rcube_mime::decode_address_list(rcube_utils::get_input_value('_to', rcube_utils::INPUT_POST, true), 1, false);
+ $delegate = reset($delegates);
+
+ if (empty($delegate) || empty($delegate['mailto'])) {
+ $this->rc->output->command('display_message', $this->gettext('libcalendaring.delegateinvalidaddress'), 'error');
+ return;
+ }
+ }
// successfully parsed tasks?
if ($task = $this->lib->mail_get_itip_object($mbox, $uid, $mime_id, 'task')) {
$task = $this->from_ical($task);
+ // forward iTip request to delegatee
+ if ($delegate) {
+ $rsvpme = intval(rcube_utils::get_input_value('_rsvp', rcube_utils::INPUT_POST));
+
+ $itip = $this->load_itip();
+ if ($itip->delegate_to($task, $delegate, $rsvpme ? true : false)) {
+ $this->rc->output->show_message('tasklist.itipsendsuccess', 'confirmation');
+ }
+ else {
+ $this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
+ }
+ }
+
// find writeable list to store the task
$list_id = !empty($_REQUEST['_folder']) ? rcube_utils::get_input_value('_folder', rcube_utils::INPUT_POST) : null;
$lists = $this->driver->get_lists();
- $list = $lists[$list_id] ?: $this->get_default_tasklist(true, $task['sensitivity'] == 'confidential');
+ $list = $lists[$list_id];
+ $dontsave = ($_REQUEST['_folder'] === '' && $task['_method'] == 'REQUEST');
+
+ // select default list except user explicitly selected 'none'
+ if (!$list && !$dontsave) {
+ $list = $this->get_default_tasklist(true, $task['sensitivity'] == 'confidential');
+ }
$metadata = array(
'uid' => $task['uid'],
@@ -1732,7 +1775,7 @@ class tasklist extends rcube_plugin
$reply_sender = $attendee['email'];
$task['attendees'][$i]['status'] = strtoupper($status);
- if ($task['attendees'][$i]['status'] != 'NEEDS-ACTION') {
+ if (!in_array($task['attendees'][$i]['status'], array('NEEDS-ACTION','DELEGATED'))) {
unset($task['attendees'][$i]['rsvp']); // remove RSVP attribute
}
}
@@ -1850,7 +1893,7 @@ class tasklist extends rcube_plugin
$error_msg = null;
}
}
- else if ($status == 'declined') {
+ else if ($status == 'declined' || $dontsave) {
$error_msg = null;
}
else {
@@ -1858,9 +1901,11 @@ class tasklist extends rcube_plugin
}
}
- if ($success) {
- $message = $task['_method'] == 'REPLY' ? 'attendeupdateesuccess' : ($deleted ? 'successremoval' : ($existing ? 'updatedsuccessfully' : 'importedsuccessfully'));
- $this->rc->output->command('display_message', $this->gettext(array('name' => $message, 'vars' => array('list' => $list['name']))), 'confirmation');
+ if ($success || $dontsave) {
+ if ($success) {
+ $message = $task['_method'] == 'REPLY' ? 'attendeupdateesuccess' : ($deleted ? 'successremoval' : ($existing ? 'updatedsuccessfully' : 'importedsuccessfully'));
+ $this->rc->output->command('display_message', $this->gettext(array('name' => $message, 'vars' => array('list' => $list['name']))), 'confirmation');
+ }
$metadata['rsvp'] = intval($metadata['rsvp']);
$metadata['after_action'] = $this->rc->config->get('calendar_itip_after_action', 0);
@@ -1891,7 +1936,17 @@ class tasklist extends rcube_plugin
/**** Task invitation plugin hooks ****/
/**
- * Handler for calendar/itip-status requests
+ * Handler for task/itip-delegate requests
+ */
+ function mail_itip_delegate()
+ {
+ // forward request to mail_import_itip() with the right status
+ $_POST['_status'] = $_REQUEST['_status'] = 'delegated';
+ $this->mail_import_itip();
+ }
+
+ /**
+ * Handler for task/itip-status requests
*/
public function task_itip_status()
{
@@ -1906,18 +1961,13 @@ class tasklist extends rcube_plugin
if (!$existing && $response['action'] == 'rsvp' || $response['action'] == 'import') {
$lists = $this->driver->get_lists();
$select = new html_select(array('name' => 'tasklist', 'id' => 'itip-saveto', 'is_escaped' => true));
- $num = 0;
+ $select->add('--', '');
foreach ($lists as $list) {
if ($list['editable']) {
$select->add($list['name'], $list['id']);
- $num++;
}
}
-
- if ($num <= 1) {
- $select = null;
- }
}
if ($select) {
@@ -1930,7 +1980,7 @@ class tasklist extends rcube_plugin
}
/**
- * Handler for calendar/itip-remove requests
+ * Handler for task/itip-remove requests
*/
public function task_itip_remove()
{