Handle start date/time; fix task list sorting

This commit is contained in:
Thomas Bruederli 2012-07-29 13:36:16 +02:00
parent e6fff6a96a
commit 78077bcb0a
9 changed files with 134 additions and 32 deletions

View file

@ -32,6 +32,8 @@ CREATE TABLE `tasks` (
`tags` text, `tags` text,
`date` varchar(10) DEFAULT NULL, `date` varchar(10) DEFAULT NULL,
`time` varchar(5) DEFAULT NULL, `time` varchar(5) DEFAULT NULL,
`startdate` varchar(10) DEFAULT NULL,
`starttime` varchar(5) DEFAULT NULL,
`flagged` tinyint(4) NOT NULL DEFAULT '0', `flagged` tinyint(4) NOT NULL DEFAULT '0',
`complete` float NOT NULL DEFAULT '0', `complete` float NOT NULL DEFAULT '0',
`alarms` varchar(255) NOT NULL, `alarms` varchar(255) NOT NULL,

View file

@ -343,7 +343,7 @@ class tasklist_database_driver extends tasklist_driver
{ {
$rec['id'] = $rec['task_id']; $rec['id'] = $rec['task_id'];
$rec['list'] = $rec['tasklist_id']; $rec['list'] = $rec['tasklist_id'];
$rec['changed'] = strtotime($rec['changed']); $rec['changed'] = new DateTime($rec['changed']);
$rec['tags'] = array_filter(explode(',', $rec['tags'])); $rec['tags'] = array_filter(explode(',', $rec['tags']));
if (!$rec['parent_id']) if (!$rec['parent_id'])
@ -409,7 +409,7 @@ class tasklist_database_driver extends tasklist_driver
if (isset($prop[$col])) if (isset($prop[$col]))
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($prop[$col]); $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($prop[$col]);
} }
foreach (array('parent_id', 'date', 'time') as $col) { foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime') as $col) {
if (isset($prop[$col])) if (isset($prop[$col]))
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col])); $sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col]));
} }

View file

@ -337,8 +337,13 @@ class tasklist_kolab_driver extends tasklist_driver
$task['date'] = $record['due']->format('Y-m-d'); $task['date'] = $record['due']->format('Y-m-d');
$task['time'] = $record['due']->format('h:i'); $task['time'] = $record['due']->format('h:i');
} }
// convert from DateTime to internal date format
if (is_a($record['start'], 'DateTime')) {
$task['startdate'] = $record['start']->format('Y-m-d');
$task['starttime'] = $record['start']->format('h:i');
}
if (is_a($record['dtstamp'], 'DateTime')) { if (is_a($record['dtstamp'], 'DateTime')) {
$task['changed'] = $record['dtstamp']->format('U'); $task['changed'] = $record['dtstamp'];
} }
return $task; return $task;
@ -360,6 +365,13 @@ class tasklist_kolab_driver extends tasklist_driver
unset($object['date']); unset($object['date']);
} }
if (!empty($task['startdate'])) {
$object['start'] = new DateTime($task['startdate'].' '.$task['starttime'], $this->plugin->timezone);
if (empty($task['starttime']))
$object['start']->_dateonly = true;
unset($object['startdate']);
}
$object['complete'] = $task['complete'] * 100; $object['complete'] = $task['complete'] * 100;
if ($task['complete'] == 1.0) if ($task['complete'] == 1.0)
$object['status'] = 'COMPLETED'; $object['status'] = 'COMPLETED';

View file

@ -30,12 +30,14 @@
* 'parent_id' => 'ID of parent task', // null if top-level task * 'parent_id' => 'ID of parent task', // null if top-level task
* 'uid' => 'Unique identifier of this task', * 'uid' => 'Unique identifier of this task',
* 'list' => 'Task list identifier to add the task to or where the task is stored', * 'list' => 'Task list identifier to add the task to or where the task is stored',
* 'changed' => <unixtime>, // Last modification date of record * 'changed' => <DateTime>, // Last modification date/time of the record
* 'title' => 'Event title/summary', * 'title' => 'Event title/summary',
* 'description' => 'Event description', * 'description' => 'Event description',
* 'tags' => array(), // List of tags for this task * 'tags' => array(), // List of tags for this task
* 'date' => 'Due date', // as string of format YYYY-MM-DD or null if no date is set * 'date' => 'Due date', // as string of format YYYY-MM-DD or null if no date is set
* 'time' => 'Due time', // as string of format hh::ii or null if no due time is set * 'time' => 'Due time', // as string of format hh::ii or null if no due time is set
* 'startdate' => 'Start date' // Delay start of the task until that date
* 'starttime' => 'Start time' // ...and time
* 'categories' => 'Task category', * 'categories' => 'Task category',
* 'flagged' => 'Boolean value whether this record is flagged', * 'flagged' => 'Boolean value whether this record is flagged',
* 'complete' => 'Float value representing the completeness state (range 0..1)', * 'complete' => 'Float value representing the completeness state (range 0..1)',

