From: Jan Schneider Date: Tue, 17 Nov 2009 16:35:33 +0000 (+0100) Subject: Implement task editing. X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=d751dc08666c7109123e1c7a4d289f855ce4b59d;p=horde.git Implement task editing. --- diff --git a/kronolith/ajax.php b/kronolith/ajax.php index 9288db902..953096cdb 100644 --- a/kronolith/ajax.php +++ b/kronolith/ajax.php @@ -370,6 +370,65 @@ try { $result->task = $task->toJson(true, $prefs->getValue('twentyFour') ? 'H:i' : 'h:i A'); break; + case 'SaveTask': + if (!$registry->hasMethod('tasks/updateTask')) { + break; + } + if (is_null($id = Horde_Util::getFormData('task_id')) || + is_null($list = Horde_Util::getFormData('old_tasklist'))) { + break; + } + $task = Horde_Util::getFormData('task'); + + $due = trim($task['due_date'] . ' ' . $task['due_time']); + if (!empty($due)) { + // strptime() is locale dependent, i.e. %p is not always matching + // AM/PM. Set the locale to C to workaround this, but grab the + // locale's D_FMT before that. + $date_format = Horde_Nls::getLangInfo(D_FMT); + $old_locale = setlocale(LC_TIME, 0); + setlocale(LC_TIME, 'C'); + $format = $date_format . ' ' + . ($prefs->getValue('twentyFour') ? '%H:%M' : '%I:%M %p'); + + // Try exact format match first. + if ($date_arr = strptime($due, $format)) { + $task['due'] = new Horde_Date( + array('year' => $date_arr['tm_year'] + 1900, + 'month' => $date_arr['tm_mon'] + 1, + 'mday' => $date_arr['tm_mday'], + 'hour' => $date_arr['tm_hour'], + 'min' => $date_arr['tm_min'], + 'sec' => $date_arr['tm_sec'])); + } else { + $task['due'] = new Horde_Date($due); + } + setlocale(LC_TIME, $old_locale); + } + + if ($task['alarm']['on']) { + $task['alarm'] = $task['alarm']['value'] * $task['alarm']['unit']; + } else { + $task['alarm'] = 0; + } + + $result = $registry->tasks->updateTask($list, $id, $task); + if (is_a($result, 'PEAR_Error')) { + $notification->push($result, 'horde.error'); + break; + } + $task = $registry->tasks->getTask($task['tasklist'], $id); + if (is_a($task, 'PEAR_Error')) { + $notification->push($task, 'horde.error'); + break; + } + $result = new stdClass; + $result->type = $task->completed ? 'complete' : 'incomplete'; + $result->list = $task->tasklist; + $result->sig = Horde_Util::getFormData('sig'); + $result->tasks = array($id => $task->toJson(false, $prefs->getValue('twentyFour') ? 'H:i' : 'h:i A')); + break; + case 'DeleteTask': if (!$registry->hasMethod('tasks/deleteTask')) { break; diff --git a/kronolith/js/kronolith.js b/kronolith/js/kronolith.js index bbb7c5ead..f8c601e46 100644 --- a/kronolith/js/kronolith.js +++ b/kronolith/js/kronolith.js @@ -1516,13 +1516,13 @@ KronolithCore = { Object.isUndefined(this.tcache.get(type).get(list))) { loading = true; this.startLoading('tasks:' + type + list, tasktype); - this._storeTasksCache($H(), type, list); + this._storeTasksCache($H(), type, list, true); this.doAction('ListTasks', { 'type': type, 'sig' : tasktype, 'list': list }, function(r) { - this._loadTasksCallback(r, tasktype); + this._loadTasksCallback(r, tasktype, true); }.bind(this)); } }, this); @@ -1538,9 +1538,15 @@ KronolithCore = { /** * Callback method for inserting tasks in the current view. * - * @param object r The ajax response object. + * @param object r The ajax response object. + * @param string tasktype The (UI) task type for that the response was + * targeted. + * @param boolean createCache Whether to create a cache list entry for the + * response, if none exists yet. Useful for + * adding individual tasks to the cache without + * assuming to have all tasks of the list. */ - _loadTasksCallback: function(r, tasktype) + _loadTasksCallback: function(r, tasktype, createCache) { // Hide spinner. this.loading--; @@ -1548,7 +1554,7 @@ KronolithCore = { $('kronolithLoading').hide(); } - this._storeTasksCache(r.response.tasks || {}, r.response.type, r.response.list); + this._storeTasksCache(r.response.tasks || {}, r.response.type, r.response.list, createCache); // Check if this is the still the result of the most current request. if (this.view != 'tasks' || @@ -1671,12 +1677,6 @@ KronolithCore = { } }); - // TODO: Assuming that tasks of the same tasklist are already in - // order - if (rowTasklist == newTask.value.l) { - continue; - } - if (Object.isUndefined(rowTask)) { // TODO: Throw error return; @@ -1783,7 +1783,7 @@ KronolithCore = { } else { $('kronolithTaskId').clear(); $('kronolithTaskOldList').clear(); - $('kronolithTaskList').setValue(Kronolith.conf.default_tasklist); + //$('kronolithTaskList').setValue(Kronolith.conf.default_tasklist); $('kronolithTaskDelete').hide(); $('kronolithTaskDueDate').setValue(d.toString(Kronolith.conf.date_format)); $('kronolithTaskDueTime').setValue(d.toString(Kronolith.conf.time_format)); @@ -1847,7 +1847,7 @@ KronolithCore = { $('kronolithTaskList').update(); $H(Kronolith.conf.calendars.tasklists).each(function(cal) { if (cal.value.edit) { - $('kronolithTaskList').insert(new Element('OPTION', { 'value': 'tasks|' + cal.key }) + $('kronolithTaskList').insert(new Element('OPTION', { 'value': cal.key.substring(6) }) .setStyle({ 'backgroundColor': cal.value.bg, 'color': cal.value.fg }) .update(cal.value.name.escapeHTML())); } @@ -1870,6 +1870,27 @@ KronolithCore = { }, /** + * Submits the task edit form to create or update a task. + */ + saveTask: function() + { + var tasklist = $F('kronolithTaskList'), + taskid = $F('kronolithTaskId'); + this.startLoading('tasks:' + ($F('kronolithTaskCompleted') ? 'complete' : 'incomplete') + tasklist, this.tasktype); + this.doAction('SaveTask', + $H($('kronolithTaskForm').serialize({ 'hash': true })) + .merge({ 'sig': this.tasktype }), + function(r) { + if (r.response.tasks && taskid) { + this._removeTask(taskid, tasklist); + this._loadTasksCallback(r, this.tasktype, false); + } + this._closeRedBox(); + window.history.back(); + }.bind(this)); + }, + + /** * Parses a date attribute string into a Date object. * * For other strings use Date.parse(). @@ -1980,13 +2001,15 @@ KronolithCore = { /** * Stores a set of tasks in the cache. * - * @param Hash tasks The tasks to be stored. - * @param string tasktypes The task type that's being stored. - * @param string tasklist The task list to which the tasks belong. + * @param Hash tasks The tasks to be stored. + * @param string tasktypes The task type that's being stored. + * @param string tasklist The task list to which the tasks belong. + * @param boolean createCache Whether to create a cache list entry for the + * response, if none exists yet. */ - _storeTasksCache: function(tasks, tasktypes, tasklist) + _storeTasksCache: function(tasks, tasktypes, tasklist, createCache) { - var taskHashes = {}; + var taskHashes = {}, cacheExists = {}; if (tasktypes == 'all' || tasktypes == 'future') { tasktypes = [ 'complete', 'incomplete' ]; @@ -1995,23 +2018,39 @@ KronolithCore = { } tasktypes.each(function(tasktype) { + cacheExists[tasktype] = false; if (!this.tcache.get(tasktype)) { - this.tcache.set(tasktype, $H()); + if (createCache) { + this.tcache.set(tasktype, $H()); + } else { + return; + } } if (!this.tcache.get(tasktype).get(tasklist)) { - this.tcache.get(tasktype).set(tasklist, $H()); + if (createCache) { + this.tcache.get(tasktype).set(tasklist, $H()); + cacheExists[tasktype] = true; + } else { + return; + } + } else { + cacheExists[tasktype] = true; } taskHashes[tasktype] = this.tcache.get(tasktype).get(tasklist); }, this); $H(tasks).each(function(task) { + var tasktype = task.value.cp ? 'complete' : 'incomplete'; + if (!cacheExists[tasktype]) { + return; + } if (!Object.isUndefined(task.value.s)) { task.value.start = Date.parse(task.value.s); } if (!Object.isUndefined(task.value.du)) { task.value.due = Date.parse(task.value.du); } - taskHashes[task.value.cp ? 'complete' : 'incomplete'].set(task.key, task.value); + taskHashes[tasktype].set(task.key, task.value); }); }, @@ -2134,6 +2173,11 @@ KronolithCore = { e.stop(); break; + case 'kronolithTaskForm': + this.saveTask(); + e.stop(); + break; + case 'kronolithSearchForm': this.go('search:' + $F('kronolithSearchContext') + ':' + $F('kronolithSearchTerm')) e.stop(); @@ -2259,6 +2303,11 @@ KronolithCore = { e.stop(); return; + case 'kronolithTaskSave': + this.saveTask(); + e.stop(); + return; + case 'kronolithEventDelete': var cal = $F('kronolithEventCalendar'), eventid = $F('kronolithEventId'); @@ -2635,6 +2684,9 @@ KronolithCore = { } }, + /** + * Submits the event edit form to create or update an event. + */ saveEvent: function() { var cal = $F('kronolithEventCalendar'), diff --git a/kronolith/templates/index/task.inc b/kronolith/templates/index/task.inc index 1f9561093..ecaf9dc3c 100644 --- a/kronolith/templates/index/task.inc +++ b/kronolith/templates/index/task.inc @@ -5,18 +5,18 @@

- +
- +

- '1 ' . _("(highest)"), 2 => 2, 3 => 3, 4 => 4, 5 => '5 ' . _("(lowest)")) as $prio => $label): ?> @@ -26,16 +26,16 @@

