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:
parent
476f791568
commit
62a6e00458
7 changed files with 252 additions and 31 deletions
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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?';
|
||||
|
|
|
@ -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?';
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue