Added iTip handling in tasklist plugin (code copied from Calendar)

This commit is contained in:
Aleksander Machniak 2014-07-29 15:11:05 +02:00
parent f2c6a3851d
commit 445edd09b7
7 changed files with 672 additions and 10 deletions

View file

@ -77,7 +77,7 @@ $labels['at'] = 'at';
$labels['this'] = 'this'; $labels['this'] = 'this';
$labels['next'] = 'next'; $labels['next'] = 'next';
// mesages // messages
$labels['savingdata'] = 'Saving data...'; $labels['savingdata'] = 'Saving data...';
$labels['errorsaving'] = 'Failed to save data.'; $labels['errorsaving'] = 'Failed to save data.';
$labels['notasksfound'] = 'No tasks found for the given criteria'; $labels['notasksfound'] = 'No tasks found for the given criteria';
@ -138,3 +138,17 @@ $labels['errornotifying'] = 'Failed to send notifications to task participants';
$labels['andnmore'] = '$nr more...'; $labels['andnmore'] = '$nr more...';
$labels['delegatedto'] = 'Delegated to: '; $labels['delegatedto'] = 'Delegated to: ';
$labels['delegatedfrom'] = 'Delegated from: '; $labels['delegatedfrom'] = 'Delegated from: ';
$labels['savetotasklist'] = 'Save to tasks';
$labels['comment'] = 'Comment';
$labels['errorimportingtask'] = 'Failed to import task(s)';
$labels['importwarningexists'] = 'A copy of this task already exists in your tasklist.';
$labels['importsuccess'] = 'Successfully imported $nr tasks';
$labels['newerversionexists'] = 'A newer version of this task already exists! Aborted.';
$labels['nowritetasklistfound'] = 'No tasklist found to save the task';
$labels['importedsuccessfully'] = 'The task was successfully added to \'$list\'';
$labels['updatedsuccessfully'] = 'The task was successfully updated in \'$list\'';
$labels['attendeupdateesuccess'] = 'Successfully updated the participant\'s status';
$labels['itipresponseerror'] = 'Failed to send the response to this task invitation';
$labels['itipinvalidrequest'] = 'This invitation is no longer valid';
$labels['sentresponseto'] = 'Successfully sent invitation response to $mailto';
$labels['successremoval'] = 'The task has been deleted successfully.';

Binary file not shown.

After

Width:  |  Height:  |  Size: 620 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 847 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View file

@ -20,7 +20,8 @@
background-position: 0 -26px; background-position: 0 -26px;
} }
ul.toolbarmenu li span.icon.taskadd { ul.toolbarmenu li span.icon.taskadd,
#attachmentmenu li a.tasklistlink span.icon.taskadd {
background-image: url(buttons.png); background-image: url(buttons.png);
background-position: -4px -90px; background-position: -4px -90px;
} }
@ -1085,6 +1086,98 @@ label.block {
margin-bottom: 0.5em; margin-bottom: 0.5em;
} }
/* Invitation UI in mail */
.messagelist tbody .attachment span.ical {
display: inline-block;
vertical-align: middle;
height: 18px;
width: 20px;
padding: 0;
background: url(images/ical-attachment.png) 2px 1px no-repeat;
}
div.tasklist-invitebox {
min-height: 20px;
margin: 5px 8px;
padding: 3px 6px 6px 34px;
border: 1px solid #ffdf0e;
background: url(images/tasklist.png) 6px 5px no-repeat #fef893;
}
div.tasklist-invitebox td.ititle {
font-weight: bold;
padding-right: 0.5em;
}
div.tasklist-invitebox td.label {
color: #666;
padding-right: 1em;
}
#event-rsvp .rsvp-buttons,
div.tasklist-invitebox .itip-buttons div {
margin-top: 0.5em;
}
#event-rsvp input.button,
div.tasklist-invitebox input.button {
font-weight: bold;
margin-right: 0.5em;
}
div.tasklist-invitebox .folder-select {
font-weight: 10px;
margin-left: 1em;
}
div.tasklist-invitebox .rsvp-status {
padding-left: 2px;
}
div.tasklist-invitebox .rsvp-status.loading {
color: #666;
padding: 1px 0 2px 24px;
background: url(images/loading_blue.gif) top left no-repeat;
}
div.tasklist-invitebox .rsvp-status.hint {
color: #666;
text-shadow: none;
font-style: italic;
}
#event-partstat .changersvp,
div.tasklist-invitebox .rsvp-status.declined,
div.tasklist-invitebox .rsvp-status.tentative,
div.tasklist-invitebox .rsvp-status.accepted,
div.tasklist-invitebox .rsvp-status.delegated,
div.tasklist-invitebox .rsvp-status.needs-action {
padding: 0 0 1px 22px;
background: url(images/attendee-status.png) 2px -20px no-repeat;
}
#event-partstat .changersvp.declined,
div.tasklist-invitebox .rsvp-status.declined {
background-position: 2px -40px;
}
#event-partstat .changersvp.tentative,
div.tasklist-invitebox .rsvp-status.tentative {
background-position: 2px -60px;
}
#event-partstat .changersvp.delegated,
div.tasklist-invitebox .rsvp-status.delegated {
background-position: 2px -180px;
}
#event-partstat .changersvp.needs-action,
div.tasklist-invitebox .rsvp-status.needs-action {
background-position: 2px 0;
}
/** Special hacks for IE7 **/ /** Special hacks for IE7 **/
/** They need to be in this file to also affect the task-create dialog embedded in mail view **/ /** They need to be in this file to also affect the task-create dialog embedded in mail view **/