View file

@ -2,8 +2,8 @@
$labels = array(); $labels = array();
$labels['navtitle'] = 'Aufgaben'; $labels['navtitle'] = 'Aufgaben';
$labels['lists'] = 'Ressourcen'; $labels['lists'] = 'Aufgabenlisten';
$labels['list'] = 'Ressource'; $labels['list'] = 'Liste';
$labels['tags'] = 'Tags'; $labels['tags'] = 'Tags';
$labels['newtask'] = 'Neue Aufgabe'; $labels['newtask'] = 'Neue Aufgabe';
@ -15,6 +15,7 @@ $labels['delete'] = 'Löschen';
$labels['title'] = 'Titel'; $labels['title'] = 'Titel';
$labels['description'] = 'Beschreibung'; $labels['description'] = 'Beschreibung';
$labels['datetime'] = 'Datum/Zeit'; $labels['datetime'] = 'Datum/Zeit';
$labels['start'] = 'Beginn';
$labels['all'] = 'Alle'; $labels['all'] = 'Alle';
$labels['flagged'] = 'Markiert'; $labels['flagged'] = 'Markiert';
@ -51,3 +52,4 @@ $labels['next'] = 'nächsten';
$labels['savingdata'] = 'Daten werden gespeichert...'; $labels['savingdata'] = 'Daten werden gespeichert...';
$labels['errorsaving'] = 'Fehler beim Speichern.'; $labels['errorsaving'] = 'Fehler beim Speichern.';
$labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.'; $labels['notasksfound'] = 'Für die aktuellen Kriterien wurden keine Aufgaben gefunden.';
$labels['invalidstartduedates'] = 'Beginn der Aufgabe darf nicht grösser als das Enddatum sein.';

View file

@ -2,8 +2,8 @@
$labels = array(); $labels = array();
$labels['navtitle'] = 'Tasks'; $labels['navtitle'] = 'Tasks';
$labels['lists'] = 'Resources'; $labels['lists'] = 'Tasklists';
$labels['list'] = 'Resource'; $labels['list'] = 'Tasklist';
$labels['tags'] = 'Tags'; $labels['tags'] = 'Tags';
$labels['newtask'] = 'New Task'; $labels['newtask'] = 'New Task';
@ -15,6 +15,7 @@ $labels['delete'] = 'Delete';
$labels['title'] = 'Title'; $labels['title'] = 'Title';
$labels['description'] = 'Description'; $labels['description'] = 'Description';
$labels['datetime'] = 'Date/Time'; $labels['datetime'] = 'Date/Time';
$labels['start'] = 'Start';
$labels['all'] = 'All'; $labels['all'] = 'All';
$labels['flagged'] = 'Flagged'; $labels['flagged'] = 'Flagged';
@ -51,3 +52,4 @@ $labels['next'] = 'next';
$labels['savingdata'] = 'Saving data...'; $labels['savingdata'] = 'Saving data...';
$labels['errorsaving'] = 'Failed to save data.'; $labels['errorsaving'] = 'Failed to save data.';
$labels['notasksfound'] = 'No tasks found for the given criteria'; $labels['notasksfound'] = 'No tasks found for the given criteria';
$labels['invalidstartduedates'] = 'Start date must not be greater than due date.';

View file

