Save email message a task is created from as relation (#3439); display the reference in the task details and edit dialogs

This commit is contained in:
Thomas Bruederli 2014-10-13 15:33:39 +02:00
parent ef7f457c02
commit 44c2639cbe
9 changed files with 272 additions and 4 deletions

View file

@ -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);
}
/**
*
*/

View file

@ -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"
*

View file

@ -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';

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -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;

View file

@ -211,6 +211,10 @@
<label><roundcube:label name="tasklist.status" /></label>
<span class="task-text"></span>
</div>
<div id="task-links" class="form-section">
<label><roundcube:label name="tasklist.links" /></label>
<span class="task-text"></span>
</div>
<div id="task-attachments" class="form-section">
<label><roundcube:label name="attachments" /></label>
<div class="task-text"></div>

View file

@ -54,6 +54,11 @@
<label for="taskedit-tasklist"><roundcube:label name="tasklist.list" /></label>
<roundcube:object name="plugin.tasklist_select" id="taskedit-tasklist" />
</div>
<div class="form-section" id="taskedit-links">
<label><roundcube:label name="tasklist.links" /></label>
<div class="task-text"></div>
<br style="clear:left">
</div>
</div>
<!-- recurrence settings -->
<div id="taskedit-panel-recurrence">

View file

@ -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 = $('<ul>').addClass('attachmentslist');
$.each(links, function(i, link) {
var li = $('<li>').addClass('link')
.addClass('message eml')
.append($('<a>')
.attr('href', link.mailurl)
.addClass('messagelink')
.text(link.subject || link.uri)
)
.appendTo(ul);
// add icon to remove the link
if (edit) {
$('<a>')
.attr('href', '#delete')
.attr('title', rcmail.gettext('removelink','tasklist'))
.addClass('delete')
.text(rcmail.gettext('delete'))
.click({ uri:link.uri }, function(e) {
remove_link(this, e.data.uri);
return false;
})
.appendTo(li);
}
});
container.empty().append(ul);
}
/**
*
*/
function remove_link(elem, uri)
{
// remove the link item matching the given uri
me.selected_task.links = $.grep(me.selected_task.links, function(link) { return link.uri != uri; });
// remove UI list item
$(elem).hide().closest('li').addClass('deleted');
}
/**
*
*/

View file

@ -564,6 +564,11 @@ class tasklist extends rcube_plugin
$rec['attachments'] = $attachments;
// convert link references into simple URIs
if (array_key_exists('links', $rec)) {
$rec['links'] = array_map(function($link) { return is_array($link) ? $link['uri'] : strval($link); }, (array)$rec['links']);
}
// convert invalid data
if (isset($rec['attendees']) && !is_array($rec['attendees']))
$rec['attendees'] = array();
@ -1045,6 +1050,15 @@ class tasklist extends rcube_plugin
$rec['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
}
// convert link URIs references into structs
if (array_key_exists('links', $rec)) {
foreach ((array)$rec['links'] as $i => $link) {
if (strpos($link, 'imap://') === 0) {
$rec['links'][$i] = $this->get_message_reference($link);
}
}
}
if (!is_array($rec['tags']))
$rec['tags'] = (array)$rec['tags'];
sort($rec['tags'], SORT_LOCALE_STRING);
@ -1163,7 +1177,7 @@ class tasklist extends rcube_plugin
$this->rc->output->set_env('autocomplete_threads', (int)$this->rc->config->get('autocomplete_threads', 0));
$this->rc->output->set_env('autocomplete_max', (int)$this->rc->config->get('autocomplete_max', 15));
$this->rc->output->set_env('autocomplete_min_length', $this->rc->config->get('autocomplete_min_length'));
$this->rc->output->add_label('autocompletechars', 'autocompletemore', 'libcalendaring.expandattendeegroup', 'libcalendaring.expandattendeegroupnodata');
$this->rc->output->add_label('autocompletechars', 'autocompletemore', 'delete', 'libcalendaring.expandattendeegroup', 'libcalendaring.expandattendeegroupnodata');
$this->rc->output->set_pagetitle($this->gettext('navtitle'));
$this->rc->output->send('tasklist.mainview');
@ -1335,8 +1349,12 @@ class tasklist extends rcube_plugin
$this->load_driver();
// add a reference to the email message
if ($msguri = $this->driver->get_message_uri($message->headers, $mbox)) {
$task['links'] = array($this->get_message_reference($msguri));
}
// copy mail attachments to task
if ($message->attachments && $this->driver->attachments) {
else if ($message->attachments && $this->driver->attachments) {
if (!is_array($_SESSION[self::SESSION_KEY]) || $_SESSION[self::SESSION_KEY]['id'] != $task['id']) {
$_SESSION[self::SESSION_KEY] = array();
$_SESSION[self::SESSION_KEY]['id'] = $task['id'];
@ -1488,6 +1506,45 @@ class tasklist extends rcube_plugin
return $list ?: $first;
}
/**
* Resolve the email message reference from the given URI
*/
public function get_message_reference($uri, $resolve = false)
{
if (strpos($uri, 'imap:///') === 0) {
$url = parse_url(substr($uri, 8));
parse_str($url['query'], $params);
$path = explode('/', $url['path']);
$uid = array_pop($path);
$folder = join('/', array_map('rawurldecode', $path));
}
if ($folder && $uid) {
// TODO: check if folder/uid still references an existing message
// TODO: validate message or resovle the new URI using the message-id parameter
$linkref = array(
'folder' => $folder,
'uid' => $uid,
'subject' => $params['subject'],
'uri' => $uri,
'mailurl' => $this->rc->url(array(
'task' => 'mail',
'action' => 'show',
'mbox' => $folder,
'uid' => $uid,
'rel' => 'task',
))
);
}
else {
$linkref = array();
}
return $linkref;
}
/**
* Import the full payload from a mail message attachment
*/