2 commits - plugins/libkolab plugins/tasklist
Thomas Brüderli
bruederli at kolabsys.com
Wed Sep 26 12:14:11 CEST 2012
plugins/libkolab/lib/kolab_format_task.php | 5
plugins/tasklist/drivers/database/tasklist_database_driver.php | 48 +++++
plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php | 61 +++++-
plugins/tasklist/drivers/tasklist_driver.php | 9
plugins/tasklist/localization/de_CH.inc | 6
plugins/tasklist/localization/en_US.inc | 5
plugins/tasklist/tasklist.js | 95 ++++++++--
plugins/tasklist/tasklist.php | 59 +++++-
8 files changed, 256 insertions(+), 32 deletions(-)
New commits:
commit 62a6e00458e3b2debe0115ca946f53dd24330328
Author: Thomas Bruederli <thomas at roundcube.net>
Date: Wed Sep 26 12:14:42 2012 +0200
Fully implement deletion of tasks: either delete all subtasks or re-assign childs to new parent; refactored moving to another list to move all childs, too
diff --git a/plugins/tasklist/drivers/database/tasklist_database_driver.php b/plugins/tasklist/drivers/database/tasklist_database_driver.php
index bcc994c..8e56923 100644
--- a/plugins/tasklist/drivers/database/tasklist_database_driver.php
+++ b/plugins/tasklist/drivers/database/tasklist_database_driver.php
@@ -358,6 +358,54 @@ class tasklist_database_driver extends tasklist_driver
}
/**
+ * Get all decendents of the given task record
+ *
+ * @param mixed Hash array with task properties or task UID
+ * @param boolean True if all childrens children should be fetched
+ * @return array List of all child task IDs
+ */
+ public function get_childs($prop, $recursive = false)
+ {
+ // resolve UID first
+ if (is_string($prop)) {
+ $result = $this->rc->db->query(sprintf(
+ "SELECT task_id AS id, tasklist_id AS list FROM " . $this->db_tasks . "
+ WHERE tasklist_id IN (%s)
+ AND uid=?",
+ $this->list_ids
+ ),
+ $prop);
+ $prop = $this->rc->db->fetch_assoc($result);
+ }
+
+ $childs = array();
+ $task_ids = array($prop['id']);
+
+ // query for childs (recursively)
+ while (!empty($task_ids)) {
+ $result = $this->rc->db->query(sprintf(
+ "SELECT task_id AS id FROM " . $this->db_tasks . "
+ WHERE tasklist_id IN (%s)
+ AND parent_id IN (%s)
+ AND del=0",
+ $this->list_ids,
+ join(',', array_map(array($this->rc->db, 'quote'), $task_ids))
+ ));
+
+ $task_ids = array();
+ while ($result && ($rec = $this->rc->db->fetch_assoc($result))) {
+ $childs[] = $rec['id'];
+ $task_ids[] = $rec['id'];
+ }
+
+ if (!$recursive)
+ break;
+ }
+
+ return $childs;
+ }
+
+ /**
* Get a list of pending alarms to be displayed to the user
*
* @param integer Current time (unix timestamp)
diff --git a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
index 60f9ccb..b70a4a0 100644
--- a/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
+++ b/plugins/tasklist/drivers/kolab/tasklist_kolab_driver.php
@@ -351,14 +351,17 @@ class tasklist_kolab_driver extends tasklist_driver
*/
public function get_task($prop)
{
- $id = is_array($prop) ? $prop['uid'] : $prop;
+ $id = is_array($prop) ? ($prop['uid'] ?: $prop['id']) : $prop;
$list_id = is_array($prop) ? $prop['list'] : null;
$folders = $list_id ? array($list_id => $this->folders[$list_id]) : $this->folders;
// find task in the available folders
- foreach ($folders as $folder) {
+ foreach ($folders as $list_id => $folder) {
+ if (is_numeric($list_id))
+ continue;
if (!$this->tasks[$id] && ($object = $folder->get_object($id))) {
$this->tasks[$id] = $this->_to_rcube_task($object);
+ $this->tasks[$id]['list'] = $list_id;
break;
}
}
@@ -367,6 +370,48 @@ class tasklist_kolab_driver extends tasklist_driver
}
/**
+ * Get all decendents of the given task record
+ *
+ * @param mixed Hash array with task properties or task UID
+ * @param boolean True if all childrens children should be fetched
+ * @return array List of all child task IDs
+ */
+ public function get_childs($prop, $recursive = false)
+ {
+ if (is_string($prop)) {
+ $task = $this->get_task($prop);
+ $prop = array('id' => $task['id'], 'list' => $task['list']);
+ }
+
+ $childs = array();
+ $list_id = $prop['list'];
+ $task_ids = array($prop['id']);
+ $folder = $this->folders[$list_id];
+
+ // query for childs (recursively)
+ while ($folder && !empty($task_ids)) {
+ $query_ids = array();
+ foreach ($task_ids as $task_id) {
+ $query = array(array('tags','=','x-parent:' . $task_id));
+ foreach ((array)$folder->select($query) as $record) {
+ // don't rely on kolab_storage_folder filtering
+ if ($record['parent_id'] == $task_id) {
+ $childs[] = $record['uid'];
+ $query_ids[] = $record['uid'];
+ }
+ }
+ }
+
+ if (!$recursive)
+ break;
+
+ $task_ids = $query_ids;
+ }
+
+ return $childs;
+ }
+
+ /**
* Get a list of pending alarms to be displayed to the user
*
* @param integer Current time (unix timestamp)
@@ -641,7 +686,7 @@ class tasklist_kolab_driver extends tasklist_driver
// moved from another folder
if ($task['_fromlist'] && ($fromfolder = $this->folders[$task['_fromlist']])) {
- if (!$fromfolder->move($task['uid'], $folder->name))
+ if (!$fromfolder->move($task['id'], $folder->name))
return false;
unset($task['_fromlist']);
@@ -649,9 +694,13 @@ class tasklist_kolab_driver extends tasklist_driver
// load previous version of this task to merge
if ($task['id']) {
- $old = $folder->get_object($task['uid']);
+ $old = $folder->get_object($task['id']);
if (!$old || PEAR::isError($old))
return false;
+
+ // merge existing properties if the update isn't complete
+ if (!isset($task['title']) || !isset($task['complete']))
+ $task += $this->_to_rcube_task($old);
}
// generate new task object from RC input
@@ -669,7 +718,7 @@ class tasklist_kolab_driver extends tasklist_driver
else {
$task = $this->_to_rcube_task($object);
$task['list'] = $list_id;
- $this->tasks[$task['uid']] = $task;
+ $this->tasks[$task['id']] = $task;
}
return $saved;
@@ -710,7 +759,7 @@ class tasklist_kolab_driver extends tasklist_driver
if (!$list_id || !($folder = $this->folders[$list_id]))
return false;
- return $folder->delete($task['uid']);
+ return $folder->delete($task['id']);
}
/**
diff --git a/plugins/tasklist/drivers/tasklist_driver.php b/plugins/tasklist/drivers/tasklist_driver.php
index 4e987f6..f810799 100644
--- a/plugins/tasklist/drivers/tasklist_driver.php
+++ b/plugins/tasklist/drivers/tasklist_driver.php
@@ -160,6 +160,15 @@ abstract class tasklist_driver
abstract public function get_task($prop);
/**
+ * Get decendents of the given task record
+ *
+ * @param mixed Hash array with task properties or task UID
+ * @param boolean True if all childrens children should be fetched
+ * @return array List of all child task IDs
+ */
+ abstract public function get_childs($prop, $recursive = false);
+
+ /**
* Add a single task to the database
*
* @param array Hash array with task properties (see header of this file)
diff --git a/plugins/tasklist/localization/de_CH.inc b/plugins/tasklist/localization/de_CH.inc
index ac06095..4fbb976 100644
--- a/plugins/tasklist/localization/de_CH.inc
+++ b/plugins/tasklist/localization/de_CH.inc
@@ -36,6 +36,10 @@ $labels['edittask'] = 'Aufgabe bearbeiten';
$labels['save'] = 'Speichern';
$labels['cancel'] = 'Abbrechen';
$labels['addsubtask'] = 'Neue Teilaufgabe';
+$labels['deletetask'] = 'Aufgabe löschen';
+$labels['deletethisonly'] = 'Nur diese Aufgabe löschen';
+$labels['deletewithchilds'] = 'Mit allen Teilaufhaben löschen';
+
$labels['tabsummary'] = 'Ãbersicht';
$labels['tabrecurrence'] = 'Wiederholung';
@@ -60,4 +64,6 @@ $labels['savingdata'] = 'Daten werden gespeichert...';
$labels['errorsaving'] = 'Fehler beim Speichern.';
$labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.';
$labels['invalidstartduedates'] = 'Beginn der Aufgabe darf nicht grösser als das Enddatum sein.';
+$labels['deletetasktconfirm'] = 'Möchten Sie diese Aufgabe wirklich löschen?';
+$labels['deleteparenttasktconfirm'] = 'Möchten Sie diese Aufgabe inklusive aller Teilaufgaben wirklich löschen?';
$labels['deletelistconfirm'] = 'Möchten Sie diese Liste mit allen Aufgaben wirklich löschen?';
diff --git a/plugins/tasklist/localization/en_US.inc b/plugins/tasklist/localization/en_US.inc
index c657516..7d5415a 100644
--- a/plugins/tasklist/localization/en_US.inc
+++ b/plugins/tasklist/localization/en_US.inc
@@ -36,6 +36,9 @@ $labels['edittask'] = 'Edit Task';
$labels['save'] = 'Save';
$labels['cancel'] = 'Cancel';
$labels['addsubtask'] = 'Add subtask';
+$labels['deletetask'] = 'Delete task';
+$labels['deletethisonly'] = 'Delete this task only';
+$labels['deletewithchilds'] = 'Delete with all subtasks';
$labels['tabsummary'] = 'Summary';
$labels['tabrecurrence'] = 'Recurrence';
@@ -60,4 +63,6 @@ $labels['savingdata'] = 'Saving data...';
$labels['errorsaving'] = 'Failed to save data.';
$labels['notasksfound'] = 'No tasks found for the given criteria';
$labels['invalidstartduedates'] = 'Start date must not be greater than due date.';
+$labels['deletetasktconfirm'] = 'Do you really want to delete this task?';
+$labels['deleteparenttasktconfirm'] = 'Do you really want to delete this task and all its subtasks?';
$labels['deletelistconfirm'] = 'Do you really want to delete this list with all its tasks?';
diff --git a/plugins/tasklist/tasklist.js b/plugins/tasklist/tasklist.js
index 2e885e6..56f29cd 100644
--- a/plugins/tasklist/tasklist.js
+++ b/plugins/tasklist/tasklist.js
@@ -815,11 +815,7 @@ function rcube_tasklist_ui(settings)
// dropped on another list -> move
if ($(this).data('type') == 'tasklist') {
if (rec) {
- var ids = [ rec.id ],
- childs = get_all_childs(rec.id);
- if (childs.length)
- ids = ids.concat(childs);
- save_task({ id:ids, list:drop_id, _fromlist:rec.list }, 'move');
+ save_task({ id:rec.id, list:drop_id, _fromlist:rec.list }, 'move');
rec.list = drop_id;
}
}
@@ -1057,11 +1053,6 @@ function rcube_tasklist_ui(settings)
// task assigned to a new list
if (me.selected_task.list && me.selected_task.list != rec.list) {
me.selected_task._fromlist = rec.list;
-
- // also move all childs
- var childs = get_all_childs(me.selected_task.id);
- if (childs.length)
- save_task({ id:childs, list:me.selected_task.list, _fromlist:rec.list }, 'move');
}
me.selected_task.complete = complete.val() / 100;
@@ -1209,14 +1200,84 @@ function rcube_tasklist_ui(settings)
function delete_task(id)
{
var rec = listdata[id];
- if (rec && confirm("Delete this?")) {
- saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
- rcmail.http_post('task', { action:'delete', t:rec, filter:filtermask });
- $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).hide();
- return true;
+ if (!rec || rec.readonly)
+ return false;
+
+ var html, buttons = [{
+ text: rcmail.gettext('cancel', 'tasklist'),
+ click: function() {
+ $(this).dialog('close');
+ }
+ }];
+
+ if (rec.children && rec.children.length) {
+ html = rcmail.gettext('deleteparenttasktconfirm','tasklist');
+ buttons.push({
+ text: rcmail.gettext('deletethisonly','tasklist'),
+ click: function() {
+ _delete_task(id, 0);
+ $(this).dialog('close');
+ }
+ });
+ buttons.push({
+ text: rcmail.gettext('deletewithchilds','tasklist'),
+ click: function() {
+ _delete_task(id, 1);
+ $(this).dialog('close');
+ }
+ });
}
-
- return false;
+ else {
+ html = rcmail.gettext('deletetasktconfirm','tasklist');
+ buttons.push({
+ text: rcmail.gettext('delete','tasklist'),
+ click: function() {
+ _delete_task(id, 0);
+ $(this).dialog('close');
+ }
+ });
+ }
+
+ var $dialog = $('<div>').html(html);
+ $dialog.dialog({
+ modal: true,
+ width: 520,
+ dialogClass: 'warning',
+ title: rcmail.gettext('deletetask', 'tasklist'),
+ buttons: buttons,
+ close: function(){
+ $dialog.dialog('destroy').hide();
+ }
+ }).addClass('tasklist-confirm').show();
+
+ return true;
+ }
+
+ /**
+ * Subfunction to submit the delete command after confirm
+ */
+ function _delete_task(id, mode)
+ {
+ var rec = listdata[id],
+ li = $('li[rel="'+id+'"]', rcmail.gui_objects.resultlist).hide();
+
+ saving_lock = rcmail.set_busy(true, 'tasklist.savingdata');
+ rcmail.http_post('task', { action:'delete', t:{ id:rec.id, list:rec.list }, mode:mode, filter:filtermask });
+
+ // move childs to parent/root
+ if (mode != 1) {
+ var parent_node = rec.parent_id ? $('li[rel="'+rec.parent_id+'"] > .childtasks', rcmail.gui_objects.resultlist) : null;
+ if (!parent_node || !parent_node.length)
+ parent_node = rcmail.gui_objects.resultlist;
+
+ $.each(rec.children, function(i,cid) {
+ var child = listdata[cid];
+ child.parent_id = rec.parent_id;
+ resort_task(child, $('li[rel="'+cid+'"]').appendTo(parent_node), true);
+ });
+ }
+
+ li.remove();
}
/**
diff --git a/plugins/tasklist/tasklist.php b/plugins/tasklist/tasklist.php
index 1c12c6b..b28b2e3 100644
--- a/plugins/tasklist/tasklist.php
+++ b/plugins/tasklist/tasklist.php
@@ -157,6 +157,7 @@ class tasklist extends rcube_plugin
*/
public function task_action()
{
+ $filter = intval(get_input_value('filter', RCUBE_INPUT_GPC));
$action = get_input_value('action', RCUBE_INPUT_GPC);
$rec = get_input_value('t', RCUBE_INPUT_POST, true);
$oldrec = $rec;
@@ -184,27 +185,64 @@ class tasklist extends rcube_plugin
break;
case 'move':
- $recs = array();
foreach ((array)$rec['id'] as $id) {
$r = $rec;
$r['id'] = $id;
if ($this->driver->move_task($r)) {
- $r = $this->driver->get_task($r);
- $this->encode_task($r);
- $refresh[] = $r;
+ $refresh[] = $this->driver->get_task($r);
$success = true;
+
+ // move all childs, too
+ foreach ($this->driver->get_childs(array('id' => $rec['id'], 'list' => $rec['_fromlist'])) as $cid) {
+ $child = $rec;
+ $child['id'] = $cid;
+ if ($this->driver->move_task($child)) {
+ $r = $this->driver->get_task($child);
+ if ((bool)($filter & self::FILTER_MASK_COMPLETE) == ($r['complete'] == 1.0)) {
+ $refresh[] = $r;
+ }
+ }
+ }
}
}
break;
case 'delete':
- if (!($success = $this->driver->delete_task($rec, false)))
+ $mode = intval(get_input_value('mode', RCUBE_INPUT_POST));
+ $oldrec = $this->driver->get_task($rec);
+ if ($success = $this->driver->delete_task($rec, false)) {
+ // delete/modify all childs
+ foreach ($this->driver->get_childs($rec, $mode) as $cid) {
+ $child = array('id' => $cid, 'list' => $rec['list']);
+
+ if ($mode == 1) { // delete all childs
+ if ($this->driver->delete_task($child, false)) {
+ if ($this->driver->undelete)
+ $_SESSION['tasklist_undelete'][$rec['id']][] = $cid;
+ }
+ else
+ $success = false;
+ }
+ else {
+ $child['parent_id'] = strval($oldrec['parent_id']);
+ $this->driver->edit_task($child);
+ }
+ }
+ }
+
+ if (!$success)
$this->rc->output->command('plugin.reload_data');
break;
case 'undelete':
- if ($success = $this->driver->undelete_task($rec))
- $refresh = $this->driver->get_task($rec);
+ if ($success = $this->driver->undelete_task($rec)) {
+ $refresh[] = $this->driver->get_task($rec);
+ foreach ((array)$_SESSION['tasklist_undelete'][$rec['id']] as $cid) {
+ if ($this->driver->undelete_task($rec)) {
+ $refresh[] = $this->driver->get_task($rec);
+ }
+ }
+ }
break;
case 'collapse':
@@ -232,8 +270,13 @@ class tasklist extends rcube_plugin
$this->rc->output->command('plugin.unlock_saving');
if ($refresh) {
- if ($refresh['id'])
+ if ($refresh['id']) {
$this->encode_task($refresh);
+ }
+ else if (is_array($refresh)) {
+ foreach ($refresh as $i => $r)
+ $this->encode_task($refresh[$i]);
+ }
$this->rc->output->command('plugin.refresh_task', $refresh);
}
}
commit 476f79156845bea6e79dff0d341c15cce3944010
Author: Thomas Bruederli <thomas at roundcube.net>
Date: Wed Sep 26 12:06:07 2012 +0200
Store parent-relationships in cache for direct queries
diff --git a/plugins/libkolab/lib/kolab_format_task.php b/plugins/libkolab/lib/kolab_format_task.php
index b041b08..2a7a629 100644
--- a/plugins/libkolab/lib/kolab_format_task.php
+++ b/plugins/libkolab/lib/kolab_format_task.php
@@ -93,7 +93,7 @@ class kolab_format_task extends kolab_format_xcal
if ($due = $this->obj->due())
$object['due'] = self::php_datetime($due);
- // related-to points to parent taks; we only support one relation
+ // related-to points to parent task; we only support one relation
$related = self::vector2array($this->obj->relatedTo());
if (count($related))
$object['parent_id'] = $related[0];
@@ -137,6 +137,9 @@ class kolab_format_task extends kolab_format_xcal
if (!empty($this->data['alarms']))
$tags[] = 'x-has-alarms';
+ if ($this->data['parent_id'])
+ $tags[] = 'x-parent:' . $this->data['parent_id'];
+
return $tags;
}
}
More information about the commits
mailing list