@ -107,6 +107,11 @@
<span class="task-text"></span> <span class="task-text"></span>
<span id="task-time"></span> <span id="task-time"></span>
</div> </div>
<div id="task-start" class="form-section">
<label><roundcube:label name="tasklist.start" /></label>
<span class="task-text"></span>
<span id="task-starttime"></span>
</div>
<div id="task-list" class="form-section"> <div id="task-list" class="form-section">
<label><roundcube:label name="tasklist.list" /></label> <label><roundcube:label name="tasklist.list" /></label>
<span class="task-text"></span> <span class="task-text"></span>
@ -137,16 +142,22 @@
<label for="edit-date"><roundcube:label name="tasklist.datetime" /></label> <label for="edit-date"><roundcube:label name="tasklist.datetime" /></label>
<input type="text" name="date" size="10" id="edit-date" tabindex="20" /> &nbsp; <input type="text" name="date" size="10" id="edit-date" tabindex="20" /> &nbsp;
<input type="text" name="time" size="6" id="edit-time" tabindex="21" /> <input type="text" name="time" size="6" id="edit-time" tabindex="21" />
<a href="#nodate" style="margin-left:1em" id="edit-nodate"><roundcube:label name="tasklist.nodate" /></a> <a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#edit-date,#edit-time"><roundcube:label name="tasklist.nodate" /></a>
</div>
<div class="form-section">
<label for="edit-startdate"><roundcube:label name="tasklist.start" /></label>
<input type="text" name="startdate" size="10" id="edit-startdate" tabindex="23" /> &nbsp;
<input type="text" name="starttime" size="6" id="edit-starttime" tabindex="24" />
<a href="#nodate" style="margin-left:1em" class="edit-nodate" rel="#edit-startdate,#edit-starttime"><roundcube:label name="tasklist.nodate" /></a>
</div> </div>
<div class="form-section"> <div class="form-section">
<label for="edit-completeness"><roundcube:label name="tasklist.complete" /></label> <label for="edit-completeness"><roundcube:label name="tasklist.complete" /></label>
<input type="text" name="title" id="edit-completeness" size="3" tabindex="23" />&nbsp;% <input type="text" name="title" id="edit-completeness" size="3" tabindex="25" />&nbsp;%
<div id="edit-completeness-slider"></div> <div id="edit-completeness-slider"></div>
</div> </div>
<div class="form-section" id="tasklist-select"> <div class="form-section" id="tasklist-select">
<label for="edit-tasklist"><roundcube:label name="tasklist.list" /></label> <label for="edit-tasklist"><roundcube:label name="tasklist.list" /></label>
<roundcube:object name="plugin.tasklist_select" id="edit-tasklist" tabindex="24" /> <roundcube:object name="plugin.tasklist_select" id="edit-tasklist" tabindex="26" />
</div> </div>
</form> </form>
</div> </div>

View file

