summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog1
-rw-r--r--app/Action/TaskAssignColorOnDueDate.php100
-rw-r--r--app/ServiceProvider/ActionProvider.php2
-rw-r--r--app/Template/board/table_column.php29
-rw-r--r--assets/js/components/calendar.js21
-rw-r--r--tests/units/Action/TaskAssignColorOnDueDateTest.php37
6 files changed, 178 insertions, 12 deletions
diff --git a/ChangeLog b/ChangeLog
index 30b6d0e2..10c41b2c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -7,6 +7,7 @@ New features:
* Send tasks by email
* Add Reply-To header to emails sent from Kanboard
* Upload Sqlite database from user interface
+* Automatic action to change task color when due date is expired
Improvements:
diff --git a/app/Action/TaskAssignColorOnDueDate.php b/app/Action/TaskAssignColorOnDueDate.php
new file mode 100644
index 00000000..06b70a18
--- /dev/null
+++ b/app/Action/TaskAssignColorOnDueDate.php
@@ -0,0 +1,100 @@
+<?php
+
+namespace Kanboard\Action;
+
+use Kanboard\Model\TaskModel;
+
+
+/**
+ * Assign a color to a priority
+ *
+ * @package Kanboard\Action
+ * @author Julien Buratto
+ */
+class TaskAssignColorOnDueDate extends Base
+{
+ /**
+ * Get action description
+ *
+ * @access public
+ * @return string
+ */
+ public function getDescription()
+ {
+ return t('Assign automatically a color when due date is expired');
+ }
+
+ /**
+ * Get the list of compatible events
+ *
+ * @access public
+ * @return array
+ */
+ public function getCompatibleEvents()
+ {
+ return array(
+ TaskModel::EVENT_DAILY_CRONJOB,
+ );
+ }
+
+ /**
+ * Get the required parameter for the action
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'color_id' => t('Color'),
+ );
+ }
+
+ /**
+ * Get all tasks
+ *
+ * @access public
+ * @return array
+ */
+
+ public function getEventRequiredParameters()
+ {
+ return array('tasks');
+ }
+
+ /**
+ * Execute the action (change the task color)
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool True if the action was executed or false when not executed
+ */
+ public function doAction(array $data)
+ {
+ $results = array();
+
+ foreach ($data['tasks'] as $task) {
+ if ($task['date_due'] <= time() && $task['date_due'] > 0) {
+ $values = array(
+ 'id' => $task['id'],
+ 'color_id' => $this->getParam('color_id'),
+ );
+ $results[] = $this->taskModificationModel->update($values, false);
+ }
+ }
+
+ return in_array(true, $results, true);
+ }
+
+ /**
+ * Check if the event data meet the action condition
+ *
+ * @access public
+ * @param array $data Event data dictionary
+ * @return bool
+ */
+ public function hasRequiredCondition(array $data)
+ {
+ return count($data['tasks']) > 0;
+ }
+}
diff --git a/app/ServiceProvider/ActionProvider.php b/app/ServiceProvider/ActionProvider.php
index 81f2b39e..a7e8040e 100644
--- a/app/ServiceProvider/ActionProvider.php
+++ b/app/ServiceProvider/ActionProvider.php
@@ -4,6 +4,7 @@ namespace Kanboard\ServiceProvider;
use Pimple\Container;
use Pimple\ServiceProviderInterface;
+use Kanboard\Action\TaskAssignColorOnDueDate;
use Kanboard\Action\TaskAssignColorPriority;
use Kanboard\Action\TaskAssignDueDateOnCreation;
use Kanboard\Action\TaskMoveColumnClosed;
@@ -92,6 +93,7 @@ class ActionProvider implements ServiceProviderInterface
$container['actionManager']->register(new TaskAssignDueDateOnCreation($container));
$container['actionManager']->register(new TaskAssignColorSwimlane($container));
$container['actionManager']->register(new TaskAssignPrioritySwimlane($container));
+ $container['actionManager']->register(new TaskAssignColorOnDueDate($container));
return $container;
}
diff --git a/app/Template/board/table_column.php b/app/Template/board/table_column.php
index df16715f..c2d6b9fc 100644
--- a/app/Template/board/table_column.php
+++ b/app/Template/board/table_column.php
@@ -53,17 +53,26 @@
<?php endif ?>
</span>
- <?php if (! $not_editable && ! empty($column['description'])): ?>
- <span class="tooltip pull-right" title="<?= $this->text->markdownAttribute($column['description']) ?>">
- &nbsp;<i class="fa fa-info-circle"></i>
- </span>
- <?php endif ?>
+ <span class="pull-right">
+ <?php if ($swimlane['nb_swimlanes'] > 1 && ! empty($column['column_score'])): ?>
+ <span title="<?= t('Total score in this column across all swimlanes') ?>">
+ (<span><?= $column['column_score'] ?></span>)
+ </span>
+ <?php endif ?>
- <?php if (! empty($column['score'])): ?>
- <span class="pull-right" title="<?= t('Score') ?>">
- <?= $column['score'] ?>
- </span>
- <?php endif ?>
+ <?php if (! empty($column['score'])): ?>
+ <span title="<?= t('Score') ?>">
+ <?= $column['score'] ?>
+ </span>
+ <?php endif ?>
+
+ <?php if (! $not_editable && ! empty($column['description'])): ?>
+ <span class="tooltip" title="<?= $this->text->markdownAttribute($column['description']) ?>">
+ &nbsp;<i class="fa fa-info-circle"></i>
+ </span>
+ <?php endif ?>
+
+ </span>
<?php if ($column['task_limit']): ?>
<span title="<?= t('Task limit') ?>">
diff --git a/assets/js/components/calendar.js b/assets/js/components/calendar.js
index d07c911d..ed6916b2 100644
--- a/assets/js/components/calendar.js
+++ b/assets/js/components/calendar.js
@@ -1,13 +1,23 @@
KB.component('calendar', function (containerElement, options) {
+ var modeMapping = { // Let's have bookable pretty mode names
+ month: 'month',
+ week: 'agendaWeek',
+ day: 'agendaDay'
+ };
this.render = function () {
var calendar = $(containerElement);
+ var mode = 'month';
+ if (window.location.hash) { // Check if hash contains mode
+ var hashMode = window.location.hash.substr(1);
+ mode = modeMapping[hashMode] || mode;
+ }
calendar.fullCalendar({
locale: $("body").data("js-lang"),
editable: true,
eventLimit: true,
- defaultView: "month",
+ defaultView: mode,
header: {
left: 'prev,next today',
center: 'title',
@@ -26,7 +36,14 @@ KB.component('calendar', function (containerElement, options) {
})
});
},
- viewRender: function() {
+ viewRender: function(view) {
+ // Map view.name back and update location.hash
+ for (var id in modeMapping) {
+ if (modeMapping[id] === view.name) { // Found
+ window.location.hash = id;
+ break;
+ }
+ }
var url = options.checkUrl;
var params = {
"start": calendar.fullCalendar('getView').start.format(),
diff --git a/tests/units/Action/TaskAssignColorOnDueDateTest.php b/tests/units/Action/TaskAssignColorOnDueDateTest.php
new file mode 100644
index 00000000..4bb87c06
--- /dev/null
+++ b/tests/units/Action/TaskAssignColorOnDueDateTest.php
@@ -0,0 +1,37 @@
+<?php
+
+require_once __DIR__.'/../Base.php';
+
+use Kanboard\Action\TaskAssignColorOnDueDate;
+use Kanboard\Event\TaskListEvent;
+use Kanboard\Model\TaskCreationModel;
+use Kanboard\Model\TaskFinderModel;
+use Kanboard\Model\ProjectModel;
+use Kanboard\Model\TaskModel;
+
+class TaskAssignColorOnDueDateTest extends Base
+{
+ public function testChangeColor()
+ {
+ $projectModel = new ProjectModel($this->container);
+ $taskCreationModel = new TaskCreationModel($this->container);
+ $taskFinderModel = new TaskFinderModel($this->container);
+
+ $this->assertEquals(1, $projectModel->create(array('name' => 'test1')));
+ $this->assertEquals(1, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test', 'date_due' => strtotime('-1 day'))));
+ $this->assertEquals(2, $taskCreationModel->create(array('project_id' => 1, 'title' => 'test')));
+
+ $tasks = $taskFinderModel->getAll(1);
+ $event = new TaskListEvent(array('tasks' => $tasks, 'project_id' => 1));
+
+ $action = new TaskAssignColorOnDueDate($this->container);
+ $action->setProjectId(1);
+ $action->setParam('color_id', 'red');
+
+ $this->assertTrue($action->execute($event, TaskModel::EVENT_DAILY_CRONJOB));
+
+ $tasks = $taskFinderModel->getAll(1);
+ $this->assertEquals('red', $tasks[0]['color_id']);
+ $this->assertEquals('yellow', $tasks[1]['color_id']);
+ }
+}