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:
parent
ef7f457c02
commit
44c2639cbe
9 changed files with 272 additions and 4 deletions
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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"
|
||||
*
|
||||
|
|
|
@ -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 |
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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');
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
Loading…
Add table
Reference in a new issue