summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrédéric Guillot <fred@kanboard.net>2014-05-21 22:33:57 -0400
committerFrédéric Guillot <fred@kanboard.net>2014-05-21 22:33:57 -0400
commita750b8ab2a0cb715da6fd9025a7ec8375db68a4d (patch)
tree5d5cdac1830336baf93b057e93cd2c1c56f405de
parent57e40671af56ae49eda467d9d5949bf9707020ee (diff)
Add categories for projects and tasks
-rw-r--r--actions/task_assign_color_category.php85
-rw-r--r--actions/task_assign_color_user.php4
-rw-r--r--assets/css/app.css30
-rw-r--r--assets/js/board.js35
-rw-r--r--common.php7
-rw-r--r--controllers/action.php2
-rw-r--r--controllers/base.php9
-rw-r--r--controllers/board.php14
-rw-r--r--controllers/category.php191
-rw-r--r--controllers/project.php4
-rw-r--r--controllers/task.php6
-rw-r--r--core/helper.php4
-rw-r--r--docs/automatic-actions.markdown12
-rw-r--r--locales/es_ES/translations.php21
-rw-r--r--locales/fr_FR/translations.php21
-rw-r--r--locales/pl_PL/translations.php21
-rw-r--r--locales/pt_BR/translations.php21
-rw-r--r--models/action.php6
-rw-r--r--models/category.php152
-rw-r--r--models/project.php6
-rw-r--r--models/task.php16
-rw-r--r--schemas/mysql.php16
-rw-r--r--schemas/sqlite.php15
-rw-r--r--templates/action_index.php2
-rw-r--r--templates/action_params.php3
-rw-r--r--templates/board_index.php6
-rw-r--r--templates/board_public.php11
-rw-r--r--templates/board_show.php13
-rw-r--r--templates/category_edit.php24
-rw-r--r--templates/category_index.php48
-rw-r--r--templates/category_remove.php16
-rw-r--r--templates/project_index.php3
-rw-r--r--templates/project_search.php4
-rw-r--r--templates/project_tasks.php4
-rw-r--r--templates/task_edit.php3
-rw-r--r--templates/task_new.php3
-rw-r--r--templates/task_show.php5
-rw-r--r--tests/Base.php2
-rw-r--r--tests/TaskTest.php14
39 files changed, 815 insertions, 44 deletions
diff --git a/actions/task_assign_color_category.php b/actions/task_assign_color_category.php
new file mode 100644
index 00000000..6fba5223
--- /dev/null
+++ b/actions/task_assign_color_category.php
@@ -0,0 +1,85 @@
+<?php
+
+namespace Action;
+
+require_once __DIR__.'/base.php';
+
+/**
+ * Assign a color to a specific category
+ *
+ * @package action
+ * @author Frederic Guillot
+ */
+class TaskAssignColorCategory extends Base
+{
+ /**
+ * Task model
+ *
+ * @accesss private
+ * @var \Model\Task
+ */
+ private $task;
+
+ /**
+ * Constructor
+ *
+ * @access public
+ * @param integer $project_id Project id
+ * @param \Model\Task $task Task model instance
+ */
+ public function __construct($project_id, \Model\Task $task)
+ {
+ parent::__construct($project_id);
+ $this->task = $task;
+ }
+
+ /**
+ * Get the required parameter for the action (defined by the user)
+ *
+ * @access public
+ * @return array
+ */
+ public function getActionRequiredParameters()
+ {
+ return array(
+ 'color_id' => t('Color'),
+ 'category_id' => t('Category'),
+ );
+ }
+
+ /**
+ * Get the required parameter for the event
+ *
+ * @access public
+ * @return string[]
+ */
+ public function getEventRequiredParameters()
+ {
+ return array(
+ 'task_id',
+ 'category_id',
+ );
+ }
+
+ /**
+ * Execute the action
+ *
+ * @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)
+ {
+ if ($data['category_id'] == $this->getParam('category_id')) {
+
+ $this->task->update(array(
+ 'id' => $data['task_id'],
+ 'color_id' => $this->getParam('color_id'),
+ ));
+
+ return true;
+ }
+
+ return false;
+ }
+}
diff --git a/actions/task_assign_color_user.php b/actions/task_assign_color_user.php
index d3ce1fbe..d5784270 100644
--- a/actions/task_assign_color_user.php
+++ b/actions/task_assign_color_user.php
@@ -42,7 +42,6 @@ class TaskAssignColorUser extends Base
public function getActionRequiredParameters()
{
return array(
- 'column_id' => t('Column'),
'color_id' => t('Color'),
'user_id' => t('Assignee'),
);
@@ -59,7 +58,6 @@ class TaskAssignColorUser extends Base
return array(
'task_id',
'owner_id',
- 'column_id',
);
}
@@ -72,7 +70,7 @@ class TaskAssignColorUser extends Base
*/
public function doAction(array $data)
{
- if ($data['column_id'] == $this->getParam('column_id') && $data['owner_id'] == $this->getParam('user_id')) {
+ if ($data['owner_id'] == $this->getParam('user_id')) {
$this->task->update(array(
'id' => $data['task_id'],
diff --git a/assets/css/app.css b/assets/css/app.css
index 67e4e6df..45ec7444 100644
--- a/assets/css/app.css
+++ b/assets/css/app.css
@@ -484,23 +484,26 @@ nav .active a {
}
.project-menu li {
+ padding-left: 5px;
display: inline;
- padding-left: 10px;
- padding-right: 10px;
border-right: 1px dotted #ccc;
}
.project-menu li:last-child {
border: none;
- padding-right: 0;
}
.project-menu ul {
padding-bottom: 5px;
}
+.project-menu a {
+ padding-right: 5px;
+}
+
.project-menu {
text-align: right;
+ font-size: 0.8em;
}
.public-board {
@@ -540,7 +543,26 @@ a.filter-on {
color: #444;
}
+.task-category-container {
+ text-align: right;
+ padding-bottom: 2px;
+}
+
+.task-category {
+ font-weight: bold;
+ font-size: 0.8em;
+ color: #000;
+ border: 1px solid #555;
+ border-radius: 4px;
+ padding: 2px;
+ padding-right: 5px;
+ padding-left: 5px;
+}
+
.task-date {
+ position: absolute;
+ bottom: 0;
+ left: 5px;
font-weight: bold;
color: #D90000;
}
@@ -552,7 +574,7 @@ a.filter-on {
}
.task-footer {
- margin-top: 10px;
+ height: 18px;
}
.task {
diff --git a/assets/js/board.js b/assets/js/board.js
index 84aca8b3..af54d0c8 100644
--- a/assets/js/board.js
+++ b/assets/js/board.js
@@ -76,7 +76,7 @@
$("#board").remove();
$("#main").append(data);
board_load_events();
- filter_apply(filter_get_user_id(), filter_has_due_date());
+ filter_apply();
}
});
}
@@ -96,32 +96,25 @@
$("#main").append(data);
board_unload_events();
board_load_events();
- filter_apply(filter_get_user_id(), filter_has_due_date());
+ filter_apply();
}
}
});
}
}
- // Get the selected user id
- function filter_get_user_id()
- {
- return $("#form-user_id").val();
- }
-
- // Return true if the filter is activated
- function filter_has_due_date()
- {
- return $("#filter-due-date").hasClass("filter-on");
- }
-
// Apply user or date filter (change tasks opacity)
- function filter_apply(selectedUserId, filterDueDate)
+ function filter_apply()
{
+ var selectedUserId = $("#form-user_id").val();
+ var selectedCategoryId = $("#form-category_id").val();
+ var filterDueDate = $("#filter-due-date").hasClass("filter-on");
+
$("[data-task-id]").each(function(index, item) {
var ownerId = item.getAttribute("data-owner-id");
var dueDate = item.getAttribute("data-due-date");
+ var categoryId = item.getAttribute("data-category-id");
if (ownerId != selectedUserId && selectedUserId != -1) {
item.style.opacity = "0.2";
@@ -133,19 +126,23 @@
if (filterDueDate && (dueDate == "" || dueDate == "0")) {
item.style.opacity = "0.2";
}
+
+ if (categoryId != selectedCategoryId && selectedCategoryId != -1) {
+ item.style.opacity = "0.2";
+ }
});
}
// Load filter events
function filter_load_events()
{
- $("#form-user_id").change(function() {
- filter_apply(filter_get_user_id(), filter_has_due_date());
- });
+ $("#form-user_id").change(filter_apply);
+
+ $("#form-category_id").change(filter_apply);
$("#filter-due-date").click(function(e) {
$(this).toggleClass("filter-on");
- filter_apply(filter_get_user_id(), filter_has_due_date());
+ filter_apply();
e.preventDefault();
});
}
diff --git a/common.php b/common.php
index 75b92df4..70b18a51 100644
--- a/common.php
+++ b/common.php
@@ -6,7 +6,7 @@ require __DIR__.'/core/translator.php';
$registry = new Core\Registry;
-$registry->db_version = 15;
+$registry->db_version = 16;
$registry->db = function() use ($registry) {
require __DIR__.'/vendor/PicoDb/Database.php';
@@ -110,6 +110,11 @@ $registry->google = function() use ($registry) {
return new \Model\Google($registry->shared('db'), $registry->shared('event'));
};
+$registry->category = function() use ($registry) {
+ require_once __DIR__.'/models/category.php';
+ return new \Model\Category($registry->shared('db'), $registry->shared('event'));
+};
+
if (file_exists('config.php')) require 'config.php';
// Board refresh frequency in seconds for the public board view
diff --git a/controllers/action.php b/controllers/action.php
index b4006940..5c5d5726 100644
--- a/controllers/action.php
+++ b/controllers/action.php
@@ -38,6 +38,7 @@ class Action extends Base
'users_list' => $this->project->getUsersList($project['id'], false),
'projects_list' => $this->project->getList(false),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($project['id'], false),
'menu' => 'projects',
'title' => t('Automatic actions')
)));
@@ -68,6 +69,7 @@ class Action extends Base
'users_list' => $this->project->getUsersList($project['id'], false),
'projects_list' => $this->project->getList(false),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($project['id'], false),
'project' => $project,
'menu' => 'projects',
'title' => t('Automatic actions')
diff --git a/controllers/base.php b/controllers/base.php
index 07c5db63..49376dce 100644
--- a/controllers/base.php
+++ b/controllers/base.php
@@ -131,6 +131,14 @@ abstract class Base
protected $google;
/**
+ * Category model
+ *
+ * @accesss protected
+ * @var \Model\Category
+ */
+ protected $category;
+
+ /**
* Event instance
*
* @accesss protected
@@ -157,6 +165,7 @@ abstract class Base
$this->rememberMe = $registry->rememberMe;
$this->lastLogin = $registry->lastLogin;
$this->google = $registry->google;
+ $this->category = $registry->category;
$this->event = $registry->shared('event');
}
diff --git a/controllers/board.php b/controllers/board.php
index 02669e3a..f7128210 100644
--- a/controllers/board.php
+++ b/controllers/board.php
@@ -128,6 +128,7 @@ class Board extends Base
$this->response->html($this->template->layout('board_public', array(
'project' => $project,
'columns' => $this->board->get($project['id']),
+ 'categories' => $this->category->getList($project['id'], false),
'title' => $project['name'],
'no_layout' => true,
'auto_refresh' => true,
@@ -195,6 +196,7 @@ class Board extends Base
'current_project_id' => $project_id,
'current_project_name' => $projects[$project_id],
'board' => $this->board->get($project_id),
+ 'categories' => $this->category->getList($project_id, true, true),
'menu' => 'boards',
'title' => $projects[$project_id]
)));
@@ -369,7 +371,11 @@ class Board extends Base
}
$this->response->html(
- $this->template->load('board_show', array('current_project_id' => $project_id, 'board' => $this->board->get($project_id))),
+ $this->template->load('board_show', array(
+ 'current_project_id' => $project_id,
+ 'board' => $this->board->get($project_id),
+ 'categories' => $this->category->getList($project_id, false),
+ )),
201
);
}
@@ -390,7 +396,11 @@ class Board extends Base
if ($this->project->isModifiedSince($project_id, $timestamp)) {
$this->response->html(
- $this->template->load('board_show', array('current_project_id' => $project_id, 'board' => $this->board->get($project_id)))
+ $this->template->load('board_show', array(
+ 'current_project_id' => $project_id,
+ 'board' => $this->board->get($project_id),
+ 'categories' => $this->category->getList($project_id, false),
+ ))
);
}
else {
diff --git a/controllers/category.php b/controllers/category.php
new file mode 100644
index 00000000..23de2735
--- /dev/null
+++ b/controllers/category.php
@@ -0,0 +1,191 @@
+<?php
+
+namespace Controller;
+
+require_once __DIR__.'/base.php';
+
+/**
+ * Categories management
+ *
+ * @package controller
+ * @author Frederic Guillot
+ */
+class Category extends Base
+{
+ /**
+ * Get the current project (common method between actions)
+ *
+ * @access private
+ * @return array
+ */
+ private function getProject()
+ {
+ $project_id = $this->request->getIntegerParam('project_id');
+ $project = $this->project->getById($project_id);
+
+ if (! $project) {
+ $this->session->flashError(t('Project not found.'));
+ $this->response->redirect('?controller=project');
+ }
+
+ return $project;
+ }
+
+ /**
+ * Get the category (common method between actions)
+ *
+ * @access private
+ * @return array
+ */
+ private function getCategory($project_id)
+ {
+ $category = $this->category->getById($this->request->getIntegerParam('category_id'));
+
+ if (! $category) {
+ $this->session->flashError(t('Category not found.'));
+ $this->response->redirect('?controller=category&action=index&project_id='.$project_id);
+ }
+
+ return $category;
+ }
+
+ /**
+ * List of categories for a given project
+ *
+ * @access public
+ */
+ public function index()
+ {
+ $project = $this->getProject();
+
+ $this->response->html($this->template->layout('category_index', array(
+ 'categories' => $this->category->getList($project['id'], false),
+ 'values' => array('project_id' => $project['id']),
+ 'errors' => array(),
+ 'project' => $project,
+ 'menu' => 'projects',
+ 'title' => t('Categories')
+ )));
+ }
+
+ /**
+ * Validate and save a new project
+ *
+ * @access public
+ */
+ public function save()
+ {
+ $project = $this->getProject();
+
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->category->validateCreation($values);
+
+ if ($valid) {
+
+ if ($this->category->create($values)) {
+ $this->session->flash(t('Your category have been created successfully.'));
+ $this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
+ }
+ else {
+ $this->session->flashError(t('Unable to create your category.'));
+ }
+ }
+
+ $this->response->html($this->template->layout('category_index', array(
+ 'categories' => $this->category->getList($project['id'], false),
+ 'values' => $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'menu' => 'projects',
+ 'title' => t('Categories')
+ )));
+ }
+
+ /**
+ * Edit a category (display the form)
+ *
+ * @access public
+ */
+ public function edit()
+ {
+ $project = $this->getProject();
+ $category = $this->getCategory($project['id']);
+
+ $this->response->html($this->template->layout('category_edit', array(
+ 'values' => $category,
+ 'errors' => array(),
+ 'project' => $project,
+ 'menu' => 'projects',
+ 'title' => t('Categories')
+ )));
+ }
+
+ /**
+ * Edit a category (validate the form and update the database)
+ *
+ * @access public
+ */
+ public function update()
+ {
+ $project = $this->getProject();
+
+ $values = $this->request->getValues();
+ list($valid, $errors) = $this->category->validateModification($values);
+
+ if ($valid) {
+
+ if ($this->category->update($values)) {
+ $this->session->flash(t('Your category have been updated successfully.'));
+ $this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
+ }
+ else {
+ $this->session->flashError(t('Unable to update your category.'));
+ }
+ }
+
+ $this->response->html($this->template->layout('category_edit', array(
+ 'values' => $values,
+ 'errors' => $errors,
+ 'project' => $project,
+ 'menu' => 'projects',
+ 'title' => t('Categories')
+ )));
+ }
+
+ /**
+ * Confirmation dialog before removing a category
+ *
+ * @access public
+ */
+ public function confirm()
+ {
+ $project = $this->getProject();
+ $category = $this->getCategory($project['id']);
+
+ $this->response->html($this->template->layout('category_remove', array(
+ 'project' => $project,
+ 'category' => $category,
+ 'menu' => 'projects',
+ 'title' => t('Remove a category')
+ )));
+ }
+
+ /**
+ * Remove a category
+ *
+ * @access public
+ */
+ public function remove()
+ {
+ $project = $this->getProject();
+ $category = $this->getCategory($project['id']);
+
+ if ($this->category->remove($category['id'])) {
+ $this->session->flash(t('Category removed successfully.'));
+ } else {
+ $this->session->flashError(t('Unable to remove this category.'));
+ }
+
+ $this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
+ }
+}
diff --git a/controllers/project.php b/controllers/project.php
index a9ce8634..1b9fd110 100644
--- a/controllers/project.php
+++ b/controllers/project.php
@@ -52,7 +52,7 @@ class Project extends Base
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id),
'or' => array(
array('column' => 'title', 'operator' => 'like', 'value' => '%'.$search.'%'),
- array('column' => 'description', 'operator' => 'like', 'value' => '%'.$search.'%'),
+ //array('column' => 'description', 'operator' => 'like', 'value' => '%'.$search.'%'),
)
);
@@ -72,6 +72,7 @@ class Project extends Base
'menu' => 'projects',
'project' => $project,
'columns' => $this->board->getColumnsList($project_id),
+ 'categories' => $this->category->getList($project['id'], false),
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
)));
}
@@ -105,6 +106,7 @@ class Project extends Base
'menu' => 'projects',
'project' => $project,
'columns' => $this->board->getColumnsList($project_id),
+ 'categories' => $this->category->getList($project['id'], false),
'tasks' => $tasks,
'nb_tasks' => $nb_tasks,
'title' => $project['name'].' ('.$nb_tasks.')'
diff --git a/controllers/task.php b/controllers/task.php
index f5738a55..bdedb9dd 100644
--- a/controllers/task.php
+++ b/controllers/task.php
@@ -34,6 +34,7 @@ class Task extends Base
'project_id' => $this->request->getIntegerParam('project_id', $defaultProject['id']),
'owner_id' => $this->request->getIntegerParam('owner_id'),
'column_id' => $this->request->getIntegerParam('column_id'),
+ 'category_id' => $this->request->getIntegerParam('category_id'),
);
if ($values['column_id'] == 0) {
@@ -121,6 +122,7 @@ class Task extends Base
'columns_list' => $this->board->getColumnsList($project_id),
'users_list' => $this->project->getUsersList($project_id),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($project_id),
'menu' => 'tasks',
'title' => t('New task')
)));
@@ -164,6 +166,7 @@ class Task extends Base
'columns_list' => $this->board->getColumnsList($values['project_id']),
'users_list' => $this->project->getUsersList($values['project_id']),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($values['project_id']),
'menu' => 'tasks',
'title' => t('New task')
)));
@@ -196,6 +199,7 @@ class Task extends Base
'columns_list' => $this->board->getColumnsList($task['project_id']),
'users_list' => $this->project->getUsersList($task['project_id']),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($task['project_id']),
'menu' => 'tasks',
'title' => t('Edit a task')
)));
@@ -230,6 +234,7 @@ class Task extends Base
'columns_list' => $this->board->getColumnsList($values['project_id']),
'users_list' => $this->project->getUsersList($values['project_id']),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($values['project_id']),
'menu' => 'tasks',
'title' => t('Edit a task')
)));
@@ -383,6 +388,7 @@ class Task extends Base
'columns_list' => $this->board->getColumnsList($task['project_id']),
'users_list' => $this->project->getUsersList($task['project_id']),
'colors_list' => $this->task->getColors(),
+ 'categories_list' => $this->category->getList($task['project_id']),
'duplicate' => true,
'menu' => 'tasks',
'title' => t('New task')
diff --git a/core/helper.php b/core/helper.php
index 7e65543a..8351328a 100644
--- a/core/helper.php
+++ b/core/helper.php
@@ -112,13 +112,13 @@ function contains($haystack, $needle)
return strpos($haystack, $needle) !== false;
}
-function in_list($id, array $listing)
+function in_list($id, array $listing, $default_value = '?')
{
if (isset($listing[$id])) {
return escape($listing[$id]);
}
- return '?';
+ return $default_value;
}
function error_class(array $errors, $name)
diff --git a/docs/automatic-actions.markdown b/docs/automatic-actions.markdown
index 6cbc0937..9526e9af 100644
--- a/docs/automatic-actions.markdown
+++ b/docs/automatic-actions.markdown
@@ -24,6 +24,7 @@ List of available events
- Task creation
- Open a closed task
- Closing a task
+- Task creation or modification
List of available actions
-------------------------
@@ -33,6 +34,7 @@ List of available actions
- Assign the task to the person who does the action
- Duplicate the task to another project
- Assign a color to a specific user
+- Assign a color to a specific category
Examples
--------
@@ -65,8 +67,14 @@ Let's say we have two projects "Customer orders" and "Production", once the orde
- Choose the action: **Duplicate the task to another project**
- Define the action parameters: **Column = Validated** and **Project = Production**
-### When I create a new task in the column "To do", assign a specific color to the user Bob
+### I want to assign a specific color to the user Bob
- Choose the event: **Task creation**
- Choose the action: **Assign a color to a specific user**
-- Define the action parameters: **Column = To do**, **Color = Green** and **Assignee = Bob**
+- Define the action parameters: **Color = Green** and **Assignee = Bob**
+
+### I want to assign a specific color to the category "Feature Request"
+
+- Choose the event: **Task creation or modification**
+- Choose the action: **Assign a color to a specific category**
+- Define the action parameters: **Color = Blue** and **Category = Feature Request**
diff --git a/locales/es_ES/translations.php b/locales/es_ES/translations.php
index e1d54c03..ac97d46c 100644
--- a/locales/es_ES/translations.php
+++ b/locales/es_ES/translations.php
@@ -309,4 +309,25 @@ return array(
// 'Unable to remove this task.' => '',
// 'Remove a task' => '',
// 'Do you really want to remove this task: "%s"?' => '',
+ // 'Assign a color to a specific category' => '',
+ // 'Task creation or modification' => '',
+ // 'Category' => '',
+ // 'Category:' => '',
+ // 'Categories' => '',
+ // 'Category not found.' => '',
+ // 'Your category have been created successfully.' => '',
+ // 'Unable to create your category.' => '',
+ // 'Your category have been updated successfully.' => '',
+ // 'Unable to update your category.' => '',
+ // 'Remove a category' => '',
+ // 'Category removed successfully.' => '',
+ // 'Unable to remove this category.' => '',
+ // 'Category modification for the project "%s"' => '',
+ // 'Category Name' => '',
+ // 'Categories for the project "%s"' => '',
+ // 'Add a new category' => '',
+ // 'Do you really want to remove this category: "%s"?' => '',
+ // 'Filter by category' => '',
+ // 'All categories' => '',
+ // 'No category' => '',
);
diff --git a/locales/fr_FR/translations.php b/locales/fr_FR/translations.php
index ac216c9b..46faf502 100644
--- a/locales/fr_FR/translations.php
+++ b/locales/fr_FR/translations.php
@@ -309,4 +309,25 @@ return array(
'Unable to remove this task.' => 'Impossible de supprimer cette tâche.',
'Remove a task' => 'Supprimer une tâche',
'Do you really want to remove this task: "%s"?' => 'Voulez-vous vraiment supprimer cette tâche « %s » ?',
+ 'Assign a color to a specific category' => 'Assigner une couleur à une catégorie spécifique',
+ 'Task creation or modification' => 'Création ou modification d\'une tâche',
+ 'Category' => 'Catégorie',
+ 'Category:' => 'Catégorie :',
+ 'Categories' => 'Catégories',
+ 'Category not found.' => 'Catégorie introuvable',
+ 'Your category have been created successfully.' => 'Votre catégorie a été créé avec succès.',
+ 'Unable to create your category.' => 'Impossible de créer votre catégorie.',
+ 'Your category have been updated successfully.' => 'Votre catégorie a été mise à jour avec succès.',
+ 'Unable to update your category.' => 'Impossible de mettre à jour votre catégorie.',
+ 'Remove a category' => 'Supprimer une catégorie',
+ 'Category removed successfully.' => 'Catégorie supprimée avec succès.',
+ 'Unable to remove this category.' => 'Impossible de supprimer cette catégorie.',
+ 'Category modification for the project "%s"' => 'Modification d\'une catégorie pour le projet « %s »',
+ 'Category Name' => 'Nom de la catégorie',
+ 'Categories for the project "%s"' => 'Catégories du projet « %s »',
+ 'Add a new category' => 'Ajouter une nouvelle catégorie',
+ 'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?',
+ 'Filter by category' => 'Filtrer par catégorie',
+ 'All categories' => 'Toutes les catégories',
+ 'No category' => 'Aucune catégorie',
);
diff --git a/locales/pl_PL/translations.php b/locales/pl_PL/translations.php
index a8feb807..94726c20 100644
--- a/locales/pl_PL/translations.php
+++ b/locales/pl_PL/translations.php
@@ -314,4 +314,25 @@ return array(
// 'Unable to remove this task.' => '',
// 'Remove a task' => '',
// 'Do you really want to remove this task: "%s"?' => '',
+ // 'Assign a color to a specific category' => '',
+ // 'Task creation or modification' => '',
+ // 'Category' => '',
+ // 'Category:' => '',
+ // 'Categories' => '',
+ // 'Category not found.' => '',
+ // 'Your category have been created successfully.' => '',
+ // 'Unable to create your category.' => '',
+ // 'Your category have been updated successfully.' => '',
+ // 'Unable to update your category.' => '',
+ // 'Remove a category' => '',
+ // 'Category removed successfully.' => '',
+ // 'Unable to remove this category.' => '',
+ // 'Category modification for the project "%s"' => '',
+ // 'Category Name' => '',
+ // 'Categories for the project "%s"' => '',
+ // 'Add a new category' => '',
+ // 'Do you really want to remove this category: "%s"?' => '',
+ // 'Filter by category' => '',
+ // 'All categories' => '',
+ // 'No category' => '',
);
diff --git a/locales/pt_BR/translations.php b/locales/pt_BR/translations.php
index ffb578c5..fc91b0b2 100644
--- a/locales/pt_BR/translations.php
+++ b/locales/pt_BR/translations.php
@@ -310,4 +310,25 @@ return array(
// 'Unable to remove this task.' => '',
// 'Remove a task' => '',
// 'Do you really want to remove this task: "%s"?' => '',
+ // 'Assign a color to a specific category' => '',
+ // 'Task creation or modification' => '',
+ // 'Category' => '',
+ // 'Category:' => '',
+ // 'Categories' => '',
+ // 'Category not found.' => '',
+ // 'Your category have been created successfully.' => '',
+ // 'Unable to create your category.' => '',
+ // 'Your category have been updated successfully.' => '',
+ // 'Unable to update your category.' => '',
+ // 'Remove a category' => '',
+ // 'Category removed successfully.' => '',
+ // 'Unable to remove this category.' => '',
+ // 'Category modification for the project "%s"' => '',
+ // 'Category Name' => '',
+ // 'Categories for the project "%s"' => '',
+ // 'Add a new category' => '',
+ // 'Do you really want to remove this category: "%s"?' => '',
+ // 'Filter by category' => '',
+ // 'All categories' => '',
+ // 'No category' => '',
);
diff --git a/models/action.php b/models/action.php
index a0236eff..c8cbf3b1 100644
--- a/models/action.php
+++ b/models/action.php
@@ -44,6 +44,7 @@ class Action extends Base
'TaskAssignCurrentUser' => t('Assign the task to the person who does the action'),
'TaskDuplicateAnotherProject' => t('Duplicate the task to another project'),
'TaskAssignColorUser' => t('Assign a color to a specific user'),
+ 'TaskAssignColorCategory' => t('Assign a color to a specific category'),
);
}
@@ -62,6 +63,7 @@ class Action extends Base
Task::EVENT_CREATE => t('Task creation'),
Task::EVENT_OPEN => t('Open a closed task'),
Task::EVENT_CLOSE => t('Closing a task'),
+ Task::EVENT_CREATE_UPDATE => t('Task creation or modification'),
);
}
@@ -239,6 +241,10 @@ class Action extends Base
require_once __DIR__.'/../actions/task_assign_color_user.php';
$className = '\Action\TaskAssignColorUser';
return new $className($project_id, new Task($this->db, $this->event));
+ case 'TaskAssignColorCategory':
+ require_once __DIR__.'/../actions/task_assign_color_category.php';
+ $className = '\Action\TaskAssignColorCategory';
+ return new $className($project_id, new Task($this->db, $this->event));
default:
throw new \LogicException('Action not found: '.$name);
}
diff --git a/models/category.php b/models/category.php
new file mode 100644
index 00000000..2a1891a5
--- /dev/null
+++ b/models/category.php
@@ -0,0 +1,152 @@
+<?php
+
+namespace Model;
+
+require_once __DIR__.'/base.php';
+
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+
+/**
+ * Category model
+ *
+ * @package model
+ * @author Frederic Guillot
+ */
+class Category extends Base
+{
+ /**
+ * SQL table name
+ *
+ * @var string
+ */
+ const TABLE = 'project_has_categories';
+
+ /**
+ * Get a category by the id
+ *
+ * @access public
+ * @param integer $category_id Category id
+ * @return array
+ */
+ public function getById($category_id)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $category_id)->findOne();
+ }
+
+ /**
+ * Return the list of all categories
+ *
+ * @access public
+ * @param integer $project_id Project id
+ * @param bool $prepend_none If true, prepend to the list the value 'None'
+ * @param bool $prepend_all If true, prepend to the list the value 'All'
+ * @return array
+ */
+ public function getList($project_id, $prepend_none = true, $prepend_all = false)
+ {
+ $listing = $this->db->table(self::TABLE)
+ ->eq('project_id', $project_id)
+ ->asc('name')
+ ->listing('id', 'name');
+
+ $prepend = array();
+
+ if ($prepend_all) {
+ $prepend[-1] = t('All categories');
+ }
+
+ if ($prepend_none) {
+ $prepend[0] = t('No category');
+ }
+
+ return $prepend + $listing;
+ }
+
+ /**
+ * Create a category
+ *
+ * @access public
+ * @param array $values Form values
+ * @return bool
+ */
+ public function create(array $values)
+ {
+ return $this->db->table(self::TABLE)->save($values);
+ }
+
+ /**
+ * Update a category
+ *
+ * @access public
+ * @param array $values Form values
+ * @return bool
+ */
+ public function update(array $values)
+ {
+ return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
+ }
+
+ /**
+ * Remove a category
+ *
+ * @access public
+ * @param integer $category_id Category id
+ * @return bool
+ */
+ public function remove($category_id)
+ {
+ $this->db->startTransaction();
+ $r1 = $this->db->table(Task::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0));
+ $r2 = $this->db->table(self::TABLE)->eq('id', $category_id)->remove();
+ $this->db->closeTransaction();
+
+ return $r1 && $r2;
+ }
+
+ /**
+ * Validate category creation
+ *
+ * @access public
+ * @param array $array Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateCreation(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('project_id', t('The project id is required')),
+ new Validators\Integer('project_id', t('The project id must be an integer')),
+ new Validators\Required('name', t('The name is required')),
+ new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+
+ /**
+ * Validate category modification
+ *
+ * @access public
+ * @param array $array Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ */
+ public function validateModification(array $values)
+ {
+ $v = new Validator($values, array(
+ new Validators\Required('id', t('The id is required')),
+ new Validators\Integer('id', t('The id must be an integer')),
+ new Validators\Required('project_id', t('The project id is required')),
+ new Validators\Integer('project_id', t('The project id must be an integer')),
+ new Validators\Required('name', t('The name is required')),
+ new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
+ ));
+
+ return array(
+ $v->execute(),
+ $v->getErrors()
+ );
+ }
+}
diff --git a/models/project.php b/models/project.php
index 680d0750..cbbbfda2 100644
--- a/models/project.php
+++ b/models/project.php
@@ -8,9 +8,9 @@ require_once __DIR__.'/board.php';
require_once __DIR__.'/task.php';
require_once __DIR__.'/../events/task_modification.php';
-use \SimpleValidator\Validator;
-use \SimpleValidator\Validators;
-use \Event\TaskModification;
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
+use Event\TaskModification;
/**
* Project model
diff --git a/models/task.php b/models/task.php
index f6258e86..0c62a9f4 100644
--- a/models/task.php
+++ b/models/task.php
@@ -4,8 +4,8 @@ namespace Model;
require_once __DIR__.'/base.php';
-use \SimpleValidator\Validator;
-use \SimpleValidator\Validators;
+use SimpleValidator\Validator;
+use SimpleValidator\Validators;
use DateTime;
/**
@@ -42,6 +42,7 @@ class Task extends Base
const EVENT_CREATE = 'task.create';
const EVENT_CLOSE = 'task.close';
const EVENT_OPEN = 'task.open';
+ const EVENT_CREATE_UPDATE = 'task.create_update';
/**
* Get available colors
@@ -90,10 +91,13 @@ class Task extends Base
self::TABLE.'.position',
self::TABLE.'.is_active',
self::TABLE.'.score',
+ self::TABLE.'.category_id',
+ Category::TABLE.'.name AS category_name',
Project::TABLE.'.name AS project_name',
Board::TABLE.'.title AS column_title',
User::TABLE.'.username'
)
+ ->join(Category::TABLE, 'id', 'category_id')
->join(Project::TABLE, 'id', 'project_id')
->join(Board::TABLE, 'id', 'column_id')
->join(User::TABLE, 'id', 'owner_id')
@@ -150,6 +154,7 @@ class Task extends Base
'tasks.position',
'tasks.is_active',
'tasks.score',
+ 'tasks.category_id',
'users.username'
)
->join('users', 'id', 'owner_id');
@@ -236,6 +241,7 @@ class Task extends Base
$this->db->closeTransaction();
// Trigger events
+ $this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $task);
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
return $task_id;
@@ -265,6 +271,7 @@ class Task extends Base
// Assign new values
$task['date_creation'] = time();
$task['owner_id'] = 0;
+ $task['category_id'] = 0;
$task['is_active'] = 1;
$task['column_id'] = $boardModel->getFirstColumn($project_id);
$task['project_id'] = $project_id;
@@ -281,6 +288,7 @@ class Task extends Base
$this->db->closeTransaction();
// Trigger events
+ $this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $task);
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
return $task_id;
@@ -320,6 +328,7 @@ class Task extends Base
$this->db->closeTransaction();
// Trigger events
+ $this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $values);
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $values);
return $task_id;
@@ -355,7 +364,8 @@ class Task extends Base
$events = array();
- if ($this->event->getLastTriggeredEvent() !== self::EVENT_UPDATE) {
+ if (! in_array($this->event->getLastTriggeredEvent(), array(self::EVENT_CREATE_UPDATE))) {
+ $events[] = self::EVENT_CREATE_UPDATE;
$events[] = self::EVENT_UPDATE;
}
diff --git a/schemas/mysql.php b/schemas/mysql.php
index 46f56e7b..eb869465 100644
--- a/schemas/mysql.php
+++ b/schemas/mysql.php
@@ -2,6 +2,22 @@
namespace Schema;
+function version_16($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_has_categories (
+ id INT NOT NULL AUTO_INCREMENT,
+ name VARCHAR(255),
+ project_id INT,
+ PRIMARY KEY (id),
+ UNIQUE KEY `idx_project_category` (project_id, name),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ ) ENGINE=InnoDB CHARSET=utf8"
+ );
+
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INT DEFAULT 0");
+}
+
function version_15($pdo)
{
$pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INT DEFAULT 0");
diff --git a/schemas/sqlite.php b/schemas/sqlite.php
index 94580235..b444faa5 100644
--- a/schemas/sqlite.php
+++ b/schemas/sqlite.php
@@ -2,6 +2,21 @@
namespace Schema;
+function version_16($pdo)
+{
+ $pdo->exec("
+ CREATE TABLE project_has_categories (
+ id INTEGER PRIMARY KEY,
+ name TEXT COLLATE NOCASE,
+ project_id INT,
+ UNIQUE (project_id, name),
+ FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
+ )"
+ );
+
+ $pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INTEGER DEFAULT 0");
+}
+
function version_15($pdo)
{
$pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INTEGER DEFAULT 0");
diff --git a/templates/action_index.php b/templates/action_index.php
index eccc0ec4..b515ccaa 100644
--- a/templates/action_index.php
+++ b/templates/action_index.php
@@ -36,6 +36,8 @@
<?= Helper\in_list($param['value'], $projects_list) ?>
<?php elseif (Helper\contains($param['name'], 'color_id')): ?>
<?= Helper\in_list($param['value'], $colors_list) ?>
+ <?php elseif (Helper\contains($param['name'], 'category_id')): ?>
+ <?= Helper\in_list($param['value'], $categories_list) ?>
<?php endif ?>
</strong>
</li>
diff --git a/templates/action_params.php b/templates/action_params.php
index ed9d8b8f..15a1d420 100644
--- a/templates/action_params.php
+++ b/templates/action_params.php
@@ -28,6 +28,9 @@
<?php elseif (Helper\contains($param_name, 'color_id')): ?>
<?= Helper\form_label($param_desc, $param_name) ?>
<?= Helper\form_select('params['.$param_name.']', $colors_list, $values) ?><br/>
+ <?php elseif (Helper\contains($param_name, 'category_id')): ?>
+ <?= Helper\form_label($param_desc, $param_name) ?>
+ <?= Helper\form_select('params['.$param_name.']', $categories_list, $values) ?><br/>
<?php endif ?>
<?php endforeach ?>
diff --git a/templates/board_index.php b/templates/board_index.php
index 523e3392..989c2e06 100644
--- a/templates/board_index.php
+++ b/templates/board_index.php
@@ -21,6 +21,10 @@
<?= t('Filter by user') ?>
<?= Helper\form_select('user_id', $users, $filters) ?>
</li>
+ <li>
+ <?= t('Filter by category') ?>
+ <?= Helper\form_select('category_id', $categories, $filters) ?>
+ </li>
<li><a href="#" id="filter-due-date"><?= t('Filter by due date') ?></a></li>
<li><a href="?controller=project&amp;action=search&amp;project_id=<?= $current_project_id ?>"><?= t('Search') ?></a></li>
<li><a href="?controller=project&amp;action=tasks&amp;project_id=<?= $current_project_id ?>"><?= t('Completed tasks') ?></a></li>
@@ -30,7 +34,7 @@
<?php if (empty($board)): ?>
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
<?php else: ?>
- <?= Helper\template('board_show', array('current_project_id' => $current_project_id, 'board' => $board)) ?>
+ <?= Helper\template('board_show', array('current_project_id' => $current_project_id, 'board' => $board, 'categories' => $categories)) ?>
<?php endif ?>
</section>
diff --git a/templates/board_public.php b/templates/board_public.php
index 2c2381ef..0808079e 100644
--- a/templates/board_public.php
+++ b/templates/board_public.php
@@ -39,7 +39,17 @@
<?= Helper\escape($task['title']) ?>
</div>
+ <?php if ($task['category_id']): ?>
+ <div class="task-category-container">
+ <span class="task-category">
+ <?= Helper\in_list($task['category_id'], $categories) ?>
+ </span>
+ </div>
+ <?php endif ?>
+
+ <?php if (! empty($task['date_due']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
<div class="task-footer">
+
<?php if (! empty($task['date_due'])): ?>
<div class="task-date">
<?= dt('%B %e, %G', $task['date_due']) ?>
@@ -56,6 +66,7 @@
<?php endif ?>
</div>
</div>
+ <?php endif ?>
</div>
<?php endforeach ?>
diff --git a/templates/board_show.php b/templates/board_show.php
index ad459399..719e3bdd 100644
--- a/templates/board_show.php
+++ b/templates/board_show.php
@@ -29,6 +29,7 @@
<div class="task draggable-item task-<?= $task['color_id'] ?>"
data-task-id="<?= $task['id'] ?>"
data-owner-id="<?= $task['owner_id'] ?>"
+ data-category-id="<?= $task['category_id'] ?>"
data-due-date="<?= $task['date_due'] ?>"
title="<?= t('View this task') ?>">
@@ -50,7 +51,17 @@
<a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
</div>
+ <?php if ($task['category_id']): ?>
+ <div class="task-category-container">
+ <span class="task-category">
+ <?= Helper\in_list($task['category_id'], $categories) ?>
+ </span>
+ </div>
+ <?php endif ?>
+
+ <?php if (! empty($task['date_due']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
<div class="task-footer">
+
<?php if (! empty($task['date_due'])): ?>
<div class="task-date">
<?= dt('%B %e, %G', $task['date_due']) ?>
@@ -67,6 +78,8 @@
<?php endif ?>
</div>
</div>
+ <?php endif ?>
+
</div>
<?php endforeach ?>
</td>
diff --git a/templates/category_edit.php b/templates/category_edit.php
new file mode 100644
index 00000000..99ba0c7c
--- /dev/null
+++ b/templates/category_edit.php
@@ -0,0 +1,24 @@
+<section id="main">
+ <div class="page-header">
+ <h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2>
+ <ul>
+ <li><a href="?controller=project"><?= t('All projects') ?></a></li>
+ </ul>
+ </div>
+ <section>
+
+ <form method="post" action="?controller=category&amp;action=update&amp;project_id=<?= $project['id'] ?>" autocomplete="off">
+
+ <?= Helper\form_hidden('id', $values) ?>
+ <?= Helper\form_hidden('project_id', $values) ?>
+
+ <?= Helper\form_label(t('Category Name'), 'name') ?>
+ <?= Helper\form_text('name', $values, $errors, array('required')) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+ </form>
+
+ </section>
+</section> \ No newline at end of file
diff --git a/templates/category_index.php b/templates/category_index.php
new file mode 100644
index 00000000..db986143
--- /dev/null
+++ b/templates/category_index.php
@@ -0,0 +1,48 @@
+<section id="main">
+ <div class="page-header">
+ <h2><?= t('Categories for the project "%s"', $project['name']) ?></h2>
+ <ul>
+ <li><a href="?controller=project"><?= t('All projects') ?></a></li>
+ </ul>
+ </div>
+ <section>
+
+ <?php if (! empty($categories)): ?>
+ <table>
+ <tr>
+ <th><?= t('Category Name') ?></th>
+ <th><?= t('Actions') ?></th>
+ </tr>
+ <?php foreach ($categories as $category_id => $category_name): ?>
+ <tr>
+ <td><?= Helper\escape($category_name) ?></td>
+ <td>
+ <ul>
+ <li>
+ <a href="?controller=category&amp;action=edit&amp;project_id=<?= $project['id'] ?>&amp;category_id=<?= $category_id ?>"><?= t('Edit') ?></a>
+ </li>
+ <li>
+ <a href="?controller=category&amp;action=confirm&amp;project_id=<?= $project['id'] ?>&amp;category_id=<?= $category_id ?>"><?= t('Remove') ?></a>
+ </li>
+ </ul>
+ </td>
+ </tr>
+ <?php endforeach ?>
+ </table>
+ <?php endif ?>
+
+ <h3><?= t('Add a new category') ?></h3>
+ <form method="post" action="?controller=category&amp;action=save&amp;project_id=<?= $project['id'] ?>" autocomplete="off">
+
+ <?= Helper\form_hidden('project_id', $values) ?>
+
+ <?= Helper\form_label(t('Category Name'), 'name') ?>
+ <?= Helper\form_text('name', $values, $errors, array('required')) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+ </form>
+
+ </section>
+</section> \ No newline at end of file
diff --git a/templates/category_remove.php b/templates/category_remove.php
new file mode 100644
index 00000000..cc2eb678
--- /dev/null
+++ b/templates/category_remove.php
@@ -0,0 +1,16 @@
+<section id="main">
+ <div class="page-header">
+ <h2><?= t('Remove a category') ?></h2>
+ </div>
+
+ <div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this category: "%s"?', $category['name']) ?>
+ </p>
+
+ <div class="form-actions">
+ <a href="?controller=category&amp;action=remove&amp;project_id=<?= $project['id'] ?>&amp;category_id=<?= $category['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
+ <?= t('or') ?> <a href="?controller=category&amp;project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
+ </div>
+ </div>
+</section> \ No newline at end of file
diff --git a/templates/project_index.php b/templates/project_index.php
index c7c7d226..df153fe7 100644
--- a/templates/project_index.php
+++ b/templates/project_index.php
@@ -60,6 +60,9 @@
<td>
<ul>
<li>
+ <a href="?controller=category&amp;action=index&amp;project_id=<?= $project['id'] ?>"><?= t('Categories') ?></a>
+ </li>
+ <li>
<a href="?controller=project&amp;action=edit&amp;project_id=<?= $project['id'] ?>"><?= t('Edit project') ?></a>
</li>
<li>
diff --git a/templates/project_search.php b/templates/project_search.php
index ee1e5025..3594fd09 100644
--- a/templates/project_search.php
+++ b/templates/project_search.php
@@ -28,6 +28,7 @@
<tr>
<th><?= t('Id') ?></th>
<th><?= t('Column') ?></th>
+ <th><?= t('Category') ?></th>
<th><?= t('Title') ?></th>
<th><?= t('Assignee') ?></th>
<th><?= t('Due date') ?></th>
@@ -44,6 +45,9 @@
<?= Helper\in_list($task['column_id'], $columns) ?>
</td>
<td>
+ <?= Helper\in_list($task['category_id'], $categories, '') ?>
+ </td>
+ <td>
<a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
<div class="task-table-icons">
<?php if (! empty($task['nb_comments'])): ?>
diff --git a/templates/project_tasks.php b/templates/project_tasks.php
index 2c152497..9f4263b8 100644
--- a/templates/project_tasks.php
+++ b/templates/project_tasks.php
@@ -15,6 +15,7 @@
<tr>
<th><?= t('Id') ?></th>
<th><?= t('Column') ?></th>
+ <th><?= t('Category') ?></th>
<th><?= t('Title') ?></th>
<th><?= t('Assignee') ?></th>
<th><?= t('Due date') ?></th>
@@ -30,6 +31,9 @@
<?= Helper\in_list($task['column_id'], $columns) ?>
</td>
<td>
+ <?= Helper\in_list($task['category_id'], $categories, '') ?>
+ </td>
+ <td>
<a href="?controller=task&amp;action=show&amp;task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
<div class="task-table-icons">
<?php if (! empty($task['nb_comments'])): ?>
diff --git a/templates/task_edit.php b/templates/task_edit.php
index 0c19db38..8c8bc107 100644
--- a/templates/task_edit.php
+++ b/templates/task_edit.php
@@ -24,6 +24,9 @@
<?= Helper\form_label(t('Assignee'), 'owner_id') ?>
<?= Helper\form_select('owner_id', $users_list, $values, $errors) ?><br/>
+ <?= Helper\form_label(t('Category'), 'category_id') ?>
+ <?= Helper\form_select('category_id', $categories_list, $values, $errors) ?><br/>
+
<?= Helper\form_label(t('Column'), 'column_id') ?>
<?= Helper\form_select('column_id', $columns_list, $values, $errors) ?><br/>
diff --git a/templates/task_new.php b/templates/task_new.php
index cf320e72..d233efd2 100644
--- a/templates/task_new.php
+++ b/templates/task_new.php
@@ -25,6 +25,9 @@
<?= Helper\form_label(t('Assignee'), 'owner_id') ?>
<?= Helper\form_select('owner_id', $users_list, $values, $errors) ?><br/>
+ <?= Helper\form_label(t('Category'), 'category_id') ?>
+ <?= Helper\form_select('category_id', $categories_list, $values, $errors) ?><br/>
+
<?= Helper\form_label(t('Column'), 'column_id') ?>
<?= Helper\form_select('column_id', $columns_list, $values, $errors) ?><br/>
diff --git a/templates/task_show.php b/templates/task_show.php
index f0cea410..a5b79359 100644
--- a/templates/task_show.php
+++ b/templates/task_show.php
@@ -31,6 +31,11 @@
<strong><?= Helper\escape($task['column_title']) ?></strong>
(<?= Helper\escape($task['project_name']) ?>)
</li>
+ <?php if ($task['category_name']): ?>
+ <li>
+ <?= t('Category:') ?> <strong><?= Helper\escape($task['category_name']) ?></strong>
+ </li>
+ <?php endif ?>
<li>
<?php if ($task['is_active'] == 1): ?>
<?= t('Status is open') ?>
diff --git a/tests/Base.php b/tests/Base.php
index 4a0fe044..400643ac 100644
--- a/tests/Base.php
+++ b/tests/Base.php
@@ -36,7 +36,7 @@ abstract class Base extends PHPUnit_Framework_TestCase
'filename' => ':memory:'
));
- if ($db->schema()->check(10)) {
+ if ($db->schema()->check(16)) {
return $db;
}
else {
diff --git a/tests/TaskTest.php b/tests/TaskTest.php
index 594d0e13..2f645131 100644
--- a/tests/TaskTest.php
+++ b/tests/TaskTest.php
@@ -17,6 +17,7 @@ class TaskTest extends Base
$this->assertEquals(2, $t->create(array('title' => 'test b', 'project_id' => 1, 'column_id' => 2, 'owner_id' => 2, 'description' => 'toto et titi sont dans un bateau')));
$tasks = $t->find(array(array('column' => 'project_id', 'operator' => 'eq', 'value' => '1')));
+ $this->assertNotFalse($tasks);
$this->assertEquals(2, count($tasks));
$this->assertEquals(1, $tasks[0]['id']);
$this->assertEquals(2, $tasks[1]['id']);
@@ -101,7 +102,7 @@ class TaskTest extends Base
// We create a task and a project
$this->assertEquals(1, $p->create(array('name' => 'test1')));
- $this->assertEquals(1, $t->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1)));
+ $this->assertEquals(1, $t->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 3, 'owner_id' => 1, 'category_id' => 2)));
$task = $t->getById(1);
$this->assertNotEmpty($task);
@@ -118,6 +119,7 @@ class TaskTest extends Base
$this->assertEquals(1, $task['project_id']);
$this->assertEquals(1, $task['owner_id']);
$this->assertEquals(1, $task['position']);
+ $this->assertEquals(2, $task['category_id']);
}
public function testDuplicateToAnotherProject()
@@ -130,11 +132,19 @@ class TaskTest extends Base
$this->assertEquals(2, $p->create(array('name' => 'test2')));
// We create a task
- $this->assertEquals(1, $t->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 1)));
+ $this->assertEquals(1, $t->create(array('title' => 'test', 'project_id' => 1, 'column_id' => 1, 'owner_id' => 1, 'category_id' => 1)));
// We duplicate our task to the 2nd project
$this->assertEquals(2, $t->duplicateToAnotherProject(1, 2));
$this->assertEquals(Task::EVENT_CREATE, $this->event->getLastTriggeredEvent());
+
+ // Check the values of the duplicated task
+ $task = $t->getById(2);
+ $this->assertNotEmpty($task);
+ $this->assertEquals(0, $task['owner_id']);
+ $this->assertEquals(0, $task['category_id']);
+ $this->assertEquals(2, $task['project_id']);
+ $this->assertEquals('test', $task['title']);
}
public function testEvents()