- List linked tasks in mail view, like notes

- Deep-links into the tasks view opening the linked task
- Mark tasks as complete directly from mail view
This commit is contained in:
Thomas Bruederli 2014-10-13 18:40:39 +02:00
parent f505abb350
commit adc41ebf61
8 changed files with 204 additions and 6 deletions

View file

@ -1237,6 +1237,25 @@ class tasklist_kolab_driver extends tasklist_driver
return $this->_convert_message_uri($uri);
}
/**
* Find tasks assigned to a specified message
*
* @see tasklist_driver::get_message_related_tasks()
*/
public function get_message_related_tasks($headers, $folder)
{
$config = kolab_storage_config::get_instance();
$result = $config->get_message_relations($headers, $folder, 'task');
foreach ($result as $idx => $rec) {
$task = $this->_to_rcube_task($rec);
$task['list'] = kolab_storage::folder_id($rec['_mailbox']);
$result[$idx] = $task;
}
return $result;
}
/**
*
*/

View file

@ -290,8 +290,8 @@ abstract class tasklist_driver
/**
* 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
* @param object $headers rcube_message_header instance holding the message headers
* @param string $folder IMAP folder the message resides in
*
* @return string An URI referencing the given IMAP message
*/
@ -301,6 +301,20 @@ abstract class tasklist_driver
return false;
}
/**
* Find tasks assigned to a specified message
*
* @param object $message rcube_message_header instance
* @param string $folder IMAP folder the message resides in
*
* @param array List of linked task objects
*/
public function get_message_related_tasks($headers, $folder)
{
// to be implemented by the derived classes
return array();
}
/**
* Helper method to determine whether the given task is considered "complete"
*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

@ -1337,6 +1337,46 @@ div.tasklist-invitebox .rsvp-status.accepted,
background-position: 2px -220px;
}
div.messagetasklinks {
margin: 8px 8px;
padding: 4px 8px;
border: 1px solid #dfdfdf;
background: #fafafa;
border-radius: 4px;
}
div.messagetasklinks span.messagetaskref {
position: relative;
display: inline-block;
margin-right: 1em;
padding-right: 24px;
}
div.messagetasklinks a.messagetasklink {
position: relative;
display: inline-block;
color: #333;
font-weight: bold;
padding: 3px 0 2px 22px;
text-shadow: 0px 1px 1px #fff;
text-decoration: none;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 16em;
background: url(buttons.png) -6px -115px no-repeat;
}
div.messagetasklinks span.messagetaskref.complete a.messagetasklink {
text-decoration: line-through;
}
div.messagetasklinks span.messagetaskref input.complete {
position: absolute;
top: 1px;
right: 2px;
}
/** Special hacks for IE7 **/
/** They need to be in this file to also affect the task-create dialog embedded in mail view **/

View file

