TODO/DONE auto-mark done when all subtasks done
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
namespace Kanboard\Plugin\OrganonTweaks\Helper;
|
||||
|
||||
use Kanboard\Core\Base;
|
||||
use Kanboard\Model\SubtaskModel;
|
||||
|
||||
/**
|
||||
* Done/Todo badge helper.
|
||||
@@ -22,6 +23,8 @@ class OrganonDoneHelper extends Base
|
||||
{
|
||||
const DONE_KEY = 'organon_done';
|
||||
const CLOSES_KEY = 'organon_tweaks_done_closes_task';
|
||||
const AUTOSUB_KEY = 'organon_tweaks_done_autosubtasks';
|
||||
const ALLDONE_KEY = 'organon_subtasks_alldone';
|
||||
|
||||
/**
|
||||
* Is this task Done? (mode-aware: closed status in close-mode, else the metadata marker)
|
||||
@@ -47,4 +50,63 @@ class OrganonDoneHelper extends Base
|
||||
{
|
||||
return (int) $this->configModel->get(self::CLOSES_KEY, 0) === 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task Done in the current mode. Idempotent -- a no-op if it is already Done. Close-mode
|
||||
* closes the task; marker-mode sets the DONE_KEY metadata.
|
||||
*
|
||||
* @param int $task_id
|
||||
*/
|
||||
public function markDone($task_id)
|
||||
{
|
||||
$task = $this->taskFinderModel->getById((int) $task_id);
|
||||
|
||||
if (empty($task) || $this->isDone($task)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->closesTask()) {
|
||||
$this->taskStatusModel->close((int) $task_id);
|
||||
} else {
|
||||
$this->taskMetadataModel->save((int) $task_id, array(self::DONE_KEY => 'on'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recompute whether the task's subtasks are ALL done and, only on the up-transition
|
||||
* (was-not-all-done -> now-all-done), auto-mark the task Done. The last-seen all-done state is kept
|
||||
* in ALLDONE_KEY ('on'/'off') to detect that edge -- so a manual unmark is honored (nothing
|
||||
* re-fires) and title edits / refreshes never re-mark. Never auto-unmarks.
|
||||
*
|
||||
* @param int $task_id
|
||||
* @param int $exclude_id a subtask id to exclude from the counts (the row being deleted --
|
||||
* EVENT_DELETE fires BEFORE the row is removed, so it still counts)
|
||||
*/
|
||||
public function syncSubtasksDone($task_id, $exclude_id = 0)
|
||||
{
|
||||
$task_id = (int) $task_id;
|
||||
$exclude_id = (int) $exclude_id;
|
||||
|
||||
$totalQuery = $this->db->table(SubtaskModel::TABLE)->eq('task_id', $task_id);
|
||||
$doneQuery = $this->db->table(SubtaskModel::TABLE)->eq('task_id', $task_id)->eq('status', SubtaskModel::STATUS_DONE);
|
||||
|
||||
if ($exclude_id > 0) {
|
||||
$totalQuery->neq('id', $exclude_id);
|
||||
$doneQuery->neq('id', $exclude_id);
|
||||
}
|
||||
|
||||
$total = $totalQuery->count();
|
||||
$done = $doneQuery->count();
|
||||
$allDone = $total > 0 && $done === $total;
|
||||
$prev = $this->taskMetadataModel->get($task_id, self::ALLDONE_KEY, 'off') === 'on';
|
||||
|
||||
// Save the new state BEFORE marking: in close-mode markDone() -> close() -> closeAll() re-fires
|
||||
// subtask events into this method; with the marker already 'on', that re-entry sees no
|
||||
// transition and is a no-op (no loop).
|
||||
$this->taskMetadataModel->save($task_id, array(self::ALLDONE_KEY => $allDone ? 'on' : 'off'));
|
||||
|
||||
if ($allDone && ! $prev) {
|
||||
$this->markDone($task_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user