-

- + - +
@@ -50,16 +50,16 @@
- +
diff --git a/nag/lib/Api.php b/nag/lib/Api.php index 48aad7182..cdbfa5323 100644 --- a/nag/lib/Api.php +++ b/nag/lib/Api.php @@ -1148,6 +1148,48 @@ class Nag_Api extends Horde_Registry_Api } /** + * Changes a task identified by tasklist and ID. + * + * @param string $tasklist A tasklist id. + * @param string $id A task id. + * @param array $task A hash with overwriting task information. + */ + public function updateTask($tasklist, $id, $task) + { + require_once dirname(__FILE__) . '/base.php'; + + if (!Horde_Auth::isAdmin() && + !array_key_exists($tasklist, + Nag::listTasklists(false, PERMS_EDIT))) { + return PEAR::raiseError(_("Permission Denied")); + } + + $storage = Nag_Driver::singleton($tasklist); + $existing = $storage->get($id); + if (is_a($existing, 'PEAR_Error')) { + return $existing; + } + + return $storage->modify( + $id, + isset($task['name']) ? $task['name'] : $existing->name, + isset($task['desc']) ? $task['desc'] : $existing->desc, + isset($task['start']) ? $task['start'] : $existing->start, + isset($task['due']) ? $task['due'] : $existing->due, + isset($task['priority']) ? $task['priority'] : $existing->priority, + isset($task['estimate']) ? $task['estimate'] : $existing->estimate, + isset($task['completed']) ? (int)$task['completed'] : $existing->completed, + isset($task['category']) ? $task['category'] : $existing->category, + isset($task['alarm']) ? $task['alarm'] : $existing->alarm, + isset($task['parent_id']) ? $task['parent_id'] : $existing->parent_id, + isset($task['private']) ? $task['private'] : $existing->private, + isset($task['owner']) ? $task['owner'] : $existing->owner, + isset($task['assignee']) ? $task['assignee'] : $existing->assignee, + $task['completed'] && !$existing->completed ? date() : $existing->completed_date, + isset($task['tasklist']) ? $task['tasklist'] : $existing->tasklist); + } + + /** * Lists active tasks as cost objects. * * @todo Implement $criteria parameter.