Branch 'roundcubemail-plugins-kolab-3.1' - 22 commits - plugins/calendar plugins/tasklist
Jeroen van Meeuwen
vanmeeuwen at kolabsys.com
Thu Oct 31 13:20:39 CET 2013
plugins/calendar/calendar.php | 56 +
plugins/calendar/calendar_base.js | 21
plugins/calendar/calendar_ui.js | 32
plugins/calendar/drivers/calendar_driver.php | 4
plugins/calendar/drivers/database/database_driver.php | 8
plugins/calendar/drivers/kolab/kolab_driver.php | 11
plugins/tasklist/drivers/database/tasklist_database_driver.php | 6
plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php | 6
plugins/tasklist/localization/en_US.inc | 2
plugins/tasklist/skins/larry/sprites.png |binary
plugins/tasklist/skins/larry/tasklist.css | 111 ++-
plugins/tasklist/skins/larry/templates/mainview.html | 13
plugins/tasklist/tasklist.js | 358 ++++++++--
plugins/tasklist/tasklist.php | 66 +
plugins/tasklist/tasklist_base.js | 18
plugins/tasklist/tasklist_ui.php | 2
16 files changed, 623 insertions(+), 91 deletions(-)
New commits:
commit d55977fee33b4e84b1af0991482ced917a61e927
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 30 16:05:07 2013 +0100
Add mail actions to contextmenu if loaded
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index ced9b20..685745d 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -163,6 +163,8 @@ class calendar extends rcube_plugin
'innerclass' => 'icon calendar',
))),
'messagemenu');
+
+ $this->api->output->add_label('calendar.createfrommail');
}
}
diff --git a/plugins/calendar/calendar_base.js b/plugins/calendar/calendar_base.js
index c60c89a..33fe9e4 100644
--- a/plugins/calendar/calendar_base.js
+++ b/plugins/calendar/calendar_base.js
@@ -6,7 +6,7 @@
* @author Thomas Bruederli <bruederli at kolabsys.com>
*
* Copyright (C) 2010, Lazlo Westerhof <hello at lazlo.me>
- * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -36,10 +36,9 @@ function rcube_calendar(settings)
var me = this;
// create new event from current mail message
- this.create_from_mail = function()
+ this.create_from_mail = function(uid)
{
- var uid;
- if ((uid = rcmail.get_single_uid())) {
+ if (uid || (uid = rcmail.get_single_uid())) {
// load calendar UI (scripts and edit dialog template)
if (!this.ui_loaded) {
$.when(
@@ -53,7 +52,7 @@ function rcube_calendar(settings)
me.ui_loaded = true;
me.ui = new rcube_calendar_ui(me.settings);
- me.create_from_mail(); // start over
+ me.create_from_mail(uid); // start over
});
return;
}
@@ -156,9 +155,6 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
// register create-from-mail command to message_commands array
if (rcmail.env.task == 'mail') {
- // place link above 'view source'
- $('#messagemenu a.calendarlink').parent().insertBefore($('#messagemenu a.sourcelink').parent());
-
rcmail.register_command('calendar-create-from-mail', function() { cal.create_from_mail() });
rcmail.addEventListener('plugin.mail2event_dialog', function(p){ cal.mail2event_dialog(p) });
rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.ui && cal.ui.unlock_saving(); });
@@ -169,6 +165,15 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
}
else
rcmail.enable_command('calendar-create-from-mail', true);
+
+ // add contextmenu item
+ if (window.rcm_contextmenu_register_command) {
+ rcm_contextmenu_register_command(
+ 'calendar-create-from-mail',
+ function(cmd,el){ cal.create_from_mail() },
+ 'calendar.createfrommail',
+ 'moveto');
+ }
}
}
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 619a652..e77bccc 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -113,6 +113,8 @@ class tasklist extends rcube_plugin
'innerclass' => 'icon taskadd',
))),
'messagemenu');
+
+ $this->api->output->add_label('tasklist.createfrommail');
}
}
diff --git a/plugins/tasklist/tasklist_base.js b/plugins/tasklist/tasklist_base.js
index e3a889c..f804c34 100644
--- a/plugins/tasklist/tasklist_base.js
+++ b/plugins/tasklist/tasklist_base.js
@@ -4,7 +4,7 @@
* @version @package_version@
* @author Thomas Bruederli <bruederli at kolabsys.com>
*
- * Copyright (C) 2012, Kolab Systems AG <contact at kolabsys.com>
+ * Copyright (C) 2013, Kolab Systems AG <contact at kolabsys.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
@@ -37,10 +37,9 @@ function rcube_tasklist(settings)
/**
* Open a new task dialog prefilled with contents from the currently selected mail message
*/
- function create_from_mail()
+ function create_from_mail(uid)
{
- var uid;
- if ((uid = rcmail.get_single_uid())) {
+ if (uid || (uid = rcmail.get_single_uid())) {
// load calendar UI (scripts and edit dialog template)
if (!ui_loaded) {
$.when(
@@ -53,7 +52,7 @@ function rcube_tasklist(settings)
ui_loaded = true;
me.ui = new rcube_tasklist_ui(settings);
- create_from_mail(); // start over
+ create_from_mail(uid); // start over
});
return;
}
@@ -90,5 +89,14 @@ window.rcmail && rcmail.env.task == 'mail' && rcmail.addEventListener('init', fu
rcmail.env.message_commands.push('tasklist-create-from-mail');
else
rcmail.enable_command('tasklist-create-from-mail', true);
+
+ // add contextmenu item
+ if (window.rcm_contextmenu_register_command) {
+ rcm_contextmenu_register_command(
+ 'tasklist-create-from-mail',
+ function(cmd,el){ tasks.create_from_mail() },
+ 'tasklist.createfrommail',
+ 'moveto');
+ }
});
commit fb7de4efda2324617a0784a1d68f36e430f56284
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 30 15:05:24 2013 +0100
Choose the currently selected list in new task dialog
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index bc88a67..25ab68d 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1188,7 +1188,7 @@ function rcube_tasklist_ui(settings)
var recstarttime = $('#taskedit-starttime').val(rec.starttime || '');
var complete = $('#taskedit-completeness').val((rec.complete || 0) * 100);
completeness_slider.slider('value', complete.val());
- var tasklist = $('#taskedit-tasklist').val(rec.list || 0).prop('disabled', rec.parent_id ? true : false);
+ var tasklist = $('#taskedit-tasklist').val(rec.list || me.selected_list).prop('disabled', rec.parent_id ? true : false);
// tag-edit line
var tagline = $(rcmail.gui_objects.edittagline).empty();
commit b705e6bc2f109fa6662c2bc6762059170e49e491
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 30 15:04:23 2013 +0100
Render newly created task, even if saved to an inactive list
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 5f78884..bc88a67 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -753,7 +753,7 @@ function rcube_tasklist_ui(settings)
}
}
- if (list.active) {
+ if (list.active || rec.tempid) {
if (!filter || match_filter(rec, {}))
render_task(rec, oldid);
}
commit c7474b112bb5438bcad242663ddb35233507eba0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 30 15:00:08 2013 +0100
Select first active list on startup
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 5035f77..5f78884 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -120,13 +120,16 @@ function rcube_tasklist_ui(settings)
init_tasklist_li(li, id);
}
- if (me.tasklists[id].editable && !me.selected_list) {
+ if (me.tasklists[id].editable && (!me.selected_list || (me.tasklists[id].active && !me.tasklists[me.selected_list].active))) {
me.selected_list = id;
- rcmail.enable_command('addtask', true);
- $(li).click();
}
}
+ if (me.selected_list) {
+ rcmail.enable_command('addtask', true);
+ $(rcmail.get_folder_li(me.selected_list, 'rcmlitasklist')).click();
+ }
+
// register server callbacks
rcmail.addEventListener('plugin.data_ready', data_ready);
rcmail.addEventListener('plugin.update_task', update_taskitem);
commit 39a86a909b89f906dbfd2375a6d3a04b94048164
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Tue Oct 29 17:11:45 2013 +0100
Refresh the entire tasks list/calendar on every 10th request to sync deleted items (#2369)
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 24ffa38..ced9b20 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -925,19 +925,28 @@ class calendar extends rcube_plugin
*/
public function refresh($attr)
{
+ // refresh the entire calendar every 10th time to also sync deleted events
+ $refetch = rand(0,10) == 10;
+
foreach ($this->driver->list_calendars(true) as $cal) {
- $events = $this->driver->load_events(
- get_input_value('start', RCUBE_INPUT_GET),
- get_input_value('end', RCUBE_INPUT_GET),
- get_input_value('q', RCUBE_INPUT_GET),
- $cal['id'],
- 1,
- $attr['last']
- );
+ if ($refetch) {
+ $this->rc->output->command('plugin.refresh_calendar',
+ array('source' => $cal['id'], 'refetch' => true));
+ }
+ else {
+ $events = $this->driver->load_events(
+ get_input_value('start', RCUBE_INPUT_GET),
+ get_input_value('end', RCUBE_INPUT_GET),
+ get_input_value('q', RCUBE_INPUT_GET),
+ $cal['id'],
+ 1,
+ $attr['last']
+ );
- foreach ($events as $event) {
- $args = array('source' => $cal['id'], 'update' => $this->_client_event($event));
- $this->rc->output->command('plugin.refresh_calendar', $args);
+ foreach ($events as $event) {
+ $this->rc->output->command('plugin.refresh_calendar',
+ array('source' => $cal['id'], 'update' => $this->_client_event($event)));
+ }
}
}
}
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 8fd1889..5035f77 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -135,9 +135,12 @@ function rcube_tasklist_ui(settings)
rcmail.addEventListener('plugin.insert_tasklist', insert_list);
rcmail.addEventListener('plugin.update_tasklist', update_list);
rcmail.addEventListener('plugin.destroy_tasklist', destroy_list);
- rcmail.addEventListener('plugin.reload_data', function(){ list_tasks(null); });
rcmail.addEventListener('plugin.unlock_saving', unlock_saving);
rcmail.addEventListener('requestrefresh', before_refresh);
+ rcmail.addEventListener('plugin.reload_data', function(){
+ list_tasks(null, true);
+ setTimeout(fetch_counts, 200);
+ });
// start loading tasks
fetch_counts();
@@ -400,7 +403,7 @@ function rcube_tasklist_ui(settings)
/**
* List tasks matching the given selector
*/
- function list_tasks(sel)
+ function list_tasks(sel, force)
{
if (rcmail.busy)
return;
@@ -412,7 +415,7 @@ function rcube_tasklist_ui(settings)
var active = active_lists(),
basefilter = filtermask == FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL,
- reload = active.join(',') != loadstate.lists || basefilter != loadstate.filter || loadstate.search != search_query;
+ reload = force || active.join(',') != loadstate.lists || basefilter != loadstate.filter || loadstate.search != search_query;
if (active.length && reload) {
ui_loading = rcmail.set_busy(true, 'loading');
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index aaeea95..619a652 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -742,6 +742,12 @@ class tasklist extends rcube_plugin
*/
public function refresh($attr)
{
+ // refresh the entire list every 10th time to also sync deleted items
+ if (rand(0,10) == 10) {
+ $this->rc->output->command('plugin.reload_data');
+ return;
+ }
+
$filter = array(
'since' => $attr['last'],
'search' => get_input_value('q', RCUBE_INPUT_GPC),
commit a2152768a1d61fc5469f9972a87cbd6bb591d4e9
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 21 20:24:49 2013 +0200
Periodically refresh event data from server
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index 11821d7..24ffa38 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -126,6 +126,7 @@ class calendar extends rcube_plugin
$this->register_action('mailtoevent', array($this, 'mail_message2event'));
$this->register_action('inlineui', array($this, 'get_inline_ui'));
$this->register_action('check-recent', array($this, 'check_recent'));
+ $this->add_hook('refresh', array($this, 'refresh'));
// remove undo information...
if ($undo = $_SESSION['calendar_event_undo']) {
@@ -919,6 +920,29 @@ class calendar extends rcube_plugin
}
/**
+ * Handler for keep-alive requests
+ * This will check for updated data in active calendars and sync them to the client
+ */
+ public function refresh($attr)
+ {
+ foreach ($this->driver->list_calendars(true) as $cal) {
+ $events = $this->driver->load_events(
+ get_input_value('start', RCUBE_INPUT_GET),
+ get_input_value('end', RCUBE_INPUT_GET),
+ get_input_value('q', RCUBE_INPUT_GET),
+ $cal['id'],
+ 1,
+ $attr['last']
+ );
+
+ foreach ($events as $event) {
+ $args = array('source' => $cal['id'], 'update' => $this->_client_event($event));
+ $this->rc->output->command('plugin.refresh_calendar', $args);
+ }
+ }
+ }
+
+ /**
* Handler for pending_alarms plugin hook triggered by the calendar module on keep-alive requests.
* This will check for pending notifications and pass them to the client
*/
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 72243e0..d5a4308 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -2103,6 +2103,20 @@ function rcube_calendar_ui(settings)
fc.fullCalendar('removeEvents', function(e){ return e.temp; });
};
+ // modify query parameters for refresh requests
+ this.before_refresh = function(query)
+ {
+ var view = fc.fullCalendar('getView');
+
+ query.start = date2unixtime(view.visStart);
+ query.end = date2unixtime(view.visEnd);
+
+ if (this.search_query)
+ query.q = this.search_query;
+
+ return query;
+ };
+
/*** event searching ***/
@@ -2797,6 +2811,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); });
rcmail.addEventListener('plugin.import_success', function(p){ cal.import_success(p); });
rcmail.addEventListener('plugin.import_error', function(p){ cal.import_error(p); });
+ rcmail.addEventListener('requestrefresh', function(q){ return cal.before_refresh(q); });
// let's go
var cal = new rcube_calendar_ui($.extend(rcmail.env.calendar_settings, rcmail.env.libcal_settings));
diff --git a/plugins/calendar/drivers/calendar_driver.php b/plugins/calendar/drivers/calendar_driver.php
index 52de901..c09d8b9 100644
--- a/plugins/calendar/drivers/calendar_driver.php
+++ b/plugins/calendar/drivers/calendar_driver.php
@@ -236,9 +236,11 @@ abstract class calendar_driver
* @param integer Event's new end (unix timestamp)
* @param string Search query (optional)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
+ * @param boolean Include virtual/recurring events (optional)
+ * @param integer Only list events modified since this time (unix timestamp)
* @return array A list of event objects (see header of this file for struct of an event)
*/
- abstract function load_events($start, $end, $query = null, $calendars = null);
+ abstract function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null);
/**
* Get a list of pending alarms to be displayed to the user
diff --git a/plugins/calendar/drivers/database/database_driver.php b/plugins/calendar/drivers/database/database_driver.php
index 8cd363c..a2cb903 100644
--- a/plugins/calendar/drivers/database/database_driver.php
+++ b/plugins/calendar/drivers/database/database_driver.php
@@ -724,7 +724,7 @@ class database_driver extends calendar_driver
*
* @see calendar_driver::load_events()
*/
- public function load_events($start, $end, $query = null, $calendars = null)
+ public function load_events($start, $end, $query = null, $calendars = null, $virtual = 1, $modifiedsince = null)
{
if (empty($calendars))
$calendars = array_keys($this->calendars);
@@ -742,6 +742,12 @@ class database_driver extends calendar_driver
$sql_add = 'AND (' . join(' OR ', $sql_query) . ')';
}
+ if (!$virtual)
+ $sql_arr .= ' AND e.recurrence_id = 0';
+
+ if ($modifiedsince)
+ $sql_add .= ' AND e.changed >= ' . $this->rc->db->quote(date('Y-m-d H:i:s', $modifiedsince));
+
$events = array();
if (!empty($calendar_ids)) {
$result = $this->rc->db->query(sprintf(
diff --git a/plugins/calendar/drivers/kolab/kolab_driver.php b/plugins/calendar/drivers/kolab/kolab_driver.php
index b8171b7..0722d23 100644
--- a/plugins/calendar/drivers/kolab/kolab_driver.php
+++ b/plugins/calendar/drivers/kolab/kolab_driver.php
@@ -733,20 +733,25 @@ class kolab_driver extends calendar_driver
* @param integer Event's new end (unix timestamp)
* @param string Search query (optional)
* @param mixed List of calendar IDs to load events from (either as array or comma-separated string)
- * @param boolean Strip virtual events (optional)
+ * @param boolean Include virtual events (optional)
+ * @param integer Only list events modified since this time (unix timestamp)
* @return array A list of event records
*/
- public function load_events($start, $end, $search = null, $calendars = null, $virtual = 1)
+ public function load_events($start, $end, $search = null, $calendars = null, $virtual = 1, $modifiedsince = null)
{
if ($calendars && is_string($calendars))
$calendars = explode(',', $calendars);
+ $query = array();
+ if ($modifiedsince)
+ $query[] = array('changed', '>=', $modifiedsince);
+
$events = $categories = array();
foreach (array_keys($this->calendars) as $cid) {
if ($calendars && !in_array($cid, $calendars))
continue;
- $events = array_merge($events, $this->calendars[$cid]->list_events($start, $end, $search, $virtual));
+ $events = array_merge($events, $this->calendars[$cid]->list_events($start, $end, $search, $virtual, $query));
$categories += $this->calendars[$cid]->categories;
}
commit f35a6119c071d5c15c16cde1949979cb0f8f6fe2
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 16 13:01:55 2013 +0200
Catch errors on iCal import and provide appropriate feedback to the user (#2353)
diff --git a/plugins/calendar/calendar.php b/plugins/calendar/calendar.php
index f038caf..11821d7 100644
--- a/plugins/calendar/calendar.php
+++ b/plugins/calendar/calendar.php
@@ -975,10 +975,18 @@ class calendar extends rcube_plugin
if (!$err && $_FILES['_data']['tmp_name']) {
$calendar = get_input_value('calendar', RCUBE_INPUT_GPC);
- $events = $this->get_ical()->import_from_file($_FILES['_data']['tmp_name']);
-
- $count = $errors = 0;
$rangestart = $_REQUEST['_range'] ? date_create("now -" . intval($_REQUEST['_range']) . " months") : 0;
+ $count = $errors = 0;
+
+ try {
+ $events = $this->get_ical()->import_from_file($_FILES['_data']['tmp_name'], 'UTF-8', true);
+ }
+ catch (Exception $e) {
+ $errors = 1;
+ $msg = $e->getMessage();
+ $events = array();
+ }
+
foreach ($events as $event) {
// TODO: correctly handle recurring events which start before $rangestart
if ($event['end'] < $rangestart && (!$event['recurrence'] || ($event['recurrence']['until'] && $event['recurrence']['until'] < $rangestart)))
@@ -1000,8 +1008,9 @@ class calendar extends rcube_plugin
$this->rc->output->command('display_message', $this->gettext('importnone'), 'notice');
$this->rc->output->command('plugin.import_success', array('source' => $calendar));
}
- else
- $this->rc->output->command('display_message', $this->gettext('importerror'), 'error');
+ else {
+ $this->rc->output->command('plugin.import_error', array('message' => $this->gettext('importerror') . ($msg ? ': ' . $msg : '')));
+ }
}
else {
if ($err == UPLOAD_ERR_INI_SIZE || $err == UPLOAD_ERR_FORM_SIZE) {
@@ -1012,7 +1021,7 @@ class calendar extends rcube_plugin
$msg = rcube_label('fileuploaderror');
}
- $this->rc->output->command('display_message', $msg, 'error');
+ $this->rc->output->command('plugin.import_error', array('message' => $msg));
$this->rc->output->command('plugin.unlock_saving', false);
}
diff --git a/plugins/calendar/calendar_ui.js b/plugins/calendar/calendar_ui.js
index 986113f..72243e0 100644
--- a/plugins/calendar/calendar_ui.js
+++ b/plugins/calendar/calendar_ui.js
@@ -1981,10 +1981,17 @@ function rcube_calendar_ui(settings)
if (form && form.elements._data.value) {
rcmail.async_upload_form(form, 'import_events', function(e) {
rcmail.set_busy(false, null, me.saving_lock);
+ $('.ui-dialog-buttonpane button', $dialog.parent()).button('enable');
+
+ // display error message if no sophisticated response from server arrived (e.g. iframe load error)
+ if (me.import_succeeded === null)
+ rcmail.display_message(rcmail.get_label('importerror', 'calendar'), 'error');
});
// display upload indicator
+ me.import_succeeded = null;
me.saving_lock = rcmail.set_busy(true, 'uploading');
+ $('.ui-dialog-buttonpane button', $dialog.parent()).button('disable');
}
};
@@ -1999,6 +2006,7 @@ function rcube_calendar_ui(settings)
closeOnEscape: false,
title: rcmail.gettext('importevents', 'calendar'),
close: function() {
+ $('.ui-dialog-buttonpane button', $dialog.parent()).button('enable');
$dialog.dialog("destroy").hide();
},
buttons: buttons,
@@ -2010,6 +2018,7 @@ function rcube_calendar_ui(settings)
// callback from server if import succeeded
this.import_success = function(p)
{
+ this.import_succeeded = true;
$("#eventsimport:ui-dialog").dialog('close');
rcmail.set_busy(false, null, me.saving_lock);
rcmail.gui_objects.importform.reset();
@@ -2018,6 +2027,13 @@ function rcube_calendar_ui(settings)
this.refresh(p);
};
+ // callback from server to report errors on import
+ this.import_error = function(p)
+ {
+ this.import_succeeded = false;
+ rcmail.display_message(p.message || rcmail.get_label('importerror', 'calendar'), 'error');
+ }
+
// show URL of the given calendar in a dialog box
this.showurl = function(calendar)
{
@@ -2780,6 +2796,7 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.addEventListener('plugin.unlock_saving', function(p){ cal.unlock_saving(); });
rcmail.addEventListener('plugin.refresh_calendar', function(p){ cal.refresh(p); });
rcmail.addEventListener('plugin.import_success', function(p){ cal.import_success(p); });
+ rcmail.addEventListener('plugin.import_error', function(p){ cal.import_error(p); });
// let's go
var cal = new rcube_calendar_ui($.extend(rcmail.env.calendar_settings, rcmail.env.libcal_settings));
commit fa9921726c07d273b567855351f7d70a6533c206
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 28 09:33:09 2013 +0100
Reduce font size of inactive tags (#2374)
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index 1b51caf..7a1df99 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -190,6 +190,7 @@ body.attachmentwin #topnav .topright {
#tagslist li.inactive {
color: #89b3be;
padding-right: 0.6em;
+ font-size: 80%;
/* display: none; */
}
commit dd26bb533301f99d327b561179a6a64853b7e644
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 19:26:22 2013 +0200
Re-calculate tag counts after updating a task item
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index c9146f2..8fd1889 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -601,6 +601,21 @@ function rcube_tasklist_ui(settings)
*/
function update_tagcloud(counts)
{
+ // compute counts first by iterating over all visible task items
+ if (typeof counts == 'undefined') {
+ counts = {};
+ $('li.taskitem', rcmail.gui_objects.resultlist).each(function(i,li){
+ var t, id = $(li).attr('rel'),
+ rec = listdata[id];
+ for (var j=0; rec && rec.tags && j < rec.tags.length; j++) {
+ t = rec.tags[j];
+ if (typeof counts[t] == 'undefined')
+ counts[t] = 0;
+ counts[t]++;
+ }
+ });
+ }
+
$(rcmail.gui_objects.tagslist).children('li').each(function(i,li){
var elem = $(li), tag = elem.attr('rel'),
count = counts[tag] || 0;
@@ -741,6 +756,7 @@ function rcube_tasklist_ui(settings)
}
append_tags(rec.tags || []);
+ update_tagcloud();
fix_tree_toggles();
}
commit fb0e0417f142847b5c376969d6a807cb3e429c8c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 19:17:48 2013 +0200
Small fixes for IE browsers
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 0293d8f..c9146f2 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -585,7 +585,7 @@ function rcube_tasklist_ui(settings)
helper: tag_draggable_helper,
start: tag_draggable_start,
appendTo: 'body',
- cursor: 'pointer',
+ cursor: 'pointer'
});
});
@@ -1073,7 +1073,7 @@ function rcube_tasklist_ui(settings)
// append inherited tags
if (itags.length) {
$.each(itags, function(i,val){
- if (!rec.tags || rec.tags.indexOf(val) < 0)
+ if (!rec.tags || $.inArray(val, rec.tags) < 0)
$('<span>').addClass('tag-element inherit').html(Q(val)).appendTo(taglist);
});
// re-sort tags list
commit 31abc5923978ec29bf04bcef973512568ff46b8e
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 18:52:59 2013 +0200
Minor CSS improvements + add missing text label
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index a1d381b..ba331be 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -51,6 +51,7 @@ $labels['listactions'] = 'List options...';
$labels['listname'] = 'Name';
$labels['showalarms'] = 'Show alarms';
$labels['import'] = 'Import';
+$labels['viewoptions'] = 'View options';
$labels['focusview'] = 'View only this list';
// date words
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index ab74a1f..1b51caf 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -258,12 +258,11 @@ body.attachmentwin #topnav .topright {
cursor: pointer;
}
-#tasklists li span.handle:hover {
+#tasklists li:hover span.handle {
background-position: -20px -101px;
}
-#tasklists li.focusview span.handle,
-#tasklists li.focusview span.handle:hover {
+#tasklists li.focusview span.handle {
background-position: -2px -101px;
}
@@ -539,8 +538,8 @@ body.attachmentwin #topnav .topright {
}
.tag-draghelper li.tag {
- list-style: none;
- font-size: 100%;
+ list-style: none;
+ font-size: 100%;
}
.taskhead .date {
commit 05ce4c8e848be03c60434c7454a8da4ba45f2616
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 16:29:33 2013 +0200
Add 'focusview' mode to quickly reduce the view to tasks from the selected list only (#2380)
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index 57d6c3c..a1d381b 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -51,6 +51,7 @@ $labels['listactions'] = 'List options...';
$labels['listname'] = 'Name';
$labels['showalarms'] = 'Show alarms';
$labels['import'] = 'Import';
+$labels['focusview'] = 'View only this list';
// date words
$labels['on'] = 'on';
diff --git a/plugins/tasklist/skins/larry/sprites.png b/plugins/tasklist/skins/larry/sprites.png
index b20b2db..5c6b9fd 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 9efc18b..ab74a1f 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -218,7 +218,7 @@ body.attachmentwin #topnav .topright {
#tasklists li {
margin: 0;
height: 20px;
- padding: 6px 8px 2px;
+ padding: 6px 8px 2px 6px;
display: block;
position: relative;
white-space: nowrap;
@@ -235,10 +235,13 @@ body.attachmentwin #topnav .topright {
#tasklists li span.listname {
display: block;
+ position: absolute;
+ top: 7px;
+ left: 26px;
+ right: 26px;
cursor: default;
padding-bottom: 2px;
padding-right: 30px;
- margin-right: 20px;
color: #004458;
overflow: hidden;
text-overflow: ellipsis;
@@ -247,7 +250,21 @@ body.attachmentwin #topnav .topright {
}
#tasklists li span.handle {
- display: none;
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ margin-right: 4px;
+ background: url(sprites.png) -200px 0 no-repeat;
+ cursor: pointer;
+}
+
+#tasklists li span.handle:hover {
+ background-position: -20px -101px;
+}
+
+#tasklists li.focusview span.handle,
+#tasklists li.focusview span.handle:hover {
+ background-position: -2px -101px;
}
#tasklists li.selected span.listname {
@@ -278,6 +295,11 @@ body.attachmentwin #topnav .topright {
color: #aaa;
}
+#tasklists li.virtual span.handle {
+ background: none;
+ cursor: default;
+}
+
#tasklists li input {
position: absolute;
top: 5px;
@@ -610,7 +632,7 @@ ul.toolbarmenu li span.collapse {
}
ul.toolbarmenu li span.add {
- background-position: 0 -100px;
+ background-position: 0 -302px;
}
ul.toolbarmenu li span.expand {
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 68ea117..0293d8f 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -54,6 +54,7 @@ function rcube_tasklist_ui(settings)
var filtermask = FILTER_MASK_ALL;
var loadstate = { filter:-1, lists:'', search:null };
var idcount = 0;
+ var focusview;
var saving_lock;
var ui_loading;
var taskcounts = {};
@@ -128,7 +129,8 @@ function rcube_tasklist_ui(settings)
// register server callbacks
rcmail.addEventListener('plugin.data_ready', data_ready);
- rcmail.addEventListener('plugin.refresh_task', update_taskitem);
+ rcmail.addEventListener('plugin.update_task', update_taskitem);
+ rcmail.addEventListener('plugin.refresh_tasks', function(p) { update_taskitem(p, true); });
rcmail.addEventListener('plugin.update_counts', update_counts);
rcmail.addEventListener('plugin.insert_tasklist', insert_list);
rcmail.addEventListener('plugin.update_tasklist', update_list);
@@ -686,11 +688,11 @@ function rcube_tasklist_ui(settings)
/**
* Callback from server to update a single task item
*/
- function update_taskitem(rec)
+ function update_taskitem(rec, filter)
{
// handle a list of task records
if ($.isArray(rec)) {
- $.each(rec, function(i,r){ update_taskitem(r); });
+ $.each(rec, function(i,r){ update_taskitem(r, filter); });
return;
}
@@ -730,10 +732,13 @@ function rcube_tasklist_ui(settings)
}
}
- if (list.active)
- render_task(rec, oldid);
- else
+ if (list.active) {
+ if (!filter || match_filter(rec, {}))
+ render_task(rec, oldid);
+ }
+ else {
$('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).remove();
+ }
append_tags(rec.tags || []);
fix_tree_toggles();
@@ -1525,7 +1530,11 @@ function rcube_tasklist_ui(settings)
return cache[rec.id];
}
- var match = !filtermask || (filtermask & rec.mask) > 0
+ var match = !filtermask || (filtermask & rec.mask) > 0;
+
+ // in focusview mode, only tasks from the selected list are allowed
+ if (focusview && rec.list != focusview)
+ match = false;
if (match && tagsfilter.length) {
match = rec.tags && rec.tags.length;
@@ -1847,6 +1856,11 @@ function rcube_tasklist_ui(settings)
if (!this.checked) remove_tasks(id);
else list_tasks(null);
rcmail.http_post('tasklist', { action:'subscribe', l:{ id:id, active:me.tasklists[id].active?1:0 } });
+
+ // disable focusview
+ if (!this.checked && focusview == id) {
+ set_focusview(null);
+ }
}
}).data('id', id).get(0).checked = me.tasklists[id].active || false;
@@ -1855,6 +1869,15 @@ function rcube_tasklist_ui(settings)
rcmail.select_folder(id, 'rcmlitasklist');
rcmail.enable_command('list-edit', 'list-remove', 'list-import', me.tasklists[id].editable);
me.selected_list = id;
+
+ // click on handle icon toggles focusview
+ if (e.target.className == 'handle') {
+ set_focusview(focusview == id ? null : id)
+ }
+ // disable focusview when selecting another list
+ else if (focusview && id != focusview) {
+ set_focusview(null);
+ }
})
.dblclick(function(e){
list_edit_dialog($(this).data('id'));
@@ -1864,6 +1887,31 @@ function rcube_tasklist_ui(settings)
.addClass(me.tasklists[id].editable ? null : 'readonly');
}
+ /**
+ * Enable/disable focusview mode for the given list
+ */
+ function set_focusview(id)
+ {
+ if (focusview && focusview != id)
+ $(rcmail.get_folder_li(focusview, 'rcmlitasklist')).removeClass('focusview');
+
+ focusview = id;
+
+ // activate list if necessary
+ if (focusview && !me.tasklists[id].active) {
+ $('input', rcmail.get_folder_li(id, 'rcmlitasklist')).get(0).checked = true;
+ me.tasklists[id].active = true;
+ fetch_counts();
+ }
+
+ // update list
+ list_tasks(null);
+
+ if (focusview) {
+ $(rcmail.get_folder_li(focusview, 'rcmlitasklist')).addClass('focusview');
+ }
+ }
+
// init dialog by default
init_taskedit();
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 68d82c4..aaeea95 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -281,7 +281,7 @@ class tasklist extends rcube_plugin
foreach ($refresh as $i => $r)
$this->encode_task($refresh[$i]);
}
- $this->rc->output->command('plugin.refresh_task', $refresh);
+ $this->rc->output->command('plugin.update_task', $refresh);
}
}
@@ -751,7 +751,7 @@ class tasklist extends rcube_plugin
$updates = $this->driver->list_tasks($filter, $lists);
if (!empty($updates)) {
- $this->rc->output->command('plugin.refresh_task', $this->tasks_data($updates, 255, $tags));
+ $this->rc->output->command('plugin.refresh_tasks', $this->tasks_data($updates, 255, $tags), true);
// update counts
$counts = $this->driver->count_tasks($lists);
diff --git a/plugins/tasklist/tasklist_ui.php b/plugins/tasklist/tasklist_ui.php
index dab9b12..21faba3 100644
--- a/plugins/tasklist/tasklist_ui.php
+++ b/plugins/tasklist/tasklist_ui.php
@@ -117,7 +117,7 @@ class tasklist_ui
$li .= html::tag('li', array('id' => 'rcmlitasklist' . $html_id, 'class' => $class),
($prop['virtual'] ? '' : html::tag('input', array('type' => 'checkbox', 'name' => '_list[]', 'value' => $id, 'checked' => $prop['active']))) .
- html::span('handle', ' ') .
+ html::span(array('class' => 'handle', 'title' => $this->plugin->gettext('focusview')), ' ') .
html::span(array('class' => 'listname', 'title' => $title), $prop['listname']));
}
commit bc5b971642399312d3a0012945a3b62d2a236b7d
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 14:00:36 2013 +0200
Assign tags by drag & dropping them onto tasks (#2389); some fixes concerning saving and resorting tasks
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index fadb62b..9efc18b 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -189,13 +189,10 @@ body.attachmentwin #topnav .topright {
#tagslist li.inactive {
color: #89b3be;
+ padding-right: 0.6em;
/* display: none; */
}
-#tagslist li.inactive .count {
- display: none;
-}
-
#tagslist li .count {
position: relative;
top: -1px;
@@ -213,6 +210,11 @@ body.attachmentwin #topnav .topright {
border-radius: 8px;
}
+.tag-draghelper .tag .count,
+#tagslist li.inactive .count {
+ display: none;
+}
+
#tasklists li {
margin: 0;
height: 20px;
@@ -504,6 +506,7 @@ body.attachmentwin #topnav .topright {
text-align: right;
}
+.tag-draghelper .tag,
.taskhead .tags .tag {
font-size: 85%;
background: #d9ecf4;
@@ -513,6 +516,11 @@ body.attachmentwin #topnav .topright {
margin-right: 3px;
}
+.tag-draghelper li.tag {
+ list-style: none;
+ font-size: 100%;
+}
+
.taskhead .date {
position: absolute;
top: 4px;
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index d318011..68ea117 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -64,6 +64,8 @@ function rcube_tasklist_ui(settings)
var search_request;
var search_query;
var completeness_slider;
+ var task_draghelper;
+ var tag_draghelper;
var me = this;
// general datepicker settings
@@ -337,7 +339,8 @@ function rcube_tasklist_ui(settings)
$(input).datepicker('widget').find('button.ui-datepicker-close')
.html(rcmail.gettext('nodate','tasklist'))
.attr('onclick', '')
- .click(function(e){
+ .unbind('click')
+ .bind('click', function(e){
$(input).datepicker('setDate', null).datepicker('hide');
});
}, 1);
@@ -570,8 +573,19 @@ function rcube_tasklist_ui(settings)
// append new tags to tag cloud
$.each(newtags, function(i, tag){
- $('<li>').attr('rel', tag).data('value', tag).html(Q(tag) + '<span class="count"></span>').appendTo(rcmail.gui_objects.tagslist);
- });
+ $('<li>').attr('rel', tag).data('value', tag)
+ .html(Q(tag) + '<span class="count"></span>')
+ .appendTo(rcmail.gui_objects.tagslist)
+ .draggable({
+ addClasses: false,
+ revert: 'invalid',
+ revertDuration: 300,
+ helper: tag_draggable_helper,
+ start: tag_draggable_start,
+ appendTo: 'body',
+ cursor: 'pointer',
+ });
+ });
// re-sort tags list
$(rcmail.gui_objects.tagslist).children('li').sortElements(function(a,b){
@@ -595,6 +609,59 @@ function rcube_tasklist_ui(settings)
});
}
+ /* Helper functions for drag & drop functionality of tags */
+
+ function tag_draggable_helper()
+ {
+ if (!tag_draghelper)
+ tag_draghelper = $('<div class="tag-draghelper"></div>');
+ else
+ tag_draghelper.html('');
+
+ $(this).clone().addClass('tag').appendTo(tag_draghelper);
+ return tag_draghelper;
+ }
+
+ function tag_draggable_start(event, ui)
+ {
+ $('.taskhead').droppable({
+ hoverClass: 'droptarget',
+ accept: tag_droppable_accept,
+ drop: tag_draggable_dropped,
+ addClasses: false
+ });
+ }
+
+ function tag_droppable_accept(draggable)
+ {
+ if (rcmail.busy)
+ return false;
+
+ var tag = draggable.data('value'),
+ drop_id = $(this).data('id'),
+ drop_rec = listdata[drop_id];
+
+ // target already has this tag assigned
+ if (!drop_rec || (drop_rec.tags && $.inArray(tag, drop_rec.tags) >= 0)) {
+ return false;
+ }
+
+ return true;
+ }
+
+ function tag_draggable_dropped(event, ui)
+ {
+ var drop_id = $(this).data('id'),
+ tag = ui.draggable.data('value'),
+ rec = listdata[drop_id];
+
+ if (rec && rec.id) {
+ if (!rec.tags) rec.tags = [];
+ rec.tags.push(tag);
+ save_task(rec, 'edit');
+ }
+ }
+
/**
*
*/
@@ -721,10 +788,10 @@ function rcube_tasklist_ui(settings)
revert: 'invalid',
addClasses: false,
cursorAt: { left:-10, top:12 },
- helper: draggable_helper,
+ helper: task_draggable_helper,
appendTo: 'body',
- start: draggable_start,
- stop: draggable_stop,
+ start: task_draggable_start,
+ stop: task_draggable_stop,
revertDuration: 300
});
@@ -769,7 +836,7 @@ function rcube_tasklist_ui(settings)
*/
function resort_task(rec, li, animated)
{
- var dir = 0, index, slice, next_li, next_id, next_rec;
+ var dir = 0, index, slice, cmp, next_li, next_id, next_rec, insert_after, past_myself;
// animated moving
var insert_animated = function(li, before, after) {
@@ -795,33 +862,36 @@ function rcube_tasklist_ui(settings)
}
// find the right place to insert the task item
- li.siblings().each(function(i, elem){
+ li.parent().children('.taskitem').each(function(i, elem){
next_li = $(elem);
next_id = next_li.attr('rel');
next_rec = listdata[next_id];
if (next_id == rec.id) {
- next_li = null;
+ past_myself = true;
return 1; // continue
}
- if (next_rec && task_cmp(rec, next_rec) > 0) {
+ cmp = next_rec ? task_cmp(rec, next_rec) : 0;
+
+ if (cmp > 0 || (cmp == 0 && !past_myself)) {
+ insert_after = next_li;
return 1; // continue;
}
- else if (next_rec && next_li && task_cmp(rec, next_rec) < 0) {
+ else if (next_li && cmp < 0) {
if (animated) insert_animated(li, next_li);
else li.insertBefore(next_li);
- next_li = null;
- return false;
+ index = $.inArray(next_id, listindex);
+ return false; // break
}
});
- index = $.inArray(next_id, listindex);
+ if (insert_after) {
+ if (animated) insert_animated(li, null, insert_after);
+ else li.insertAfter(insert_after);
- if (next_li) {
- if (animated) insert_animated(li, null, next_li);
- else li.insertAfter(next_li);
- index++;
+ next_id = insert_after.attr('rel');
+ index = $.inArray(next_id, listindex);
}
// insert into list index
@@ -865,20 +935,20 @@ function rcube_tasklist_ui(settings)
/* Helper functions for drag & drop functionality */
- function draggable_helper()
+ function task_draggable_helper()
{
- if (!draghelper)
- draghelper = $('<div class="taskitem-draghelper">✔</div>');
+ if (!task_draghelper)
+ task_draghelper = $('<div class="taskitem-draghelper">✔</div>');
- return draghelper;
+ return task_draghelper;
}
- function draggable_start(event, ui)
+ function task_draggable_start(event, ui)
{
$('.taskhead, #rootdroppable, #'+rcmail.gui_objects.folderlist.id+' li').droppable({
hoverClass: 'droptarget',
- accept: droppable_accept,
- drop: draggable_dropped,
+ accept: task_droppable_accept,
+ drop: task_draggable_dropped,
addClasses: false
});
@@ -886,13 +956,13 @@ function rcube_tasklist_ui(settings)
$('#rootdroppable').show();
}
- function draggable_stop(event, ui)
+ function task_draggable_stop(event, ui)
{
$(this).parent().removeClass('dragging');
$('#rootdroppable').hide();
}
- function droppable_accept(draggable)
+ function task_droppable_accept(draggable)
{
if (rcmail.busy)
return false;
@@ -924,7 +994,7 @@ function rcube_tasklist_ui(settings)
return true;
}
- function draggable_dropped(event, ui)
+ function task_draggable_dropped(event, ui)
{
var drop_id = $(this).data('id'),
task_id = ui.draggable.data('id'),
@@ -1220,6 +1290,9 @@ function rcube_tasklist_ui(settings)
if (!me.selected_task.list && list.id)
me.selected_task.list = list.id;
+ if (!me.selected_task.tags.length)
+ me.selected_task.tags = '';
+
if (save_task(me.selected_task, action))
$dialog.dialog('close');
};
commit b52df5e51e8159cc80a2a6d11deceacf27471da2
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 11:24:26 2013 +0200
List (inherited) tags only once
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 198ba82..d318011 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -998,7 +998,8 @@ function rcube_tasklist_ui(settings)
// append inherited tags
if (itags.length) {
$.each(itags, function(i,val){
- $('<span>').addClass('tag-element inherit').html(Q(val)).appendTo(taglist);
+ if (!rec.tags || rec.tags.indexOf(val) < 0)
+ $('<span>').addClass('tag-element inherit').html(Q(val)).appendTo(taglist);
});
// re-sort tags list
$(taglist).children().sortElements(function(a,b){
@@ -1498,7 +1499,7 @@ function rcube_tasklist_ui(settings)
}
}
- return itags;
+ return $.unqiqueStrings(itags);
}
/**
@@ -1816,6 +1817,22 @@ jQuery.fn.sortElements = (function(){
};
})();
+// equivalent to $.unique() but working on arrays of strings
+jQuery.unqiqueStrings = (function() {
+ return function(arr) {
+ var hash = {}, out = [];
+
+ for (var i = 0; i < arr.length; i++) {
+ hash[arr[i]] = 0;
+ }
+ for (var val in hash) {
+ out.push(val);
+ }
+
+ return out;
+ };
+})();
+
/* tasklist plugin UI initialization */
var rctasks;
commit 66371500664f1d4ddf4743249b676d0d41b09526
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 24 10:59:01 2013 +0200
Fix assignment to a new list in edit dialog
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 2c1a921..198ba82 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1208,7 +1208,7 @@ function rcube_tasklist_ui(settings)
}
// task assigned to a new list
- if (me.selected_task.list && me.selected_task.list != rec.list) {
+ if (me.selected_task.list && listdata[id] && me.selected_task.list != listdata[id].list) {
me.selected_task._fromlist = rec.list;
}
commit f8220576d8e0cbcc6f22b9b7dbfe08eb070f604b
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 23 18:10:18 2013 +0200
Show the number of occurences for each tag (#2365) and reduce visibility of those tags not matching any tasks (#2374) in the current selection
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index 70ab8da..fadb62b 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -180,12 +180,39 @@ body.attachmentwin #topnav .topright {
#tagslist li {
display: inline-block;
color: #004458;
- margin-right: 0.5em;
+ padding-right: 0.2em;
+ margin-right: 0.3em;
margin-bottom: 0.4em;
min-width: 1.2em;
cursor: pointer;
}
+#tagslist li.inactive {
+ color: #89b3be;
+/* display: none; */
+}
+
+#tagslist li.inactive .count {
+ display: none;
+}
+
+#tagslist li .count {
+ position: relative;
+ top: -1px;
+ margin-left: 5px;
+ padding: 0.15em 0.5em;
+ font-size: 80%;
+ font-weight: bold;
+ color: #59838e;
+ background: #c7e3ef;
+ box-shadow: inset 0 1px 1px 0 #b0ccd7;
+ -o-box-shadow: inset 0 1px 1px 0 #b0ccd7;
+ -webkit-box-shadow: inset 0 1px 1px 0 #b0ccd7;
+ -moz-box-shadow: inset 0 1px 1px 0 #b0ccd7;
+ border-color: #b0ccd7;
+ border-radius: 8px;
+}
+
#tasklists li {
margin: 0;
height: 20px;
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
index b85f7fb..52463e0 100644
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -157,6 +157,8 @@ $(document).ready(function(e){
UI.init();
new rcube_splitter({ id:'taskviewsplitter', p1:'#sidebar', p2:'#mainview-right',
orientation:'v', relative:true, start:240, min:180, size:16, offset:2 }).init();
+ new rcube_splitter({ id:'taskviewsplitterv', p1:'#tagsbox', p2:'#tasklistsbox',
+ orientation:'h', relative:true, start:242, min:120, size:16, offset:6 }).init();
});
</script>
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 742f23a..2c1a921 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -474,8 +474,9 @@ function rcube_tasklist_ui(settings)
listdata[listdata[id].parent_id].children.push(id);
}
- render_tasklist();
append_tags(response.tags || []);
+ render_tasklist();
+
rcmail.set_busy(false, 'loading', ui_loading);
}
@@ -488,6 +489,7 @@ function rcube_tasklist_ui(settings)
var id, rec,
count = 0,
cache = {},
+ activetags = {},
msgbox = $('#listmessagebox').hide(),
list = $(rcmail.gui_objects.resultlist).html('');
@@ -497,10 +499,19 @@ function rcube_tasklist_ui(settings)
if (match_filter(rec, cache)) {
render_task(rec);
count++;
+
+ // keep a list of tags from all visible tasks
+ for (var t, j=0; rec.tags && j < rec.tags.length; j++) {
+ t = rec.tags[j];
+ if (typeof activetags[t] == 'undefined')
+ activetags[t] = 0;
+ activetags[t]++;
+ }
}
}
fix_tree_toggles();
+ update_tagcloud(activetags);
if (!count)
msgbox.html(rcmail.gettext('notasksfound','tasklist')).show();
@@ -559,7 +570,7 @@ function rcube_tasklist_ui(settings)
// append new tags to tag cloud
$.each(newtags, function(i, tag){
- $('<li>').attr('rel', tag).data('value', tag).html(Q(tag)).appendTo(rcmail.gui_objects.tagslist);
+ $('<li>').attr('rel', tag).data('value', tag).html(Q(tag) + '<span class="count"></span>').appendTo(rcmail.gui_objects.tagslist);
});
// re-sort tags list
@@ -569,6 +580,22 @@ function rcube_tasklist_ui(settings)
}
/**
+ * Display the given counts to each tag and set those inactive which don't
+ * have any matching tasks in the current view.
+ */
+ function update_tagcloud(counts)
+ {
+ $(rcmail.gui_objects.tagslist).children('li').each(function(i,li){
+ var elem = $(li), tag = elem.attr('rel'),
+ count = counts[tag] || 0;
+
+ elem.children('.count').html(count+'');
+ if (count == 0) elem.addClass('inactive');
+ else elem.removeClass('inactive');
+ });
+ }
+
+ /**
*
*/
function update_counts(counts)
commit d0315100ea8bc7e42be9e62957d3b93289311098
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Wed Oct 23 17:12:42 2013 +0200
Add UI elements to expand/collapse all tasks (#2291)
diff --git a/plugins/tasklist/skins/larry/sprites.png b/plugins/tasklist/skins/larry/sprites.png
index 5224f6f..b20b2db 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 caa5067..70ab8da 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -66,7 +66,7 @@ body.attachmentwin #topnav .topright {
}
#taskselector {
- margin: -4px 0 0;
+ margin: -4px 40px 0 0;
padding: 0;
}
@@ -336,6 +336,27 @@ body.attachmentwin #topnav .topright {
background: -ms-linear-gradient(top, #eee 0%, #dfdfdf 100%);
background: linear-gradient(top, #eee 0%, #dfdfdf 100%);
border-bottom: 1px solid #ccc;
+ position: relative;
+}
+
+#tasksview .buttonbar .buttonbar-right {
+ position: absolute;
+ top: 6px;
+ right: 8px;
+}
+
+.buttonbar-right .listmenu {
+ display: inline-block;
+ cursor: pointer;
+}
+
+.buttonbar-right .listmenu .inner {
+ display: inline-block;
+ height: 18px;
+ width: 20px;
+ padding: 0;
+ background: url(sprites.png) 0 -237px no-repeat;
+ text-indent: -5000px;
}
#thelist {
@@ -547,11 +568,24 @@ body.attachmentwin #topnav .topright {
border-top: 1px solid #219de6;
}
-ul.toolbarmenu li span.add {
+ul.toolbarmenu li span.add,
+ul.toolbarmenu li span.expand,
+ul.toolbarmenu li span.collapse {
background-image: url(sprites.png);
+}
+
+ul.toolbarmenu li span.add {
background-position: 0 -100px;
}
+ul.toolbarmenu li span.expand {
+ background-position: 0 -258px;
+}
+
+ul.toolbarmenu li span.collapse {
+ background-position: 0 -280px;
+}
+
ul.toolbarmenu li span.delete {
background-position: 0 -1508px;
}
diff --git a/plugins/tasklist/skins/larry/templates/mainview.html b/plugins/tasklist/skins/larry/templates/mainview.html
index 9196d02..b85f7fb 100644
--- a/plugins/tasklist/skins/larry/templates/mainview.html
+++ b/plugins/tasklist/skins/larry/templates/mainview.html
@@ -59,6 +59,10 @@
<li class="nodate"><a href="#nodate"><roundcube:label name="tasklist.nodate" ucfirst="true" /></a></li>
<li class="complete"><a href="#complete"><roundcube:label name="tasklist.complete" /><span class="count"></span></a></li>
</ul>
+
+ <div class="buttonbar-right">
+ <roundcube:button name="taskviewmenulink" id="taskviewmenulink" type="link" title="tasklist.viewoptions" class="listmenu viewoptions" onclick="UI.show_popup('taskviewmenu');return false" innerClass="inner" content="⚙" />
+ </div>
</div>
<div class="scroller">
@@ -92,6 +96,13 @@
</ul>
</div>
+<div id="taskviewmenu" class="popupmenu">
+ <ul class="toolbarmenu">
+ <li><roundcube:button command="expand-all" label="expand-all" class="icon" classAct="icon active" innerclass="icon expand" /></li>
+ <li><roundcube:button command="collapse-all" label="collapse-all" class="icon" classAct="icon active" innerclass="icon collapse" /></li>
+ </ul>
+</div>
+
<div id="taskshow">
<div class="form-section" id="task-parent-title"></div>
<div class="form-section">
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 631494c..742f23a 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -92,6 +92,7 @@ function rcube_tasklist_ui(settings)
this.add_childtask = add_childtask;
this.quicksearch = quicksearch;
this.reset_search = reset_search;
+ this.expand_collapse = expand_collapse;
this.list_remove = list_remove;
this.list_edit_dialog = list_edit_dialog;
this.unlock_saving = unlock_saving;
@@ -520,6 +521,30 @@ function rcube_tasklist_ui(settings)
}
/**
+ * Expand/collapse all task items with childs
+ */
+ function expand_collapse(expand)
+ {
+ var collapsed = !expand;
+
+ $('.taskitem .childtasks')[(collapsed ? 'hide' : 'show')]();
+ $('.taskitem .childtoggle')
+ .removeClass(collapsed ? 'expanded' : 'collapsed')
+ .addClass(collapsed ? 'collapsed' : 'expanded')
+ .html(collapsed ? '▶' : '▼');
+
+ // store new toggle collapse states
+ var ids = [];
+ for (var id in listdata) {
+ if (listdata[id].children && listdata[id].children.length)
+ ids.push(id);
+ }
+ if (ids.length) {
+ rcmail.http_post('tasks/task', { action:'collapse', t:{ id:ids.join(',') }, collapsed:collapsed?1:0 });
+ }
+ }
+
+ /**
*
*/
function append_tags(taglist)
@@ -1781,6 +1806,8 @@ window.rcmail && rcmail.addEventListener('init', function(evt) {
rcmail.register_command('search', function(){ rctasks.quicksearch(); }, true);
rcmail.register_command('reset-search', function(){ rctasks.reset_search(); }, true);
+ rcmail.register_command('expand-all', function(){ rctasks.expand_collapse(true); }, true);
+ rcmail.register_command('collapse-all', function(){ rctasks.expand_collapse(false); }, true);
rctasks.init();
});
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index e6b5d1c..68d82c4 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -248,13 +248,15 @@ class tasklist extends rcube_plugin
break;
case 'collapse':
- if (intval(get_input_value('collapsed', RCUBE_INPUT_GPC))) {
- $this->collapsed_tasks[] = $rec['id'];
- }
- else {
- $i = array_search($rec['id'], $this->collapsed_tasks);
- if ($i !== false)
- unset($this->collapsed_tasks[$i]);
+ foreach (explode(',', $rec['id']) as $rec_id) {
+ if (intval(get_input_value('collapsed', RCUBE_INPUT_GPC))) {
+ $this->collapsed_tasks[] = $rec_id;
+ }
+ else {
+ $i = array_search($rec_id, $this->collapsed_tasks);
+ if ($i !== false)
+ unset($this->collapsed_tasks[$i]);
+ }
}
$this->rc->user->save_prefs(array('tasklist_collapsed_tasks' => join(',', array_unique($this->collapsed_tasks))));
commit 5c4530245713dab4d7ed38f273dacc31f4699365
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 21 16:38:22 2013 +0200
Refresh tasks list periodically with changes from the server (#2369)
diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
index 8ad776a..3c5ad38 100644
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -286,7 +286,7 @@ class tasklist_database_driver extends tasklist_driver
if ($filter['mask'] & tasklist::FILTER_MASK_COMPLETE)
$sql_add .= ' AND complete=1';
- else // don't show complete tasks by default
+ else if (empty($filter['since'])) // don't show complete tasks by default
$sql_add .= ' AND complete<1';
if ($filter['mask'] & tasklist::FILTER_MASK_FLAGGED)
@@ -301,6 +301,10 @@ class tasklist_database_driver extends tasklist_driver
$sql_add = 'AND (' . join(' OR ', $sql_query) . ')';
}
+ if ($filter['since'] && is_numeric($filter['since'])) {
+ $sql_add .= ' AND changed >= ' . $this->rc->db->quote(date('Y-m-d H:i:s', $filter['since']));
+ }
+
$tasks = array();
if (!empty($list_ids)) {
$result = $this->rc->db->query(sprintf(
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index c630a41..fd3e5c3 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -371,7 +371,7 @@ class tasklist_kolab_driver extends tasklist_driver
$query = array();
if ($filter['mask'] & tasklist::FILTER_MASK_COMPLETE)
$query[] = array('tags','~','x-complete');
- else
+ else if (empty($filter['since']))
$query[] = array('tags','!~','x-complete');
// full text search (only works with cache enabled)
@@ -382,6 +382,10 @@ class tasklist_kolab_driver extends tasklist_driver
}
}
+ if ($filter['since']) {
+ $query[] = array('changed', '>=', $filter['since']);
+ }
+
foreach ($lists as $list_id) {
$folder = $this->folders[$list_id];
foreach ((array)$folder->select($query) as $record) {
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 19db0fe..631494c 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -132,6 +132,7 @@ function rcube_tasklist_ui(settings)
rcmail.addEventListener('plugin.destroy_tasklist', destroy_list);
rcmail.addEventListener('plugin.reload_data', function(){ list_tasks(null); });
rcmail.addEventListener('plugin.unlock_saving', unlock_saving);
+ rcmail.addEventListener('requestrefresh', before_refresh);
// start loading tasks
fetch_counts();
@@ -439,6 +440,19 @@ function rcube_tasklist_ui(settings)
}
/**
+ * Modify query parameters for refresh requests
+ */
+ function before_refresh(query)
+ {
+ query.filter = filtermask == FILTER_MASK_COMPLETE ? FILTER_MASK_COMPLETE : FILTER_MASK_ALL;
+ query.lists = active_lists().join(',');
+ if (search_query)
+ query.q = search_query;
+
+ return query;
+ }
+
+ /**
* Callback if task data from server is ready
*/
function data_ready(response)
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 9d5544c..e6b5d1c 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -90,6 +90,7 @@ class tasklist extends rcube_plugin
$this->register_action('mail2task', array($this, 'mail_message2task'));
$this->register_action('get-attachment', array($this, 'attachment_get'));
$this->register_action('upload', array($this, 'attachment_upload'));
+ $this->add_hook('refresh', array($this, 'refresh'));
$this->collapsed_tasks = array_filter(explode(',', $this->rc->config->get('tasklist_collapsed_tasks', '')));
}
@@ -540,8 +541,17 @@ class tasklist extends rcube_plugin
}
*/
+ $data = $this->tasks_data($this->driver->list_tasks($filter, $lists), $f, $tags);
+ $this->rc->output->command('plugin.data_ready', array('filter' => $f, 'lists' => $lists, 'search' => $search, 'data' => $data, 'tags' => array_values(array_unique($tags))));
+ }
+
+ /**
+ * Prepare and sort the given task records to be sent to the client
+ */
+ private function tasks_data($records, $f, &$tags)
+ {
$data = $tags = $this->task_tree = $this->task_titles = array();
- foreach ($this->driver->list_tasks($filter, $lists) as $rec) {
+ foreach ($records as $rec) {
if ($rec['parent_id']) {
$this->task_tree[$rec['id']] = $rec['parent_id'];
}
@@ -558,7 +568,7 @@ class tasklist extends rcube_plugin
array_walk($data, array($this, 'task_walk_tree'));
usort($data, array($this, 'task_sort_cmp'));
- $this->rc->output->command('plugin.data_ready', array('filter' => $f, 'lists' => $lists, 'search' => $search, 'data' => $data, 'tags' => array_values(array_unique($tags))));
+ return $data;
}
/**
@@ -724,6 +734,28 @@ class tasklist extends rcube_plugin
exit;
}
+ /**
+ * Handler for keep-alive requests
+ * This will check for updated data in active lists and sync them to the client
+ */
+ public function refresh($attr)
+ {
+ $filter = array(
+ 'since' => $attr['last'],
+ 'search' => get_input_value('q', RCUBE_INPUT_GPC),
+ 'mask' => intval(get_input_value('filter', RCUBE_INPUT_GPC)) & self::FILTER_MASK_COMPLETE,
+ );
+ $lists = get_input_value('lists', RCUBE_INPUT_GPC);;
+
+ $updates = $this->driver->list_tasks($filter, $lists);
+ if (!empty($updates)) {
+ $this->rc->output->command('plugin.refresh_task', $this->tasks_data($updates, 255, $tags));
+
+ // update counts
+ $counts = $this->driver->count_tasks($lists);
+ $this->rc->output->command('plugin.update_counts', $counts);
+ }
+ }
/**
* Handler for pending_alarms plugin hook triggered by the calendar module on keep-alive requests.
commit ee52d6e52f1c85c35f85faebb73b05b0c8210741
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 21 14:51:29 2013 +0200
Show inherited tags in task show dialog (#2368)
diff --git a/plugins/tasklist/skins/larry/tasklist.css b/plugins/tasklist/skins/larry/tasklist.css
index 173704d..caa5067 100644
--- a/plugins/tasklist/skins/larry/tasklist.css
+++ b/plugins/tasklist/skins/larry/tasklist.css
@@ -806,6 +806,12 @@ label.block {
/* cursor: pointer; */
}
+.form-section span.tag-element.inherit {
+ color: #666;
+ background: #f2f2f2;
+ border-color: #ddd;
+}
+
.tagedit-list li.tagedit-listelement-old a.tagedit-close,
.tagedit-list li.tagedit-listelement-old a.tagedit-break,
.tagedit-list li.tagedit-listelement-old a.tagedit-delete,
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index e52174b..19db0fe 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -921,10 +921,22 @@ function rcube_tasklist_ui(settings)
$('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%');
$('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : ''));
- var taglist = $('#task-tags')[(rec.tags && rec.tags.length ? 'show' : 'hide')]().children('.task-text').empty();
+ var itags = get_inherited_tags(rec);
+ var taglist = $('#task-tags')[(rec.tags && rec.tags.length || itags.length ? 'show' : 'hide')]().children('.task-text').empty();
if (rec.tags && rec.tags.length) {
$.each(rec.tags, function(i,val){
- $('<span>').addClass('tag-element').html(Q(val)).data('value', val).appendTo(taglist);
+ $('<span>').addClass('tag-element').html(Q(val)).appendTo(taglist);
+ });
+ }
+
+ // append inherited tags
+ if (itags.length) {
+ $.each(itags, function(i,val){
+ $('<span>').addClass('tag-element inherit').html(Q(val)).appendTo(taglist);
+ });
+ // re-sort tags list
+ $(taglist).children().sortElements(function(a,b){
+ return $.text([a]).toLowerCase() > $.text([b]).toLowerCase() ? 1 : -1;
});
}
commit 9fc827c2c3ca7322ee4f8d72dce6f85db4a81b2c
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 21 14:26:30 2013 +0200
Inherit tags from parent tasks for filter matching (#2373)
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 2089c70..e52174b 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1377,8 +1377,9 @@ function rcube_tasklist_ui(settings)
if (match && tagsfilter.length) {
match = rec.tags && rec.tags.length;
+ var alltags = get_inherited_tags(rec).concat(rec.tags || []);
for (var i=0; match && i < tagsfilter.length; i++) {
- if ($.inArray(tagsfilter[i], rec.tags) < 0)
+ if ($.inArray(tagsfilter[i], alltags) < 0)
match = false;
}
}
@@ -1408,6 +1409,23 @@ function rcube_tasklist_ui(settings)
/**
*
*/
+ function get_inherited_tags(rec)
+ {
+ var parent_id, itags = [];
+
+ if ((parent_id = rec.parent_id)) {
+ while (parent_id && listdata[parent_id]) {
+ itags = itags.concat(listdata[parent_id].tags || []);
+ parent_id = listdata[parent_id].parent_id;
+ }
+ }
+
+ return itags;
+ }
+
+ /**
+ *
+ */
function list_edit_dialog(id)
{
var list = me.tasklists[id],
commit af0f782563464c80c960952879cd02b340bb4df0
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Mon Oct 21 14:06:05 2013 +0200
Sort tags alphabetically (#2367)
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 6ebddc4..9d5544c 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -605,6 +605,10 @@ class tasklist extends rcube_plugin
$rec['attachments'][$k]['classname'] = rcube_utils::file2class($attachment['mimetype'], $attachment['name']);
}
+ if (!is_array($rec['tags']))
+ $rec['tags'] = (array)$rec['tags'];
+ sort($rec['tags'], SORT_LOCALE_STRING);
+
if (in_array($rec['id'], $this->collapsed_tasks))
$rec['collapsed'] = true;
commit e7372b3b82894d9b3cc4ac0a9883a5ded0ea3004
Author: Thomas Bruederli <bruederli at kolabsys.com>
Date: Thu Oct 17 18:28:56 2013 +0200
Also save tag from input box which hasn't been added to the list with <Enter>
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index d76c9d1..2089c70 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -1102,10 +1102,16 @@ function rcube_tasklist_ui(settings)
}
}
+ // collect tags
$('input[type="hidden"]', rcmail.gui_objects.edittagline).each(function(i,elem){
if (elem.value)
me.selected_task.tags.push(elem.value);
});
+ // including the "pending" one in the text box
+ var newtag = $('#tagedit-input').val();
+ if (newtag != '') {
+ me.selected_task.tags.push(newtag);
+ }
// serialize alarm settings
var alarm = $('#taskedit select.edit-alarm-type').val();
More information about the commits
mailing list