@ -137,6 +137,11 @@ function rcube_tasklist_ui(settings)
{
// initialize task list selectors
for (var id in me.tasklists) {
if (settings.selected_list && me.tasklists[settings.selected_list] && !me.tasklists[settings.selected_list].active) {
me.tasklists[settings.selected_list].active = true;
me.selected_list = settings.selected_list;
$(rcmail.gui_objects.tasklistslist).find("input[value='"+settings.selected_list+"']").prop('checked', true);
}
if (me.tasklists[id].editable && (!me.selected_list || me.tasklists[id].default || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) {
me.selected_list = id;
}
@ -269,10 +274,9 @@ function rcube_tasklist_ui(settings)
$('#taskviewsortmenu .by-' + (settings.sort_col || 'auto')).attr('aria-checked', 'true').addClass('selected');
$('#taskviewsortmenu .sortorder.' + (settings.sort_order || 'asc')).attr('aria-checked', 'true').addClass('selected');
// start loading tasks
fetch_counts();
list_tasks();
list_tasks(settings.selected_filter);
// register event handlers for UI elements
$('#taskselector a').click(function(e) {
@ -769,6 +773,19 @@ function rcube_tasklist_ui(settings)
append_tags(response.tags || []);
render_tasklist();
// show selected task dialog
if (settings.selected_id) {
if (listdata[settings.selected_id]) {
task_show_dialog(settings.selected_id);
delete settings.selected_id;
}
// remove _id from window location
if (window.history.replaceState) {
window.history.replaceState({}, document.title, rcmail.url('', { _list: me.selected_list }));
}
}
rcmail.set_busy(false, 'loading', ui_loading);
}

View file

@ -61,6 +61,7 @@ class tasklist extends rcube_plugin
public $home; // declare public to be used in other classes
private $collapsed_tasks = array();
private $message_tasks = array();
private $itip;
private $ical;
@ -125,6 +126,9 @@ class tasklist extends rcube_plugin
}
else if ($args['task'] == 'mail') {
if ($args['action'] == 'show' || $args['action'] == 'preview') {
if ($this->rc->config->get('tasklist_mail_embed', true)) {
$this->add_hook('message_load', array($this, 'mail_message_load'));
}
$this->add_hook('template_object_messagebody', array($this, 'mail_messagebody_html'));
}
@ -204,7 +208,8 @@ class tasklist extends rcube_plugin
$success = $refresh = false;
// force notify if hidden + active
if ((int)$this->rc->config->get('calendar_itip_send_option', 3) === 1 && empty($rec['_reportpartstat']))
$itip_send_option = (int)$this->rc->config->get('calendar_itip_send_option', 3);
if ($itip_send_option === 1 && empty($rec['_reportpartstat']))
$rec['_notify'] = 1;
switch ($action) {
@ -220,6 +225,24 @@ class tasklist extends rcube_plugin
}
break;
case 'complete':
$complete = intval(rcube_utils::get_input_value('complete', rcube_utils::INPUT_POST));
if (!($rec = $this->driver->get_task($rec))) {
break;
}
$rec['status'] = $complete ? 'COMPLETED' : ($rec['complete'] > 0 ? 'IN-PROCESS' : 'NEEDS-ACTION');
// sent itip notifications if enabled (no user interaction here)
if (($itip_send_option & 1)) {
if ($this->is_attendee($rec)) {
$rec['_reportpartstat'] = $rec['status'];
}
else if ($this->is_organizer($rec)) {
$rec['_notify'] = 1;
}
}
case 'edit':
$rec = $this->prepare_task($rec);
$clone = $this->handle_recurrence($rec, $this->driver->get_task($rec));
@ -1437,7 +1460,36 @@ class tasklist extends rcube_plugin
}
}
// prepend event boxes to message body
// list linked tasks
$links = array();
foreach ($this->message_tasks as $task) {
$checkbox = new html_checkbox(array(
'name' => 'completed',
'class' => 'complete',
'title' => $this->gettext('complete'),
'data-list' => $task['list'],
));
$complete = $this->driver->is_complete($task);
$links[] = html::span('messagetaskref' . ($complete ? ' complete' : ''),
$checkbox->show($complete ? $task['uid'] : null, array('value' => $task['uid'])) . ' ' .
html::a(array(
'href' => $this->rc->url(array(
'task' => 'tasks',
'list' => $task['list'],
'id' => $task['uid'],
'complete' => $complete?1:null,
)),
'class' => 'messagetasklink',
'rel' => $task['uid'] . '@' . $task['list'],
'target' => '_blank',
), Q($task['title']))
);
}
if (count($links)) {
$html .= html::div('messagetasklinks', join("\n", $links));
}
// prepend iTip/relation boxes to message body
if ($html) {
$this->load_ui();
$this->ui->init();
@ -1465,6 +1517,17 @@ class tasklist extends rcube_plugin
return $p;
}
/**
* Lookup backend storage and find notes associated with the given message
*/
public function mail_message_load($p)
{
if (!$p['object']->headers->others['x-kolab-type']) {
$this->load_driver();
$this->message_tasks = $this->driver->get_message_related_tasks($p['object']->headers, $p['object']->folder);
}
}
/**
* Load iCalendar functions
*/

View file

@ -30,6 +30,7 @@ function rcube_tasklist(settings)
/* private vars */
var ui_loaded = false;
var me = this;
var mywin = window;
/* public members */
this.ui;
@ -79,6 +80,18 @@ function rcube_tasklist(settings)
function mail2task_dialog(prop)
{
this.ui.edit_task(null, 'new', prop);
rcmail.addEventListener('responseaftertask', refresh_mailview);
}
/**
* Reload the mail view/preview to update the tasks listing
*/
function refresh_mailview(e)
{
var win = rcmail.env.contentframe ? rcmail.get_frame_window(rcmail.env.contentframe) : mywin;
if (win && e.response.action == 'task') {
win.location.reload();
}
}
// handler for attachment-save-tasklist commands
@ -95,6 +108,21 @@ function rcube_tasklist(settings)
}
}
// register event handlers on linked task items in message view
// the checkbox allows to mark a task as complete
if (rcmail.env.action == 'show' || rcmail.env.action == 'preview') {
$('div.messagetasklinks input.complete').click(function(e) {
var $this = $(this);
$(this).closest('.messagetaskref').toggleClass('complete');
// submit change to server
rcmail.http_post('tasks/task', {
action: 'complete',
t: { id:this.value, list:$this.attr('data-list') },
complete: this.checked?1:0
}, rcmail.set_busy(true, 'tasklist.savingdata'));
});
}
}
/* tasklist plugin initialization (for email task) */

View file

@ -92,6 +92,23 @@ class tasklist_ui
'emails' => ';' . strtolower(join(';', $identity['emails']))
);
if ($list = rcube_utils::get_input_value('_list', rcube_utils::INPUT_GPC)) {
$settings['selected_list'] = $list;
}
if ($list && ($id = rcube_utils::get_input_value('_id', rcube_utils::INPUT_GPC))) {
$settings['selected_id'] = $id;
// check if the referenced task is completed
$task = $this->plugin->driver->get_task(array('id' => $id, 'list' => $list));
console($id, $task);
if ($task && $this->plugin->driver->is_complete($task)) {
$settings['selected_filter'] = 'complete';
}
}
else if ($filter = rcube_utils::get_input_value('_filter', rcube_utils::INPUT_GPC)) {
$settings['selected_filter'] = $filter;
}
return $settings;
}