View file

@ -56,6 +56,7 @@ class tasklist extends rcube_plugin
private $collapsed_tasks = array(); private $collapsed_tasks = array();
private $itip; private $itip;
private $ical;
/** /**
@ -107,15 +108,19 @@ class tasklist extends rcube_plugin
$this->register_action('mail2task', array($this, 'mail_message2task')); $this->register_action('mail2task', array($this, 'mail_message2task'));
$this->register_action('get-attachment', array($this, 'attachment_get')); $this->register_action('get-attachment', array($this, 'attachment_get'));
$this->register_action('upload', array($this, 'attachment_upload')); $this->register_action('upload', array($this, 'attachment_upload'));
$this->register_action('mailimportitip', array($this, 'mail_import_itip'));
$this->register_action('mailimportattach', array($this, 'mail_import_attachment'));
$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->add_hook('refresh', array($this, 'refresh')); $this->add_hook('refresh', array($this, 'refresh'));
$this->collapsed_tasks = array_filter(explode(',', $this->rc->config->get('tasklist_collapsed_tasks', ''))); $this->collapsed_tasks = array_filter(explode(',', $this->rc->config->get('tasklist_collapsed_tasks', '')));
} }
else if ($args['task'] == 'mail') { else if ($args['task'] == 'mail') {
// TODO: register hooks to catch ical/vtodo email attachments
if ($args['action'] == 'show' || $args['action'] == 'preview') { if ($args['action'] == 'show' || $args['action'] == 'preview') {
// $this->add_hook('message_load', array($this, 'mail_message_load')); $this->add_hook('message_load', array($this, 'mail_message_load'));
// $this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html')); $this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html'));
} }
// add 'Create event' item to message menu // add 'Create event' item to message menu
@ -1255,6 +1260,532 @@ class tasklist extends rcube_plugin
$this->rc->output->send(); $this->rc->output->send();
} }
/**
* 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
*/
public function mail_messagebody_html($p)
{
// load iCalendar functions (if necessary)
if (!empty($this->ics_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 = '';
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)) {
continue;
}
// show a box for every task in the file
foreach ($objects as $idx => $task) {
if ($task['_type'] != 'task') {
continue;
}
// get prepared inline UI for this event object
$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)
)
);
// limit listing
if ($idx >= 3) {
break;
}
}
}
// prepend event boxes to message body
if ($html) {
$this->load_ui();
$this->ui->init();
$p['content'] = $html . $p['content'];
$this->rc->output->add_label('tasklist.savingdata','tasklist.deletetaskconfirm','tasklist.declinedeleteconfirm');
// add "Save to calendar" button into attachment menu
$this->add_button(array(
'id' => 'attachmentsavetask',
'name' => 'attachmentsavetask',
'type' => 'link',
'wrapper' => 'li',
'command' => 'attachment-save-task',
'class' => 'icon tasklistlink',
'classact' => 'icon tasklistlink active',
'innerclass' => 'icon taskadd',
'label' => 'tasklist.savetotasklist',
), 'attachmentmenu');
}
return $p;
}
/**
* 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])) {
// 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()
{
if (!$this->ical) {
$this->ical = libcalendaring::get_ical();
}
return $this->ical;
}
/**
* Get properties of the tasklist this user has specified as default
*/
public function get_default_tasklist($writeable = false)
{
// $default_id = $this->rc->config->get('tasklist_default_list');
$lists = $this->driver->get_lists();
// $list = $calendars[$default_id] ?: null;
if (!$list || ($writeable && !$list['editable'])) {
foreach ($lists as $l) {
if ($l['default']) {
$list = $l;
break;
}
if (!$writeable || $l['editable']) {
$first = $l;
}
}
}
return $list ?: $first;
}
/**
* Import the full payload from a mail message attachment
*/
public function mail_import_attachment()
{
$uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST);
$mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST);
$mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST);
$charset = RCMAIL_CHARSET;
// establish imap connection
$imap = $this->rc->get_storage();
$imap->set_mailbox($mbox);
if ($uid && $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);
}
}
$success = $existing = 0;
if (!empty($tasks)) {
// find writeable tasklist to store task
$cal_id = !empty($_REQUEST['_list']) ? rcube_utils::get_input_value('_list', rcube_utils::INPUT_POST) : null;
$lists = $this->driver->get_lists();
$list = $lists[$cal_id] ?: $this->get_default_tasklist(true);
foreach ($tasks as $task) {
// save to tasklist
if ($list && $list['editable'] && $task['_type'] == 'task') {
$task['list'] = $list['id'];
if (!$this->driver->get_task($task['uid'])) {
$success += (bool) $this->driver->create_task($task);
}
else {
$existing++;
}
}
}
}
if ($success) {
$this->rc->output->command('display_message', $this->gettext(array(
'name' => 'importsuccess',
'vars' => array('nr' => $success),
)), 'confirmation');
}
else if ($existing) {
$this->rc->output->command('display_message', $this->gettext('importwarningexists'), 'warning');
}
else {
$this->rc->output->command('display_message', $this->gettext('errorimportingtask'), 'error');
}
}
/**
* Handler for POST request to import an event attached to a mail message
*/
public function mail_import_itip()
{
$uid = rcube_utils::get_input_value('_uid', rcube_utils::INPUT_POST);
$mbox = rcube_utils::get_input_value('_mbox', rcube_utils::INPUT_POST);
$mime_id = rcube_utils::get_input_value('_part', rcube_utils::INPUT_POST);
$status = rcube_utils::get_input_value('_status', rcube_utils::INPUT_POST);
$delete = intval(rcube_utils::get_input_value('_del', rcube_utils::INPUT_POST));
$noreply = intval(rcube_utils::get_input_value('_noreply', rcube_utils::INPUT_POST)) || $status == 'needs-action';
$error_msg = $this->gettext('errorimportingtask');
$success = false;
// successfully parsed tasks?
if ($task = $this->mail_get_itip_task($mbox, $uid, $mime_id)) {
// 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();
$list = $lists[$list_id] ?: $this->get_default_tasklist(true);
$metadata = array(
'uid' => $task['uid'],
'changed' => is_object($task['changed']) ? $task['changed']->format('U') : 0,
'sequence' => intval($task['sequence']),
'fallback' => strtoupper($status),
'method' => $this->ical->method,
'task' => 'tasks',
);
// update my attendee status according to submitted method
if (!empty($status)) {
$organizer = null;
$emails = $this->lib->get_user_emails();
foreach ($task['attendees'] as $i => $attendee) {
if ($attendee['role'] == 'ORGANIZER') {
$organizer = $attendee;
}
else if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
$metadata['attendee'] = $attendee['email'];
$metadata['rsvp'] = $attendee['role'] != 'NON-PARTICIPANT';
$reply_sender = $attendee['email'];
$task['attendees'][$i]['status'] = strtoupper($status);
if ($task['attendees'][$i]['status'] != 'NEEDS-ACTION') {
unset($task['attendees'][$i]['rsvp']); // remove RSVP attribute
}
}
}
// add attendee with this user's default identity if not listed
if (!$reply_sender) {
$sender_identity = $this->rc->user->get_identity();
$task['attendees'][] = array(
'name' => $sender_identity['name'],
'email' => $sender_identity['email'],
'role' => 'OPT-PARTICIPANT',
'status' => strtoupper($status),
);
$metadata['attendee'] = $sender_identity['email'];
}
}
// save to tasklist
if ($list && $list['editable']) {
$task['list'] = $list['id'];
// check for existing task with the same UID
$existing = $this->driver->get_task($task['uid']);
if ($existing) {
// only update attendee status
if ($this->ical->method == 'REPLY') {
// try to identify the attendee using the email sender address
$existing_attendee = -1;
foreach ($existing['attendees'] as $i => $attendee) {
if ($task['_sender'] && ($attendee['email'] == $task['_sender'] || $attendee['email'] == $task['_sender_utf'])) {
$existing_attendee = $i;
break;
}
}
$task_attendee = null;
foreach ($task['attendees'] as $attendee) {
if ($task['_sender'] && ($attendee['email'] == $task['_sender'] || $attendee['email'] == $task['_sender_utf'])) {
$task_attendee = $attendee;
$metadata['fallback'] = $attendee['status'];
$metadata['attendee'] = $attendee['email'];
$metadata['rsvp'] = $attendee['rsvp'] || $attendee['role'] != 'NON-PARTICIPANT';
break;
}
}
// found matching attendee entry in both existing and new events
if ($existing_attendee >= 0 && $task_attendee) {
$existing['attendees'][$existing_attendee] = $task_attendee;
$success = $this->driver->edit_task($existing);
}
// update the entire attendees block
else if (($task['sequence'] >= $existing['sequence'] || $task['changed'] >= $existing['changed']) && $task_attendee) {
$existing['attendees'][] = $task_attendee;
$success = $this->driver->edit_task($existing);
}
else {
$error_msg = $this->gettext('newerversionexists');
}
}
// delete the task when declined
else if ($status == 'declined' && $delete) {
$deleted = $this->driver->delete_task($existing, true);
$success = true;
}
// import the (newer) task
else if ($task['sequence'] >= $existing['sequence'] || $task['changed'] >= $existing['changed']) {
$task['id'] = $existing['id'];
$task['list'] = $existing['list'];
// preserve my participant status for regular updates
if (empty($status)) {
$emails = $this->lib->get_user_emails();
foreach ($task['attendees'] as $i => $attendee) {
if ($attendee['email'] && in_array(strtolower($attendee['email']), $emails)) {
foreach ($existing['attendees'] as $j => $_attendee) {
if ($attendee['email'] == $_attendee['email']) {
$task['attendees'][$i] = $existing['attendees'][$j];
break;
}
}
}
}
}
// set status=CANCELLED on CANCEL messages
if ($this->ical->method == 'CANCEL') {
$task['status'] = 'CANCELLED';
}
// show me as free when declined (#1670)
if ($status == 'declined' || $task['status'] == 'CANCELLED') {
$task['free_busy'] = 'free';
}
$success = $this->driver->edit_task($task);
}
else if (!empty($status)) {
$existing['attendees'] = $task['attendees'];
if ($status == 'declined') { // show me as free when declined (#1670)
$existing['free_busy'] = 'free';
}
$success = $this->driver->edit_event($existing);
}
else {
$error_msg = $this->gettext('newerversionexists');
}
}
else if (!$existing && ($status != 'declined' || $this->rc->config->get('kolab_invitation_tasklists'))) {
$success = $this->driver->create_task($task);
}
else if ($status == 'declined') {
$error_msg = null;
}
}
else if ($status == 'declined') {
$error_msg = null;
}
else {
$error_msg = $this->gettext('nowritetasklistfound');
}
}
if ($success) {
$message = $this->ical->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('tasklist_itip_after_action');
$this->rc->output->command('plugin.itip_message_processed', $metadata);
$error_msg = null;
}
else if ($error_msg) {
$this->rc->output->command('display_message', $error_msg, 'error');
}
// send iTip reply
if ($this->ical->method == 'REQUEST' && $organizer && !$noreply && !in_array(strtolower($organizer['email']), $emails) && !$error_msg) {
$task['comment'] = rcube_utils::get_input_value('_comment', rcube_utils::INPUT_POST);
$itip = $this->load_itip();
$itip->set_sender_email($reply_sender);
if ($itip->send_itip_message($task, 'REPLY', $organizer, 'itipsubject' . $status, 'itipmailbody' . $status))
$this->rc->output->command('display_message', $this->gettext(array('name' => 'sentresponseto', 'vars' => array('mailto' => $organizer['name'] ? $organizer['name'] : $organizer['email']))), 'confirmation');
else
$this->rc->output->command('display_message', $this->gettext('itipresponseerror'), 'error');
}
$this->rc->output->send();
}
/**** Task invitation plugin hooks ****/
/**
* Handler for calendar/itip-status requests
*/
function task_itip_status()
{
$data = rcube_utils::get_input_value('data', rcube_utils::INPUT_POST, true);
// find local copy of the referenced task
$existing = $this->driver->get_task($data);
$itip = $this->load_itip();
$response = $itip->get_itip_status($data, $existing);
// get a list of writeable lists to save new tasks to
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;
foreach ($lists as $list) {
if ($list['editable']) {
$select->add($list['name'], $list['id']);
$num++;
}
}
if ($num <= 1) {
$select = null;
}
}
if ($select) {
$default_list = $this->get_default_tasklist(true);
$response['select'] = html::span('folder-select', $this->gettext('saveintasklist') . '&nbsp;' .
$select->show($this->rc->config->get('tasklist_default_list', $default_list['id'])));
}
$this->rc->output->command('plugin.update_itip_object_status', $response);
}
/**
* Handler for calendar/itip-remove requests
*/
function task_itip_remove()
{
$success = false;
$uid = rcube_utils::get_input_value('uid', rcube_utils::INPUT_POST);
// search for event if only UID is given
if ($task = $this->driver->get_task($uid)) {
$success = $this->driver->delete_task($task, true);
}
if ($success) {
$this->rc->output->show_message('tasklist.successremoval', 'confirmation');
}
else {
$this->rc->output->show_message('tasklist.errorsaving', 'error');
}
}
/******* Utility functions *******/ /******* Utility functions *******/
@ -1275,4 +1806,3 @@ class tasklist extends rcube_plugin
return $this->driver->user_delete($args); return $this->driver->user_delete($args);
} }
} }

View file

@ -37,6 +37,7 @@ function rcube_tasklist(settings)
/* public methods */ /* public methods */
this.create_from_mail = create_from_mail; this.create_from_mail = create_from_mail;
this.mail2taskdialog = mail2task_dialog; this.mail2taskdialog = mail2task_dialog;
this.save_to_tasklist = save_to_tasklist;
/** /**
@ -80,14 +81,29 @@ function rcube_tasklist(settings)
this.ui.edit_task(null, 'new', prop); this.ui.edit_task(null, 'new', prop);
} }
// handler for attachment-save-tasklist commands
function save_to_tasklist()
{
// TODO: show dialog to select the tasklist for importing
if (this.selected_attachment && window.rcube_libcalendaring) {
rcmail.http_post('tasks/mailimportattach', {
_uid: rcmail.env.uid,
_mbox: rcmail.env.mailbox,
_part: this.selected_attachment,
// _list: $('#tasklist-attachment-saveto').val(),
}, rcmail.set_busy(true, 'itip.savingdata'));
}
}
} }
/* tasklist plugin initialization (for email task) */ /* tasklist plugin initialization (for email task) */
window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', function(evt) { window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', function(evt) {
var tasks = new rcube_tasklist(rcmail.env.libcal_settings); var tasks = new rcube_tasklist(rcmail.env.libcal_settings);
rcmail.register_command('tasklist-create-from-mail', function() { tasks.create_from_mail() }); rcmail.register_command('tasklist-create-from-mail', function() { tasks.create_from_mail(); });
rcmail.addEventListener('plugin.mail2taskdialog', function(p){ tasks.mail2taskdialog(p) }); rcmail.register_command('attachment-save-task', function() { tasks.save_to_tasklist(); });
rcmail.addEventListener('plugin.mail2taskdialog', function(p) { tasks.mail2taskdialog(p); });
rcmail.addEventListener('plugin.unlock_saving', function(p) { tasks.ui && tasks.ui.unlock_saving(); }); rcmail.addEventListener('plugin.unlock_saving', function(p) { tasks.ui && tasks.ui.unlock_saving(); });
if (rcmail.env.action != 'show') if (rcmail.env.action != 'show')
@ -95,6 +111,16 @@ window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', fu
else else
rcmail.enable_command('tasklist-create-from-mail', true); rcmail.enable_command('tasklist-create-from-mail', true);
rcmail.addEventListener('beforemenu-open', function(p) {
if (p.menu == 'attachmentmenu') {
tasks.selected_attachment = p.id;
var mimetype = rcmail.env.attachments[p.id],
is_ics = mimetype == 'text/calendar' || mimetype == 'text/x-vcalendar' || mimetype == 'application/ics';
rcmail.enable_command('attachment-save-task', is_ics);
}
});
// add contextmenu item // add contextmenu item
if (window.rcm_contextmenu_register_command) { if (window.rcm_contextmenu_register_command) {
rcm_contextmenu_register_command( rcm_contextmenu_register_command(
@ -104,4 +130,3 @@ window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', fu
'moveto'); 'moveto');
} }
}); });