diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php index 817cfdf9..e44b2c6a 100644 --- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php +++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php @@ -819,6 +819,59 @@ class tasklist_kolab_driver extends tasklist_driver $config->save_tags($uid, $tags); } + /** + * Find messages linked with a task record + */ + private function get_links($uid) + { + $config = kolab_storage_config::get_instance(); + return array_map(array($this, '_convert_message_uri'), $config->get_object_links($uid)); + } + + /** + * + */ + private function save_links($uid, $links) + { + // make sure we have a valid array + if (empty($links)) { + $links = array(); + } + + // convert the given (simplified) message links into absolute IMAP URIs + $links = array_map(function($link) { + $url = parse_url(substr($link, 8)); + parse_str($url['query'], $linkref); + + $path = explode('/', $url['path']); + $linkref['uid'] = array_pop($path); + $linkref['folder'] = join('/', array_map('rawurldecode', $path)); + + return kolab_storage_config::build_member_url($linkref); + }, $links); + + $config = kolab_storage_config::get_instance(); + $remove = array_diff($config->get_object_links($uid), $links); + return $config->save_object_links($uid, $links, $remove); + } + + /** + * Simplify the given message URI by converting the mailbox + * part into a relative IMAP path valid for the current user. + */ + protected function _convert_message_uri($uri) + { + if (strpos($uri, 'imap:///') === 0) { + $linkref = kolab_storage_config::parse_member_url($uri); + + return 'imap:///' . implode('/', array_map('rawurlencode', explode('/', $linkref['folder']))) . + '/' . $linkref['uid'] . + '?' . http_build_query($linkref['params'], '', '&'); + } + + return $uri; + } + /** * Convert from Kolab_Format to internal representation */ @@ -839,6 +892,7 @@ class tasklist_kolab_driver extends tasklist_driver 'organizer' => $record['organizer'], 'sequence' => $record['sequence'], 'tags' => $record['tags'], + 'links' => $this->get_links($record['uid']), ); // convert from DateTime to internal date format @@ -1013,9 +1067,10 @@ class tasklist_kolab_driver extends tasklist_driver if (!$list_id || !($folder = $this->get_folder($list_id))) return false; - // tags are stored separately + // email links and tags are stored separately + $links = $task['links']; $tags = $task['tags']; - unset($task['tags']); + unset($task['tags'], $task['links']); // moved from another folder if ($task['_fromlist'] && ($fromfolder = $this->get_folder($task['_fromlist']))) { @@ -1049,6 +1104,8 @@ class tasklist_kolab_driver extends tasklist_driver $saved = false; } else { + // save links in configuration.relation object + $this->save_links($object['uid'], $links); // save tags in configuration.relation object $this->save_tags($object['uid'], $tags); @@ -1169,6 +1226,17 @@ class tasklist_kolab_driver extends tasklist_driver return false; } + /** + * Build a URI representing the given message reference + * + * @see tasklist_driver::get_message_uri() + */ + public function get_message_uri($headers, $folder) + { + $uri = kolab_storage_config::get_message_uri($headers, $folder); + return $this->_convert_message_uri($uri); + } + /** * */ diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php index cdaa76ba..2102d219 100644 --- a/plugins/tasklist/drivers/tasklist_driver.php +++ b/plugins/tasklist/drivers/tasklist_driver.php @@ -287,6 +287,20 @@ abstract class tasklist_driver */ public function get_attachment_body($id, $task) { } + /** + * Build a URI representing the given message reference + * + * @param object rcube_message_header Instance holding the message headers + * @param string IMAP folder the message resides in + * + * @return string An URI referencing the given IMAP message + */ + public function get_message_uri($headers, $folder) + { + // to be implemented by the derived classes + return false; + } + /** * Helper method to determine whether the given task is considered "complete" * diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc index 72759959..4563999b 100644 --- a/plugins/tasklist/localization/en_US.inc +++ b/plugins/tasklist/localization/en_US.inc @@ -37,6 +37,7 @@ $labels['start'] = 'Start'; $labels['starttime'] = 'Start time'; $labels['alarms'] = 'Reminder'; $labels['repeat'] = 'Repeat'; +$labels['links'] = 'References'; $labels['status'] = 'Status'; $labels['status-needs-action'] = 'Needs action'; $labels['status-in-process'] = 'In process'; @@ -61,6 +62,7 @@ $labels['mytasks'] = 'My tasks'; $labels['mytaskstitle'] = 'Tasks assigned to you'; $labels['nodate'] = 'no date'; $labels['removetag'] = 'Remove'; +$labels['removelink'] = 'Remove email reference'; $labels['auto'] = 'Auto'; $labels['taskdetails'] = 'Details'; diff --git a/plugins/tasklist/skins/larry/sprites.png b/plugins/tasklist/skins/larry/sprites.png index 7a9c89f4..3552b008 100644 Binary files a/plugins/tasklist/skins/larry/sprites.png and b/plugins/tasklist/skins/larry/sprites.png differ diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css index aafc9e80..bf53e6b8 100644 --- a/plugins/tasklist/skins/larry/tasklist.css +++ b/plugins/tasklist/skins/larry/tasklist.css @@ -1083,6 +1083,56 @@ label.block { -o-box-shadow: 0 0 5px 2px rgba(71,135,177, 0.9); } +#task-links { + margin-top: 0; + margin-bottom: 0.2em; +} + +#task-links label { + vertical-align: top; + margin-top: 0.3em; +} + +#task-links .attachmentslist { + display: inline-block; +} + +#task-links .attachmentslist li { + display: inline-block; + margin-right: 1em; +} + +#taskedit-links .attachmentslist li.message.eml, +#task-links .attachmentslist li.message.eml { + background-image: url(sprites.png); + background-position: -2px -388px; +} + +#taskedit-links .attachmentslist li.message a.messagelink, +#task-links .attachmentslist li.message a.messagelink { + padding: 0 0 0 24px; +} + +#taskedit-links .attachmentslist li.deleted a.messagelink, +#taskedit-links .attachmentslist li.deleted a.messagelink:hover { + text-decoration: line-through; +} + +#taskedit-links label { + float: left; + margin-top: 0.3em; +} + +#taskedit-links .task-text { + margin-left: 8em; + min-height: 22px; +} + +#taskedit-links .attachmentslist li a.delete { + top: 0; + background-position: -6px -378px; +} + #task-attachments .attachmentslist li { float: left; margin-right: 1em; diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html index f3fd5953..4d3b9c14 100644 --- a/plugins/tasklist/skins/larry/templates/mainview.html +++ b/plugins/tasklist/skins/larry/templates/mainview.html @@ -211,6 +211,10 @@ +
diff --git a/plugins/tasklist/skins/larry/templates/taskedit.html b/plugins/tasklist/skins/larry/templates/taskedit.html index e44c5d8a..81556681 100644 --- a/plugins/tasklist/skins/larry/templates/taskedit.html +++ b/plugins/tasklist/skins/larry/templates/taskedit.html @@ -54,6 +54,11 @@
+
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js index 8a928874..3cadd57a 100644 --- a/plugins/tasklist/tasklist.js +++ b/plugins/tasklist/tasklist.js @@ -558,6 +558,12 @@ function rcube_tasklist_ui(settings) } }); + // register click handler for message links + $('#task-links, #taskedit-links').on('click', 'li a.messagelink', function(e) { + rcmail.open_window(this.href); + return false; + }); + // handle global document clicks: close popup menus $(document.body).click(clear_popups); @@ -1778,6 +1784,13 @@ function rcube_tasklist_ui(settings) } } + // build attachments list + $('#task-links').hide(); + if ($.isArray(rec.links) && rec.links.length) { + task_show_links(rec.links || [], $('#task-links').children('.task-text')); + $('#task-links').show(); + } + // list task attendees if (list.attendees && rec.attendees) { /* @@ -1984,6 +1997,14 @@ function rcube_tasklist_ui(settings) // set alarm(s) me.set_alarms_edit('#taskedit-alarms', action != 'new' && rec.valarms ? rec.valarms : []); + if ($.isArray(rec.links) && rec.links.length) { + task_show_links(rec.links, $('#taskedit-links .task-text'), true); + $('#taskedit-links').show(); + } + else { + $('#taskedit-links').hide(); + } + // set recurrence me.set_recurrence_edit(rec); @@ -2268,6 +2289,53 @@ function rcube_tasklist_ui(settings) delete rcmail.env.attachments[id]; } + /** + * + */ + function task_show_links(links, container, edit) + { + var dellink, ul = $('