@ -54,6 +54,7 @@ function rcube_tasklist(settings)
var saving_lock; var saving_lock;
var ui_loading; var ui_loading;
var taskcounts = {}; var taskcounts = {};
var listindex = [];
var listdata = {}; var listdata = {};
var tags = []; var tags = [];
var draghelper; var draghelper;
@ -354,12 +355,14 @@ function rcube_tasklist(settings)
function data_ready(response) function data_ready(response)
{ {
listdata = {}; listdata = {};
listindex = [];
loadstate.lists = response.lists; loadstate.lists = response.lists;
loadstate.filter = response.filter; loadstate.filter = response.filter;
loadstate.search = response.search; loadstate.search = response.search;
for (var i=0; i < response.data.length; i++) { for (var i=0; i < response.data.length; i++) {
listdata[response.data[i].id] = response.data[i]; listdata[response.data[i].id] = response.data[i];
listindex.push(response.data[i].id);
} }
render_tasklist(); render_tasklist();
@ -373,12 +376,13 @@ function rcube_tasklist(settings)
function render_tasklist() function render_tasklist()
{ {
// clear display // clear display
var rec, var id, rec,
count = 0, count = 0,
msgbox = $('#listmessagebox').hide(), msgbox = $('#listmessagebox').hide(),
list = $(rcmail.gui_objects.resultlist).html(''); list = $(rcmail.gui_objects.resultlist).html('');
for (var id in listdata) { for (var i=0; i < listindex.length; i++) {
id = listindex[i];
rec = listdata[id]; rec = listdata[id];
if (match_filter(rec)) { if (match_filter(rec)) {
render_task(rec); render_task(rec);
@ -436,9 +440,18 @@ function rcube_tasklist(settings)
*/ */
function update_taskitem(rec) function update_taskitem(rec)
{ {
var id = rec.id; var id = rec.id,
oldid = rec.tempid || id;
oldindex = listindex.indexOf(oldid);
if (oldindex >= 0)
listindex[oldindex] = id;
else
listindex.push(id);
listdata[id] = rec; listdata[id] = rec;
render_task(rec, rec.tempid || id);
render_task(rec, oldid);
append_tags(rec.tags || []); append_tags(rec.tags || []);
} }
@ -525,7 +538,7 @@ function rcube_tasklist(settings)
*/ */
function resort_task(rec, li, animated) function resort_task(rec, li, animated)
{ {
var dir = 0, next_li, next_id, next_rec; var dir = 0, index, slice, next_li, next_id, next_rec;
// animated moving // animated moving
var insert_animated = function(li, before, after) { var insert_animated = function(li, before, after) {
@ -542,6 +555,13 @@ function rcube_tasklist(settings)
}); });
} }
// remove from list index
var oldindex = listindex.indexOf(rec.id);
if (oldindex >= 0) {
slice = listindex.slice(0,oldindex);
listindex = slice.concat(listindex.slice(oldindex+1));
}
// find the right place to insert the task item // find the right place to insert the task item
li.siblings().each(function(i, elem){ li.siblings().each(function(i, elem){
next_li = $(elem); next_li = $(elem);
@ -558,17 +578,26 @@ function rcube_tasklist(settings)
} }
else if (next_rec && next_li && task_cmp(rec, next_rec) < 0) { else if (next_rec && next_li && task_cmp(rec, next_rec) < 0) {
if (animated) insert_animated(li, next_li); if (animated) insert_animated(li, next_li);
else li.insertBefore(next_li) else li.insertBefore(next_li);
next_li = null; next_li = null;
return false; return false;
} }
}); });
index = listindex.indexOf(next_id);
if (next_li) { if (next_li) {
if (animated) insert_animated(li, null, next_li); if (animated) insert_animated(li, null, next_li);
else li.insertAfter(next_li); else li.insertAfter(next_li);
index++;
}
// insert into list index
if (next_id && index >= 0) {
slice = listindex.slice(0,index);
slice.push(rec.id);
listindex = slice.concat(listindex.slice(index));
} }
return;
} }
/** /**
@ -617,15 +646,19 @@ function rcube_tasklist(settings)
{ {
var drag_id = draggable.data('id'), var drag_id = draggable.data('id'),
parent_id = $(this).data('id'), parent_id = $(this).data('id'),
rec = listdata[parent_id]; drag_rec = listdata[drag_id],
drop_rec = listdata[parent_id];
if (parent_id == listdata[drag_id].parent_id) if (drop_rec && drop_rec.list != drag_rec.list)
return false; return false;
while (rec && rec.parent_id) { if (parent_id == drag_rec.parent_id)
if (rec.parent_id == drag_id) return false;
while (drop_rec && drop_rec.parent_id) {
if (drop_rec.parent_id == drag_id)
return false; return false;
rec = listdata[rec.parent_id]; drop_rec = listdata[drop_rec.parent_id];
} }
return true; return true;
@ -672,6 +705,8 @@ function rcube_tasklist(settings)
$('#task-description').html(text2html(rec.description || '', 300, 6))[(rec.description ? 'show' : 'hide')](); $('#task-description').html(text2html(rec.description || '', 300, 6))[(rec.description ? 'show' : 'hide')]();
$('#task-date')[(rec.date ? 'show' : 'hide')]().children('.task-text').html(Q(rec.date || rcmail.gettext('nodate','tasklist'))); $('#task-date')[(rec.date ? 'show' : 'hide')]().children('.task-text').html(Q(rec.date || rcmail.gettext('nodate','tasklist')));
$('#task-time').html(Q(rec.time || '')); $('#task-time').html(Q(rec.time || ''));
$('#task-start')[(rec.startdate ? 'show' : 'hide')]().children('.task-text').html(Q(rec.startdate || ''));
$('#task-starttime').html(Q(rec.starttime || ''));
$('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%'); $('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%');
$('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : '')); $('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : ''));
@ -732,6 +767,8 @@ function rcube_tasklist(settings)
var description = $('#edit-description').val(rec.description || ''); var description = $('#edit-description').val(rec.description || '');
var recdate = $('#edit-date').val(rec.date || '').datepicker(datepicker_settings); var recdate = $('#edit-date').val(rec.date || '').datepicker(datepicker_settings);
var rectime = $('#edit-time').val(rec.time || ''); var rectime = $('#edit-time').val(rec.time || '');
var recstartdate = $('#edit-startdate').val(rec.startdate || '').datepicker(datepicker_settings);
var recstarttime = $('#edit-starttime').val(rec.starttime || '');
var complete = $('#edit-completeness').val((rec.complete || 0) * 100); var complete = $('#edit-completeness').val((rec.complete || 0) * 100);
completeness_slider.slider('value', complete.val()); completeness_slider.slider('value', complete.val());
var tasklist = $('#edit-tasklist').val(rec.list || 0); // .prop('disabled', rec.parent_id ? true : false); var tasklist = $('#edit-tasklist').val(rec.list || 0); // .prop('disabled', rec.parent_id ? true : false);
@ -755,22 +792,31 @@ function rcube_tasklist(settings)
texts: { removeLinkTitle: rcmail.gettext('removetag', 'tasklist') } texts: { removeLinkTitle: rcmail.gettext('removetag', 'tasklist') }
}); });
$('#edit-nodate').unbind('click').click(function(){ $('a.edit-nodate').unbind('click').click(function(){
recdate.val(''); var sel = $(this).attr('rel');
rectime.val(''); if (sel) $(sel).val('');
return false; return false;
}) })
// define dialog buttons // define dialog buttons
var buttons = {}; var buttons = {};
buttons[rcmail.gettext('save', 'tasklist')] = function() { buttons[rcmail.gettext('save', 'tasklist')] = function() {
me.selected_task.title = title.val(); // copy form field contents into task object to save
me.selected_task.description = description.val(); $.each({ title:title, description:description, date:recdate, time:rectime, startdate:recstartdate, starttime:recstarttime, list:tasklist }, function(key,input){
me.selected_task.date = recdate.val(); me.selected_task[key] = input.val();
me.selected_task.time = rectime.val(); });
me.selected_task.list = tasklist.val();
me.selected_task.tags = []; me.selected_task.tags = [];
// do some basic input validation
if (me.selected_task.startdate && me.selected_task.date) {
var startdate = $.datepicker.parseDate(datepicker_settings.dateFormat, me.selected_task.startdate, datepicker_settings);
var duedate = $.datepicker.parseDate(datepicker_settings.dateFormat, me.selected_task.date, datepicker_settings);
if (startdate > duedate) {
alert(rcmail.gettext('invalidstartduedates', 'tasklist'));
return false;
}
}
$('input[name="tags[]"]', rcmail.gui_objects.edittagline).each(function(i,elem){ $('input[name="tags[]"]', rcmail.gui_objects.edittagline).each(function(i,elem){
if (elem.value) if (elem.value)
me.selected_task.tags.push(elem.value); me.selected_task.tags.push(elem.value);
@ -812,9 +858,10 @@ function rcube_tasklist(settings)
$dialog.dialog('destroy').remove(); $dialog.dialog('destroy').remove();
}, },
buttons: buttons, buttons: buttons,
minHeight: 340,
minWidth: 500, minWidth: 500,
width: 580 width: 580
}).append(editform.show()); // adding form content AFTERWARDS massively speeds up opening on IE6 }).append(editform.show()); // adding form content AFTERWARDS massively speeds up opening on IE
title.select(); title.select();
} }

View file

@ -261,6 +261,18 @@ class tasklist extends rcube_plugin
} }
} }
if (!empty($rec['startdate'])) {
try {
$date = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone);
$rec['startdate'] = $date->format('Y-m-d');
if (!empty($rec['starttime']))
$rec['starttime'] = $date->format('H:i');
}
catch (Exception $e) {
$rec['startdate'] = $rec['starttime'] = null;
}
}
return $rec; return $rec;
} }
@ -400,6 +412,7 @@ class tasklist extends rcube_plugin
$rec['mask'] = $this->filter_mask($rec); $rec['mask'] = $this->filter_mask($rec);
$rec['flagged'] = intval($rec['flagged']); $rec['flagged'] = intval($rec['flagged']);
$rec['complete'] = floatval($rec['complete']); $rec['complete'] = floatval($rec['complete']);
$rec['changed'] = is_object($rec['changed']) ? $rec['changed']->format('U') : null;
if ($rec['date']) { if ($rec['date']) {
try { try {
@ -417,6 +430,17 @@ class tasklist extends rcube_plugin
$rec['_hasdate'] = 0; $rec['_hasdate'] = 0;
} }
if ($rec['startdate']) {
try {
$date = new DateTime($rec['startdate'] . ' ' . $rec['starttime'], $this->timezone);
$rec['startdatetime'] = intval($date->format('U'));
$rec['startdate'] = $date->format($this->rc->config->get('date_format', 'Y-m-d'));
}
catch (Exception $e) {
$rec['startdate'] = $rec['startdatetime'] = null;
}
}
if (!isset($rec['_depth'])) { if (!isset($rec['_depth'])) {
$rec['_depth'] = 0; $rec['_depth'] = 0;
$parent_id = $this->task_tree[$rec['id']]; $parent_id = $this->task_tree[$rec['id']];