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