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

This commit is contained in:
Thomas Bruederli 2012-09-26 12:14:42 +02:00
parent 476f791568
commit 62a6e00458
7 changed files with 252 additions and 31 deletions

View file

@ -357,6 +357,54 @@ class tasklist_database_driver extends tasklist_driver
return false;
}
/**
* 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
*

View file

@ -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;
}
}
@ -366,6 +369,48 @@ class tasklist_kolab_driver extends tasklist_driver
return $this->tasks[$id];
}
/**
* 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
*
@ -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']);
}
/**

View file

@ -159,6 +159,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
*

View file

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

View file

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

View file

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

View file

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