Use status attribute to set a task as 'complete' (#3026)
This commit is contained in:
parent
d87c46acac
commit
3a2d5eed5a
13 changed files with 87 additions and 28 deletions
|
@ -44,7 +44,7 @@ class kolab_format_task extends kolab_format_xcal
|
|||
$this->obj->setPercentComplete(intval($object['complete']));
|
||||
|
||||
$status = kolabformat::StatusUndefined;
|
||||
if ($object['complete'] == 100)
|
||||
if ($object['complete'] == 100 && !array_key_exists('status', $object))
|
||||
$status = kolabformat::StatusCompleted;
|
||||
else if ($object['status'] && array_key_exists($object['status'], $this->status_map))
|
||||
$status = $this->status_map[$object['status']];
|
||||
|
@ -113,7 +113,7 @@ class kolab_format_task extends kolab_format_xcal
|
|||
{
|
||||
$tags = array();
|
||||
|
||||
if ($this->data['status'] == 'COMPLETED' || $this->data['complete'] == 100)
|
||||
if ($this->data['status'] == 'COMPLETED' || ($this->data['complete'] == 100 && empty($this->data['status'])))
|
||||
$tags[] = 'x-complete';
|
||||
|
||||
if ($this->data['priority'] == 1)
|
||||
|
|
|
@ -36,6 +36,7 @@ CREATE TABLE IF NOT EXISTS `tasks` (
|
|||
`starttime` varchar(5) DEFAULT NULL,
|
||||
`flagged` tinyint(4) NOT NULL DEFAULT '0',
|
||||
`complete` float NOT NULL DEFAULT '0',
|
||||
`status` enum('','NEEDS-ACTION','IN-PROCESS','COMPLETED','CANCELLED') NOT NULL DEFAULT '',
|
||||
`alarms` varchar(255) DEFAULT NULL,
|
||||
`recurrence` varchar(255) DEFAULT NULL,
|
||||
`organizer` varchar(255) DEFAULT NULL,
|
||||
|
@ -48,4 +49,4 @@ CREATE TABLE IF NOT EXISTS `tasks` (
|
|||
REFERENCES `tasklists`(`tasklist_id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) /*!40000 ENGINE=InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci */;
|
||||
|
||||
REPLACE INTO `system` (`name`, `value`) VALUES ('tasklist-database-version', '2013011000');
|
||||
REPLACE INTO `system` (`name`, `value`) VALUES ('tasklist-database-version', '2014051900');
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
ALTER TABLE `tasks` ADD `status` ENUM('','NEEDS-ACTION','IN-PROCESS','COMPLETED','CANCELLED') NOT NULL DEFAULT '' AFTER `complete`;
|
||||
|
||||
UPDATE `tasks` SET status='COMPLETED' WHERE complete=1.0 AND status='';
|
|
@ -49,6 +49,7 @@ CREATE TABLE tasks (
|
|||
starttime varchar(5) DEFAULT NULL,
|
||||
flagged smallint NOT NULL DEFAULT 0,
|
||||
complete float NOT NULL DEFAULT 0,
|
||||
status varchar(16) NOT NULL DEFAULT '',
|
||||
alarms varchar(255) DEFAULT NULL,
|
||||
recurrence varchar(255) DEFAULT NULL,
|
||||
organizer varchar(255) DEFAULT NULL,
|
||||
|
@ -60,4 +61,4 @@ CREATE TABLE tasks (
|
|||
CREATE INDEX tasks_tasklisting_idx ON tasks (tasklist_id, del, date);
|
||||
CREATE INDEX tasks_uid_idx ON tasks (uid);
|
||||
|
||||
INSERT INTO system (name, value) VALUES ('tasklist-database-version', '2013011000');
|
||||
INSERT INTO system (name, value) VALUES ('tasklist-database-version', '2014051900');
|
||||
|
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
class tasklist_database_driver extends tasklist_driver
|
||||
{
|
||||
const IS_COMPLETE_SQL = "(status='COMPLETED' OR (complete=1 AND status=''))";
|
||||
|
||||
public $undelete = true; // yes, we can
|
||||
public $sortable = false;
|
||||
public $alarm_types = array('DISPLAY');
|
||||
|
@ -224,7 +226,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
$result = $this->rc->db->query(sprintf(
|
||||
"SELECT task_id, flagged, date FROM " . $this->db_tasks . "
|
||||
WHERE tasklist_id IN (%s)
|
||||
AND del=0 AND complete<1",
|
||||
AND del=0 AND NOT " . self::IS_COMPLETE_SQL,
|
||||
join(',', $list_ids)
|
||||
));
|
||||
|
||||
|
@ -286,9 +288,9 @@ class tasklist_database_driver extends tasklist_driver
|
|||
$sql_add = ' AND date IS NULL';
|
||||
|
||||
if ($filter['mask'] & tasklist::FILTER_MASK_COMPLETE)
|
||||
$sql_add .= ' AND complete=1';
|
||||
$sql_add .= ' AND ' . self::IS_COMPLETE_SQL;
|
||||
else if (empty($filter['since'])) // don't show complete tasks by default
|
||||
$sql_add .= ' AND complete<1';
|
||||
$sql_add .= ' AND NOT ' . self::IS_COMPLETE_SQL;
|
||||
|
||||
if ($filter['mask'] & tasklist::FILTER_MASK_FLAGGED)
|
||||
$sql_add .= ' AND flagged=1';
|
||||
|
@ -435,7 +437,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
$result = $this->rc->db->query(sprintf(
|
||||
"SELECT * FROM " . $this->db_tasks . "
|
||||
WHERE tasklist_id IN (%s)
|
||||
AND notify <= %s AND complete < 1",
|
||||
AND notify <= %s AND NOT " . self::IS_COMPLETE_SQL,
|
||||
join(',', $list_ids),
|
||||
$this->rc->db->fromunixtime($time)
|
||||
));
|
||||
|
@ -529,7 +531,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
$prop['recurrence'] = $this->serialize_recurrence($prop['recurrence']);
|
||||
}
|
||||
|
||||
foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence') as $col) {
|
||||
foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence', 'status') as $col) {
|
||||
if (empty($prop[$col]))
|
||||
$prop[$col] = null;
|
||||
}
|
||||
|
@ -537,8 +539,8 @@ class tasklist_database_driver extends tasklist_driver
|
|||
$notify_at = $this->_get_notification($prop);
|
||||
$result = $this->rc->db->query(sprintf(
|
||||
"INSERT INTO " . $this->db_tasks . "
|
||||
(tasklist_id, uid, parent_id, created, changed, title, date, time, startdate, starttime, description, tags, flagged, complete, alarms, recurrence, notify)
|
||||
VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
(tasklist_id, uid, parent_id, created, changed, title, date, time, startdate, starttime, description, tags, flagged, complete, status, alarms, recurrence, notify)
|
||||
VALUES (?, ?, ?, %s, %s, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
$this->rc->db->now(),
|
||||
$this->rc->db->now()
|
||||
),
|
||||
|
@ -554,6 +556,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
join(',', (array)$prop['tags']),
|
||||
$prop['flagged'] ? 1 : 0,
|
||||
intval($prop['complete']),
|
||||
$prop['status'],
|
||||
$prop['alarms'],
|
||||
$prop['recurrence'],
|
||||
$notify_at
|
||||
|
@ -586,7 +589,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
if (isset($prop[$col]))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . $this->rc->db->quote($prop[$col]);
|
||||
}
|
||||
foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence') as $col) {
|
||||
foreach (array('parent_id', 'date', 'time', 'startdate', 'starttime', 'alarms', 'recurrence', 'status') as $col) {
|
||||
if (isset($prop[$col]))
|
||||
$sql_set[] = $this->rc->db->quote_identifier($col) . '=' . (empty($prop[$col]) ? 'NULL' : $this->rc->db->quote($prop[$col]));
|
||||
}
|
||||
|
@ -694,7 +697,7 @@ class tasklist_database_driver extends tasklist_driver
|
|||
*/
|
||||
private function _get_notification($task)
|
||||
{
|
||||
if ($task['valarms'] && $task['complete'] < 1) {
|
||||
if ($task['valarms'] && !$this->is_complete($task)) {
|
||||
$alarm = libcalendaring::get_next_alarm($task, 'task');
|
||||
|
||||
if ($alarm['time'] && in_array($alarm['action'], $this->alarm_types))
|
||||
|
|
|
@ -307,7 +307,7 @@ class tasklist_kolab_driver extends tasklist_driver
|
|||
foreach ($folder->select(array(array('tags','!~','x-complete'))) as $record) {
|
||||
$rec = $this->_to_rcube_task($record);
|
||||
|
||||
if ($rec['complete'] >= 1.0) // don't count complete tasks
|
||||
if ($this->is_complete($rec)) // don't count complete tasks
|
||||
continue;
|
||||
|
||||
$counts['all']++;
|
||||
|
@ -603,7 +603,8 @@ class tasklist_kolab_driver extends tasklist_driver
|
|||
'description' => $record['description'],
|
||||
'tags' => array_filter((array)$record['categories']),
|
||||
'flagged' => $record['priority'] == 1,
|
||||
'complete' => $record['status'] == 'COMPLETED' ? 1 : floatval($record['complete'] / 100),
|
||||
'complete' => floatval($record['complete'] / 100),
|
||||
'status' => $record['status'],
|
||||
'parent_id' => $record['parent_id'],
|
||||
'recurrence' => $record['recurrence'],
|
||||
);
|
||||
|
@ -672,7 +673,7 @@ class tasklist_kolab_driver extends tasklist_driver
|
|||
}
|
||||
|
||||
$object['complete'] = $task['complete'] * 100;
|
||||
if ($task['complete'] == 1.0)
|
||||
if ($task['complete'] == 1.0 && empty($task['complete']))
|
||||
$object['status'] = 'COMPLETED';
|
||||
|
||||
if ($task['flagged'])
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
* 'categories' => 'Task category',
|
||||
* 'flagged' => 'Boolean value whether this record is flagged',
|
||||
* 'complete' => 'Float value representing the completeness state (range 0..1)',
|
||||
* 'sensitivity' => 0|1|2, // Event sensitivity (0=public, 1=private, 2=confidential)
|
||||
* 'status' => 'Task status string according to (NEEDS-ACTION, IN-PROCESS, COMPLETED, CANCELLED) RFC 2445',
|
||||
* 'valarms' => array( // List of reminders (new format), each represented as a hash array:
|
||||
* array(
|
||||
* 'trigger' => '-PT90M', // ISO 8601 period string prefixed with '+' or '-', or DateTime object
|
||||
|
@ -270,6 +270,17 @@ abstract class tasklist_driver
|
|||
*/
|
||||
public function get_attachment_body($id, $task) { }
|
||||
|
||||
/**
|
||||
* Helper method to determine whether the given task is considered "complete"
|
||||
*
|
||||
* @param array $task Hash array with event properties:
|
||||
* @return boolean True if complete, False otherwiese
|
||||
*/
|
||||
public function is_complete($task)
|
||||
{
|
||||
return ($task['complete'] >= 1.0 && empty($task['status'])) || $task['status'] === 'COMPLETED';
|
||||
}
|
||||
|
||||
/**
|
||||
* List availabale categories
|
||||
* The default implementation reads them from config/user prefs
|
||||
|
|
|
@ -19,6 +19,11 @@ $labels['datetime'] = 'Due';
|
|||
$labels['start'] = 'Start';
|
||||
$labels['alarms'] = 'Reminder';
|
||||
$labels['repeat'] = 'Repeat';
|
||||
$labels['status'] = 'Status';
|
||||
$labels['status-needs-action'] = 'Needs action';
|
||||
$labels['status-in-process'] = 'In process';
|
||||
$labels['status-completed'] = 'Completed';
|
||||
$labels['status-cancelled'] = 'Cancelled';
|
||||
|
||||
$labels['all'] = 'All';
|
||||
$labels['flagged'] = 'Flagged';
|
||||
|
|
|
@ -141,6 +141,10 @@
|
|||
<label><roundcube:label name="tasklist.complete" /></label>
|
||||
<span class="task-text"></span>
|
||||
</div>
|
||||
<div id="task-status" class="form-section">
|
||||
<label><roundcube:label name="tasklist.status" /></label>
|
||||
<span class="task-text"></span>
|
||||
</div>
|
||||
<div id="task-attachments" class="form-section">
|
||||
<label><roundcube:label name="attachments" /></label>
|
||||
<div class="task-text"></div>
|
||||
|
|
|
@ -46,6 +46,10 @@
|
|||
<input type="text" name="title" id="taskedit-completeness" size="3" tabindex="25" /> %
|
||||
<div id="taskedit-completeness-slider"></div>
|
||||
</div>
|
||||
<div class="form-section">
|
||||
<label for="taskedit-status"><roundcube:label name="tasklist.status" /></label>
|
||||
<roundcube:object name="plugin.status_select" id="taskedit-status" tabindex="26" />
|
||||
</div>
|
||||
<div class="form-section" id="tasklist-select">
|
||||
<label for="taskedit-tasklist"><roundcube:label name="tasklist.list" /></label>
|
||||
<roundcube:object name="plugin.tasklist_select" id="taskedit-tasklist" tabindex="26" />
|
||||
|
|
|
@ -264,7 +264,7 @@ function rcube_tasklist_ui(settings)
|
|||
if (rcmail.busy)
|
||||
return false;
|
||||
|
||||
rec.complete = e.target.checked ? 1 : 0;
|
||||
rec.status = e.target.checked ? 'COMPLETED' : (rec.complete == 1 ? 'NEEDS-ACTION' : '');
|
||||
li.toggleClass('complete');
|
||||
save_task(rec, 'edit');
|
||||
return true;
|
||||
|
@ -815,7 +815,7 @@ function rcube_tasklist_ui(settings)
|
|||
|
||||
var div = $('<div>').addClass('taskhead').html(
|
||||
'<div class="progressbar"><div class="progressvalue" style="width:' + (rec.complete * 100) + '%"></div></div>' +
|
||||
'<input type="checkbox" name="completed[]" value="1" class="complete" ' + (rec.complete == 1.0 ? 'checked="checked" ' : '') + '/>' +
|
||||
'<input type="checkbox" name="completed[]" value="1" class="complete" ' + (is_complete(rec) ? 'checked="checked" ' : '') + '/>' +
|
||||
'<span class="flagged"></span>' +
|
||||
'<span class="title">' + text2html(Q(rec.title)) + '</span>' +
|
||||
'<span class="tags">' + tags_html + '</span>' +
|
||||
|
@ -835,7 +835,7 @@ function rcube_tasklist_ui(settings)
|
|||
revertDuration: 300
|
||||
});
|
||||
|
||||
if (rec.complete == 1.0)
|
||||
if (is_complete(rec))
|
||||
div.addClass('complete');
|
||||
if (rec.flagged)
|
||||
div.addClass('flagged');
|
||||
|
@ -951,12 +951,20 @@ function rcube_tasklist_ui(settings)
|
|||
*/
|
||||
function task_cmp(a, b)
|
||||
{
|
||||
var d = Math.floor(a.complete) - Math.floor(b.complete);
|
||||
var d = is_complete(a) - is_complete(b);
|
||||
if (!d) d = (b._hasdate-0) - (a._hasdate-0);
|
||||
if (!d) d = (a.datetime||99999999999) - (b.datetime||99999999999);
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given task should be displayed as "complete"
|
||||
*/
|
||||
function is_complete(rec)
|
||||
{
|
||||
return ((rec.complete == 1.0 && !rec.status) || rec.status === 'COMPLETED') ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
@ -1144,6 +1152,7 @@ function rcube_tasklist_ui(settings)
|
|||
$('#task-starttime').html(Q(rec.starttime || ''));
|
||||
$('#task-alarm')[(rec.alarms_text ? 'show' : 'hide')]().children('.task-text').html(Q(rec.alarms_text));
|
||||
$('#task-completeness .task-text').html(((rec.complete || 0) * 100) + '%');
|
||||
$('#task-status')[(rec.status ? 'show' : 'hide')]().children('.task-text').html(rcmail.gettext('status-'+String(rec.status).toLowerCase(),'tasklist'));
|
||||
$('#task-list .task-text').html(Q(me.tasklists[rec.list] ? me.tasklists[rec.list].name : ''));
|
||||
|
||||
var itags = get_inherited_tags(rec);
|
||||
|
@ -1257,6 +1266,7 @@ function rcube_tasklist_ui(settings)
|
|||
var recstarttime = $('#taskedit-starttime').val(rec.starttime || '');
|
||||
var complete = $('#taskedit-completeness').val((rec.complete || 0) * 100);
|
||||
completeness_slider.slider('value', complete.val());
|
||||
var taskstatus = $('#taskedit-status').val(rec.status || '');
|
||||
var tasklist = $('#taskedit-tasklist').val(rec.list || me.selected_list).prop('disabled', rec.parent_id ? true : false);
|
||||
|
||||
// tag-edit line
|
||||
|
@ -1308,7 +1318,7 @@ function rcube_tasklist_ui(settings)
|
|||
var buttons = {};
|
||||
buttons[rcmail.gettext('save', 'tasklist')] = function() {
|
||||
// copy form field contents into task object to save
|
||||
$.each({ title:title, description:description, date:recdate, time:rectime, startdate:recstartdate, starttime:recstarttime, list:tasklist }, function(key,input){
|
||||
$.each({ title:title, description:description, date:recdate, time:rectime, startdate:recstartdate, starttime:recstarttime, status:taskstatus, list:tasklist }, function(key,input){
|
||||
me.selected_task[key] = input.val();
|
||||
});
|
||||
me.selected_task.tags = [];
|
||||
|
|
|
@ -214,7 +214,7 @@ class tasklist extends rcube_plugin
|
|||
$child = array('id' => $cid, 'list' => $rec['list'], '_fromlist' => $rec['_fromlist']);
|
||||
if ($this->driver->move_task($child)) {
|
||||
$r = $this->driver->get_task($child);
|
||||
if ((bool)($filter & self::FILTER_MASK_COMPLETE) == ($r['complete'] == 1.0)) {
|
||||
if ((bool)($filter & self::FILTER_MASK_COMPLETE) == $this->driver->is_complete($r)) {
|
||||
$refresh[] = $r;
|
||||
}
|
||||
}
|
||||
|
@ -237,7 +237,7 @@ class tasklist extends rcube_plugin
|
|||
$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)) {
|
||||
if ((bool)($filter & self::FILTER_MASK_COMPLETE) == $this->driver->is_complete($r)) {
|
||||
$refresh[] = $r;
|
||||
}
|
||||
}
|
||||
|
@ -524,7 +524,7 @@ class tasklist extends rcube_plugin
|
|||
private function handle_recurrence(&$rec, $old)
|
||||
{
|
||||
$clone = null;
|
||||
if ($rec['complete'] == 1.0 && $old && $old['complete'] < 1.0 && is_array($rec['recurrence'])) {
|
||||
if ($this->driver->is_complete($rec) && $old && $this->driver->is_complete($old) && is_array($rec['recurrence'])) {
|
||||
$engine = libcalendaring::get_recurrence();
|
||||
$rrule = $rec['recurrence'];
|
||||
$updates = array();
|
||||
|
@ -569,6 +569,7 @@ class tasklist extends rcube_plugin
|
|||
// update the task but unset completed flag
|
||||
$rec = array_merge($rec, $updates);
|
||||
$rec['complete'] = $old['complete'];
|
||||
$rec['satus'] = $old['satus'];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -718,7 +719,7 @@ class tasklist extends rcube_plugin
|
|||
$tags = array_merge($tags, (array)$rec['tags']);
|
||||
|
||||
// apply filter; don't trust the driver on this :-)
|
||||
if ((!$f && $rec['complete'] < 1.0) || ($rec['mask'] & $f))
|
||||
if ((!$f && !$this->driver->is_complete($rec)) || ($rec['mask'] & $f))
|
||||
$data[] = $rec;
|
||||
}
|
||||
|
||||
|
@ -845,7 +846,7 @@ class tasklist extends rcube_plugin
|
|||
|
||||
if ($rec['flagged'])
|
||||
$mask |= self::FILTER_MASK_FLAGGED;
|
||||
if ($rec['complete'] == 1.0)
|
||||
if ($this->driver->is_complete($rec))
|
||||
$mask |= self::FILTER_MASK_COMPLETE;
|
||||
|
||||
if (empty($rec['date']))
|
||||
|
|
|
@ -67,7 +67,7 @@ class tasklist_ui
|
|||
{
|
||||
$this->plugin->register_handler('plugin.tasklists', array($this, 'tasklists'));
|
||||
$this->plugin->register_handler('plugin.tasklist_select', array($this, 'tasklist_select'));
|
||||
$this->plugin->register_handler('plugin.category_select', array($this, 'category_select'));
|
||||
$this->plugin->register_handler('plugin.status_select', array($this, 'status_select'));
|
||||
$this->plugin->register_handler('plugin.searchform', array($this->rc->output, 'search_form'));
|
||||
$this->plugin->register_handler('plugin.quickaddform', array($this, 'quickadd_form'));
|
||||
$this->plugin->register_handler('plugin.tasks', array($this, 'tasks_resultview'));
|
||||
|
@ -129,6 +129,21 @@ class tasklist_ui
|
|||
return html::tag('ul', $attrib, $li, html::$common_attrib);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render HTML form for task status selector
|
||||
*/
|
||||
function status_select($attrib = array())
|
||||
{
|
||||
$attrib['name'] = 'status';
|
||||
$select = new html_select($attrib);
|
||||
$select->add('---', '');
|
||||
$select->add($this->plugin->gettext('status-needs-action'), 'NEEDS-ACTION');
|
||||
$select->add($this->plugin->gettext('status-in-process'), 'IN-PROCESS');
|
||||
$select->add($this->plugin->gettext('status-completed'), 'COMPLETED');
|
||||
$select->add($this->plugin->gettext('status-cancelled'), 'CANCELLED');
|
||||
|
||||
return $select->show(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a HTML select box for list selection
|
||||
|
|
Loading…
Add table
Reference in a new issue