summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Controller/App.php29
-rw-r--r--app/Controller/Base.php4
-rw-r--r--app/Controller/Board.php2
-rw-r--r--app/Controller/Calendar.php4
-rw-r--r--app/Controller/Config.php7
-rw-r--r--app/Controller/Link.php91
-rw-r--r--app/Controller/Task.php6
-rw-r--r--app/Controller/Tasklink.php74
-rw-r--r--app/Core/Helper.php4
-rw-r--r--app/Locale/da_DK/translations.php33
-rw-r--r--app/Locale/de_DE/translations.php33
-rw-r--r--app/Locale/es_ES/translations.php33
-rw-r--r--app/Locale/fi_FI/translations.php33
-rw-r--r--app/Locale/fr_FR/translations.php65
-rw-r--r--app/Locale/hu_HU/translations.php33
-rw-r--r--app/Locale/it_IT/translations.php33
-rw-r--r--app/Locale/ja_JP/translations.php33
-rw-r--r--app/Locale/pl_PL/translations.php33
-rw-r--r--app/Locale/pt_BR/translations.php33
-rw-r--r--app/Locale/ru_RU/translations.php33
-rw-r--r--app/Locale/sv_SE/translations.php33
-rw-r--r--app/Locale/th_TH/translations.php33
-rw-r--r--app/Locale/zh_CN/translations.php33
-rw-r--r--app/Model/Acl.php2
-rw-r--r--app/Model/Link.php427
-rw-r--r--app/Model/ProjectPermission.php17
-rw-r--r--app/Model/TaskDuplication.php1
-rw-r--r--app/Model/TaskFilter.php32
-rw-r--r--app/Model/TaskFinder.php24
-rw-r--r--app/Model/TaskLink.php355
-rw-r--r--app/Schema/Mysql.php83
-rw-r--r--app/Schema/Postgres.php76
-rw-r--r--app/Schema/Sqlite.php78
-rw-r--r--app/Template/board/tasklinks.php41
-rw-r--r--app/Template/config/sidebar.php2
-rw-r--r--app/Template/link/create.php18
-rw-r--r--app/Template/link/edit.php48
-rw-r--r--app/Template/link/index.php21
-rw-r--r--app/Template/link/remove.php26
-rw-r--r--app/Template/task/public.php12
-rw-r--r--app/Template/task/show.php2
-rw-r--r--app/Template/tasklink/create.php27
-rw-r--r--app/Template/tasklink/edit.php53
-rw-r--r--app/Template/tasklink/remove.php6
-rw-r--r--app/Template/tasklink/show.php93
45 files changed, 998 insertions, 1161 deletions
diff --git a/app/Controller/App.php b/app/Controller/App.php
index ef0a08a9..46731e7c 100644
--- a/app/Controller/App.php
+++ b/app/Controller/App.php
@@ -2,7 +2,8 @@
namespace Controller;
-use Model\Subtask as SubTaskModel;
+use Model\Subtask as SubtaskModel;
+use Model\Task as TaskModel;
/**
* Application controller
@@ -39,7 +40,7 @@ class App extends Base
*/
public function index($user_id = 0, $action = 'index')
{
- $status = array(SubTaskModel::STATUS_TODO, SubTaskModel::STATUS_INPROGRESS);
+ $status = array(SubTaskModel::STATUS_TODO, SubtaskModel::STATUS_INPROGRESS);
$user_id = $user_id ?: $this->userSession->getId();
$projects = $this->projectPermission->getActiveMemberProjects($user_id);
$project_ids = array_keys($projects);
@@ -88,11 +89,8 @@ class App extends Base
if (empty($payload['text'])) {
$this->response->html('<p>'.t('Nothing to preview...').'</p>');
}
- else {
- $this->response->html(
- $this->template->markdown($payload['text'])
- );
- }
+
+ $this->response->html($this->template->markdown($payload['text']));
}
/**
@@ -104,4 +102,21 @@ class App extends Base
{
$this->response->css($this->color->getCss());
}
+
+ /**
+ * Task autocompletion (Ajax)
+ *
+ * @access public
+ */
+ public function autocomplete()
+ {
+ $this->response->json(
+ $this->taskFilter
+ ->create()
+ ->filterByProjects($this->projectPermission->getActiveMemberProjectIds($this->userSession->getId()))
+ ->excludeTasks(array($this->request->getIntegerParam('exclude_task_id')))
+ ->filterByTitle($this->request->getStringParam('term'))
+ ->toAutoCompletion()
+ );
+ }
}
diff --git a/app/Controller/Base.php b/app/Controller/Base.php
index 2c8b5cde..76a81612 100644
--- a/app/Controller/Base.php
+++ b/app/Controller/Base.php
@@ -141,7 +141,7 @@ abstract class Base
private function sendHeaders($action)
{
// HTTP secure headers
- $this->response->csp(array('style-src' => "'self' 'unsafe-inline'"));
+ $this->response->csp(array('style-src' => "'self' 'unsafe-inline'", 'img-src' => "'self' data:"));
$this->response->nosniff();
$this->response->xss();
@@ -201,7 +201,7 @@ abstract class Base
{
$project_id = $this->request->getIntegerParam('project_id');
$task_id = $this->request->getIntegerParam('task_id');
-
+
// Allow urls without "project_id"
if ($task_id > 0 && $project_id === 0) {
$project_id = $this->taskFinder->getProjectId($task_id);
diff --git a/app/Controller/Board.php b/app/Controller/Board.php
index ae249982..90b7f357 100644
--- a/app/Controller/Board.php
+++ b/app/Controller/Board.php
@@ -410,7 +410,7 @@ class Board extends Base
{
$task = $this->getTask();
$this->response->html($this->template->render('board/tasklinks', array(
- 'links' => $this->taskLink->getAll($task['id']),
+ 'links' => $this->taskLink->getLinks($task['id']),
'task' => $task,
)));
}
diff --git a/app/Controller/Calendar.php b/app/Controller/Calendar.php
index abbcab7f..0e749558 100644
--- a/app/Controller/Calendar.php
+++ b/app/Controller/Calendar.php
@@ -2,7 +2,7 @@
namespace Controller;
-use Model\Task;
+use Model\Task as TaskModel;
/**
* Project Calendar controller
@@ -74,7 +74,7 @@ class Calendar extends Base
$this->taskFilter
->create()
->filterByOwner($user_id)
- ->filterByStatus(Task::STATUS_OPEN)
+ ->filterByStatus(TaskModel::STATUS_OPEN)
->filterByDueDateRange(
$this->request->getStringParam('start'),
$this->request->getStringParam('end')
diff --git a/app/Controller/Config.php b/app/Controller/Config.php
index 6ec10279..01c7ad53 100644
--- a/app/Controller/Config.php
+++ b/app/Controller/Config.php
@@ -87,17 +87,12 @@ class Config extends Base
*
* @access public
*/
- public function board(array $values = array(), array $errors = array())
+ public function board()
{
$this->common('board');
$this->response->html($this->layout('config/board', array(
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
- 'links' => $this->link->getMergedList(),
- 'values' => $values + array(
- 'project_id' => -1
- ),
- 'errors' => $errors,
'title' => t('Settings').' &gt; '.t('Board settings'),
)));
}
diff --git a/app/Controller/Link.php b/app/Controller/Link.php
index fca9017a..ec9c6195 100644
--- a/app/Controller/Link.php
+++ b/app/Controller/Link.php
@@ -1,11 +1,13 @@
<?php
+
namespace Controller;
/**
* Link controller
*
* @package controller
- * @author Olivier Maridat
+ * @author Olivier Maridat
+ * @author Frederic Guillot
*/
class Link extends Base
{
@@ -21,13 +23,10 @@ class Link extends Base
{
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->userSession->getId());
$params['config_content_for_layout'] = $this->template->render($template, $params);
-
- if (isset($params['values']['project_id']) && -1 != $params['values']['project_id']) {
- return $this->projectLayout($template, $params);
- }
+
return $this->template->layout('config/layout', $params);
}
-
+
/**
* Get the current link
*
@@ -36,49 +35,27 @@ class Link extends Base
*/
private function getLink()
{
- $link = $this->link->getById($this->request->getIntegerParam('link_id'), $this->request->getIntegerParam('project_id', -1));
+ $link = $this->link->getById($this->request->getIntegerParam('link_id'));
+
if (! $link) {
$this->notfound();
}
- $link['link_id'] = $link[0]['link_id'];
- $link['project_id'] = $link[0]['project_id'];
- return $link;
- }
- /**
- * Method to get a project
- *
- * @access protected
- * @param integer $project_id Default project id
- * @return array
- */
- protected function getProject($project_id = -1)
- {
- $project = array('id' => $project_id);
- $project_id = $this->request->getIntegerParam('project_id', $project_id);
- if (-1 != $project_id) {
- $project = parent::getProject($project_id);
- }
- return $project;
+ return $link;
}
/**
- * List of links for a given project
+ * List of links
*
* @access public
*/
public function index(array $values = array(), array $errors = array())
{
- $project = $this->getProject();
- $values['project_id'] = $project['id'];
- $values[] = array();
-
$this->response->html($this->layout('link/index', array(
- 'links' => $this->link->getMergedList($project['id']),
+ 'links' => $this->link->getMergedList(),
'values' => $values,
'errors' => $errors,
- 'project' => $project,
- 'title' => t('Settings').' &gt; '.t('Board\'s links settings'),
+ 'title' => t('Settings').' &gt; '.t('Task\'s links'),
)));
}
@@ -91,19 +68,18 @@ class Link extends Base
{
$values = $this->request->getValues();
list($valid, $errors) = $this->link->validateCreation($values);
-
+
if ($valid) {
- if ($this->link->create($values)) {
+
+ if ($this->link->create($values['label'], $values['opposite_label'])) {
$this->session->flash(t('Link added successfully.'));
- $this->response->redirect('?controller=link&action=index&project_id='.$values['project_id']);
+ $this->response->redirect($this->helper->url('link', 'index'));
}
else {
$this->session->flashError(t('Unable to create your link.'));
}
}
- if (!empty($values)) {
- $this->link->prepare($values);
- }
+
$this->index($values, $errors);
}
@@ -114,14 +90,15 @@ class Link extends Base
*/
public function edit(array $values = array(), array $errors = array())
{
- $project = $this->getProject();
-
+ $link = $this->getLink();
+ $link['label'] = t($link['label']);
+
$this->response->html($this->layout('link/edit', array(
- 'values' => empty($values) ? $this->getLink() : $values,
+ 'values' => $values ?: $link,
'errors' => $errors,
- 'project' => $project,
- 'edit' => true,
- 'title' => t('Links')
+ 'labels' => $this->link->getList($link['id']),
+ 'link' => $link,
+ 'title' => t('Link modification')
)));
}
@@ -134,19 +111,17 @@ class Link extends Base
{
$values = $this->request->getValues();
list($valid, $errors) = $this->link->validateModification($values);
-
+
if ($valid) {
if ($this->link->update($values)) {
$this->session->flash(t('Link updated successfully.'));
- $this->response->redirect('?controller=link&action=index&project_id='.$values['project_id']);
+ $this->response->redirect($this->helper->url('link', 'index'));
}
- else {
+ else {
$this->session->flashError(t('Unable to update your link.'));
}
}
- if (!empty($values)) {
- $this->link->prepare($values);
- }
+
$this->edit($values, $errors);
}
@@ -157,11 +132,9 @@ class Link extends Base
*/
public function confirm()
{
- $project = $this->getProject();
$link = $this->getLink();
-
+
$this->response->html($this->layout('link/remove', array(
- 'project' => $project,
'link' => $link,
'title' => t('Remove a link')
)));
@@ -176,14 +149,14 @@ class Link extends Base
{
$this->checkCSRFParam();
$link = $this->getLink();
-
- if ($this->link->remove($link['link_id'])) {
+
+ if ($this->link->remove($link['id'])) {
$this->session->flash(t('Link removed successfully.'));
- $this->response->redirect('?controller=link&action=index&project_id='.$link['project_id']);
}
else {
$this->session->flashError(t('Unable to remove this link.'));
}
- $this->confirm();
+
+ $this->response->redirect($this->helper->url('link', 'index'));
}
}
diff --git a/app/Controller/Task.php b/app/Controller/Task.php
index e4c2d773..0789e8eb 100644
--- a/app/Controller/Task.php
+++ b/app/Controller/Task.php
@@ -37,7 +37,7 @@ class Task extends Base
'project' => $project,
'comments' => $this->comment->getAll($task['id']),
'subtasks' => $this->subtask->getAll($task['id']),
- 'links' => $this->taskLink->getAll($task['id']),
+ 'links' => $this->taskLink->getLinks($task['id']),
'task' => $task,
'columns_list' => $this->board->getColumnsList($task['project_id']),
'colors_list' => $this->color->getList(),
@@ -72,13 +72,11 @@ class Task extends Base
'files' => $this->file->getAll($task['id']),
'comments' => $this->comment->getAll($task['id']),
'subtasks' => $subtasks,
- 'links' => $this->taskLink->getAll($task['id']),
+ 'links' => $this->taskLink->getLinks($task['id']),
'task' => $task,
'values' => $values,
'columns_list' => $this->board->getColumnsList($task['project_id']),
'colors_list' => $this->color->getList(),
- 'link_list' => $this->link->getLinkLabelList($task['project_id'], false),
- 'task_list' => $this->taskFinder->getList($task['project_id'], TaskModel::STATUS_OPEN, $task['id']),
'date_format' => $this->config->get('application_date_format'),
'date_formats' => $this->dateParser->getAvailableFormats(),
'title' => $task['project_name'].' &gt; '.$task['title'],
diff --git a/app/Controller/Tasklink.php b/app/Controller/Tasklink.php
index d76de8fe..61b7fab8 100644
--- a/app/Controller/Tasklink.php
+++ b/app/Controller/Tasklink.php
@@ -2,12 +2,12 @@
namespace Controller;
-use Model\Task AS TaskModel;
/**
* TaskLink controller
*
* @package controller
* @author Olivier Maridat
+ * @author Frederic Guillot
*/
class Tasklink extends Base
{
@@ -20,9 +20,11 @@ class Tasklink extends Base
private function getTaskLink()
{
$link = $this->taskLink->getById($this->request->getIntegerParam('link_id'));
+
if (! $link) {
$this->notfound();
}
+
return $link;
}
@@ -38,16 +40,15 @@ class Tasklink extends Base
if (empty($values)) {
$values = array(
'task_id' => $task['id'],
- 'another_link' => $this->request->getIntegerParam('another_link', 0)
);
}
- $this->response->html($this->taskLayout('tasklink/edit', array(
+ $this->response->html($this->taskLayout('tasklink/create', array(
'values' => $values,
'errors' => $errors,
- 'link_list' => $this->link->getLinkLabelList($task['project_id']),
- 'task_list' => $this->taskFinder->getList($task['project_id'], TaskModel::STATUS_OPEN, $task['id']),
'task' => $task,
+ 'labels' => $this->link->getList(0, false),
+ 'title' => t('Add a new link')
)));
}
@@ -60,19 +61,17 @@ class Tasklink extends Base
{
$task = $this->getTask();
$values = $this->request->getValues();
+
list($valid, $errors) = $this->taskLink->validateCreation($values);
if ($valid) {
- if ($this->taskLink->create($values)) {
+
+ if ($this->taskLink->create($values['task_id'], $values['opposite_task_id'], $values['link_id'])) {
$this->session->flash(t('Link added successfully.'));
- if (isset($values['another_link']) && $values['another_link'] == 1) {
- $this->response->redirect('?controller=tasklink&action=create&task_id='.$task['id'].'&project_id='.$task['project_id'].'&another_link=1');
- }
-
- $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#links');
+ $this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])).'#links');
}
else {
- $this->session->flashError(t('Unable to add the link.'));
+ $this->session->flashError(t('Unable to create your link.'));
}
}
@@ -80,50 +79,6 @@ class Tasklink extends Base
}
/**
- * Edit form
- *
- * @access public
- */
- public function edit(array $values = array(), array $errors = array())
- {
- $task = $this->getTask();
- $taskLink = $this->getTaskLink();
-
- $this->response->html($this->taskLayout('tasklink/edit', array(
- 'values' => empty($values) ? $taskLink : $values,
- 'errors' => $errors,
- 'link_list' => $this->link->getLinkLabelList($task['project_id'], false),
- 'task_list' => $this->taskFinder->getList($task['project_id'], TaskModel::STATUS_OPEN, $task['id']),
- 'link' => $taskLink,
- 'task' => $task,
- 'edit' => true,
- )));
- }
-
- /**
- * Update and validate a link
- *
- * @access public
- */
- public function update()
- {
- $task = $this->getTask();
- $values = $this->request->getValues();
- list($valid, $errors) = $this->taskLink->validateModification($values);
-
- if ($valid) {
- if ($this->taskLink->update($values)) {
- $this->session->flash(t('Link updated successfully.'));
- $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#links');
- }
- else {
- $this->session->flashError(t('Unable to update the link.'));
- }
- }
- $this->edit($values, $errors);
- }
-
- /**
* Confirmation dialog before removing a link
*
* @access public
@@ -132,6 +87,7 @@ class Tasklink extends Base
{
$task = $this->getTask();
$link = $this->getTaskLink();
+
$this->response->html($this->taskLayout('tasklink/remove', array(
'link' => $link,
'task' => $task,
@@ -147,14 +103,14 @@ class Tasklink extends Base
{
$this->checkCSRFParam();
$task = $this->getTask();
-
+
if ($this->taskLink->remove($this->request->getIntegerParam('link_id'))) {
$this->session->flash(t('Link removed successfully.'));
- $this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'&project_id='.$task['project_id'].'#links');
}
else {
$this->session->flashError(t('Unable to remove this link.'));
}
- $this->confirm();
+
+ $this->response->redirect($this->helper->url('task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])));
}
}
diff --git a/app/Core/Helper.php b/app/Core/Helper.php
index a9d455f9..d60e29d4 100644
--- a/app/Core/Helper.php
+++ b/app/Core/Helper.php
@@ -155,9 +155,6 @@ class Helper
*/
public function formValue($values, $name)
{
- if (false !== ($pos = strpos($name, '['))) {
- $name = substr($name, 0, $pos);
- }
if (isset($values->$name)) {
return 'value="'.$this->e($values->$name).'"';
}
@@ -584,7 +581,6 @@ class Helper
{
return strpos($haystack, $needle) !== false;
}
-
/**
* Return a value from a dictionary
diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php
index d0bebe0c..db91895f 100644
--- a/app/Locale/da_DK/translations.php
+++ b/app/Locale/da_DK/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php
index 42db9bcd..967d5f62 100644
--- a/app/Locale/de_DE/translations.php
+++ b/app/Locale/de_DE/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php
index e921c10f..c69d9084 100644
--- a/app/Locale/es_ES/translations.php
+++ b/app/Locale/es_ES/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php
index 0ed1031b..0dd29c37 100644
--- a/app/Locale/fi_FI/translations.php
+++ b/app/Locale/fi_FI/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php
index ec09348d..7872dec4 100644
--- a/app/Locale/fr_FR/translations.php
+++ b/app/Locale/fr_FR/translations.php
@@ -688,52 +688,37 @@ return array(
'Task age in days' => 'Age de la tâche en jours',
'Days in this column' => 'Jours dans cette colonne',
'%dd' => '%dj',
- 'relates to' => 'liée à',
+ 'Add a link' => 'Ajouter un lien',
+ 'Add a new link' => 'Ajouter un nouveau lien',
+ 'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien : « %s » ?',
+ 'Do you really want to remove this link with task #%d?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche n°%s ?',
+ 'Field required' => 'Champ obligatoire',
+ 'Link added successfully.' => 'Lien créé avec succès.',
+ 'Link updated successfully.' => 'Lien mis à jour avec succès.',
+ 'Link removed successfully.' => 'Lien supprimé avec succès.',
+ 'Link labels' => 'Libellé des liens',
+ 'Link modification' => 'Modification d\'un lien',
+ 'Links' => 'Liens',
+ 'Link settings' => 'Paramètres des liens',
+ 'Opposite label' => 'Nom du libellé opposé',
+ 'Remove a link' => 'Supprimer un lien',
+ 'Task\'s links' => 'Liens des tâches',
+ 'The labels must be different' => 'Les libellés doivent être différents',
+ 'There is no link.' => 'Il n\'y a aucun lien.',
+ 'This label must be unique' => 'Ce libellé doit être unique',
+ 'Unable to create your link.' => 'Impossible d\'ajouter ce lien.',
+ 'Unable to update your link.' => 'Impossible de mettre à jour ce lien.',
+ 'Unable to remove this link.' => 'Impossible de supprimer ce lien.',
+ 'relates to' => 'est liée à',
'blocks' => 'bloque',
'is blocked by' => 'est bloquée par',
'duplicates' => 'duplique',
'is duplicated by' => 'est dupliquée par',
'is a child of' => 'est un enfant de',
'is a parent of' => 'est un parent de',
- 'targets milestone' => 'vise la milestone',
- 'is a milestone o' => 'est une milestone de',
+ 'targets milestone' => 'vise l\'étape importante',
+ 'is a milestone of' => 'est une étape importante de',
'fixes' => 'corrige',
'is fixed by' => 'est corrigée par',
- 'Links' => 'Liens',
- 'Add a link' => 'Ajouter un lien',
- 'Edit a link' => 'Mettre à jour un lien',
- 'Remove a link' => 'Supprimer un lien',
- 'Create another link' => 'Ajouter un autre lien',
- 'Linked task id' => 'Id de la tâche à lier',
- 'The exact same link already exists' => 'Un tel lien existe déjà',
- 'This linked task id doesn\'t exist' => 'Cette tâche n\'existe pas',
- 'A task can not be linked to itself' => 'Une tâche ne peut être liée à elle-même',
- 'Link added successfully.' => 'Lien créé avec succès.',
- 'Link updated successfully.' => 'Lien mis à jour avec succès.',
- 'Link removed successfully.' => 'Lien supprimé avec succès.',
- 'Unable to add the link.' => 'Impossible d\'ajouter ce lien.',
- 'Unable to update the link.' => 'Impossible de mettre à jour ce lien.',
- 'Unable to remove this link.' => 'Impossible de supprimer ce lien.',
- 'Do you really want to remove this link with task #%s?' => 'Voulez-vous vraiment supprimer ce lien avec la tâche #%s ?',
- 'The link type is required' => 'Le type du lien est obligatoire',
- 'The linked task id is required' => 'L\'id de la tâche à lier est obligatoire',
- 'The link id must be an integer' => 'L\'id du lien doit être un entier',
- 'The related task id must be an integer' => 'L\'id de la tâche à lier doit être un entier',
- 'Link management' => 'Gestion des liens',
- 'Add a new link' => 'Ajouter un nouveau lien',
- 'Add a new link label' => 'Ajouter un nouveau libellé de lien',
- 'Link modification for the project "%s"' => 'Mettre à jour un lien du projet « %s »',
- 'Links settings' => 'Paramètres des liens',
- 'Board\'s links settings' => 'Paramètres des liens du tableau',
- 'Link labels' => 'Libellés des liens',
- 'Link Label' => 'Libellé du lien',
- 'Link Inverse Label' => 'Libellé du lien inverse',
- 'Example:' => 'Exemple :',
- 'precedes' => 'précède',
- 'follows' => 'suit',
- '#9 precedes #10' => '#9 précède #10',
- '#10 follows #9' => '#10 suit #9',
- 'and therefore' => 'et donc',
- 'Do you really want to remove this link: "%s"?' => 'Voulez-vous vraiment supprimer ce lien: « %s » ?',
- 'You need to add link labels to this project before to link this task to another one.' => 'Il faut ajouter des libellés de liens à ce project avant de pouvoir lier une tâche à une autre.',
+ 'This task' => 'Cette tâche',
);
diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php
index 3ca6cf28..1f8de0ea 100644
--- a/app/Locale/hu_HU/translations.php
+++ b/app/Locale/hu_HU/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php
index 5c3ef47b..afd41d5c 100644
--- a/app/Locale/it_IT/translations.php
+++ b/app/Locale/it_IT/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php
index 91030bee..9d06b83f 100644
--- a/app/Locale/ja_JP/translations.php
+++ b/app/Locale/ja_JP/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php
index 7acb1497..2ebfc219 100644
--- a/app/Locale/pl_PL/translations.php
+++ b/app/Locale/pl_PL/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php
index ac865046..ded6e0db 100644
--- a/app/Locale/pt_BR/translations.php
+++ b/app/Locale/pt_BR/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php
index 624a17f1..de792516 100644
--- a/app/Locale/ru_RU/translations.php
+++ b/app/Locale/ru_RU/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php
index b77a8a5a..838b426a 100644
--- a/app/Locale/sv_SE/translations.php
+++ b/app/Locale/sv_SE/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php
index 64e0170e..66c7712b 100644
--- a/app/Locale/th_TH/translations.php
+++ b/app/Locale/th_TH/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php
index addf7fa6..32278e21 100644
--- a/app/Locale/zh_CN/translations.php
+++ b/app/Locale/zh_CN/translations.php
@@ -686,4 +686,37 @@ return array(
// 'Task age in days' => '',
// 'Days in this column' => '',
// '%dd' => '',
+ // 'Add a link' => '',
+ // 'Add a new link' => '',
+ // 'Do you really want to remove this link: "%s"?' => '',
+ // 'Do you really want to remove this link with task #%d?' => '',
+ // 'Field required' => '',
+ // 'Link added successfully.' => '',
+ // 'Link updated successfully.' => '',
+ // 'Link removed successfully.' => '',
+ // 'Link labels' => '',
+ // 'Link modification' => '',
+ // 'Links' => '',
+ // 'Link settings' => '',
+ // 'Opposite label' => '',
+ // 'Remove a link' => '',
+ // 'Task\'s links' => '',
+ // 'The labels must be different' => '',
+ // 'There is no link.' => '',
+ // 'This label must be unique' => '',
+ // 'Unable to create your link.' => '',
+ // 'Unable to update your link.' => '',
+ // 'Unable to remove this link.' => '',
+ // 'relates to' => '',
+ // 'blocks' => '',
+ // 'is blocked by' => '',
+ // 'duplicates' => '',
+ // 'is duplicated by' => '',
+ // 'is a child of' => '',
+ // 'is a parent of' => '',
+ // 'targets milestone' => '',
+ // 'is a milestone of' => '',
+ // 'fixes' => '',
+ // 'is fixed by' => '',
+ // 'This task' => '',
);
diff --git a/app/Model/Acl.php b/app/Model/Acl.php
index b6c0e6b8..e713f2e1 100644
--- a/app/Model/Acl.php
+++ b/app/Model/Acl.php
@@ -38,6 +38,7 @@ class Acl extends Base
'project' => array('show', 'tasks', 'search', 'activity'),
'subtask' => '*',
'task' => '*',
+ 'tasklink' => '*',
'calendar' => array('show', 'events', 'save'),
);
@@ -67,6 +68,7 @@ class Acl extends Base
'app' => array('dashboard'),
'user' => array('index', 'create', 'save', 'remove'),
'config' => '*',
+ 'link' => '*',
'project' => array('remove'),
);
diff --git a/app/Model/Link.php b/app/Model/Link.php
index eddf1c6c..87ba49c4 100644
--- a/app/Model/Link.php
+++ b/app/Model/Link.php
@@ -1,389 +1,209 @@
<?php
+
namespace Model;
+use PDO;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
-use PDO;
/**
* Link model
- * A link is made of one bidirectional (<->), or two sided (<- and ->) link labels.
*
* @package model
- * @author Olivier Maridat
+ * @author Olivier Maridat
+ * @author Frederic Guillot
*/
class Link extends Base
{
-
/**
* SQL table name
*
* @var string
*/
- const TABLE = 'link';
-
- const TABLE_LABEL = 'link_label';
-
- /**
- * Direction: left to right ->
- *
- * @var integer
- */
- const BEHAVIOUR_LEFT2RIGTH = 0;
-
- /**
- * Direction: right to left <-
- *
- * @var integer
- */
- const BEHAVIOUR_RIGHT2LEFT = 1;
-
- /**
- * Bidirectional <->
- *
- * @var integer
- */
- const BEHAVIOUR_BOTH = 2;
+ const TABLE = 'links';
/**
- * Get a link by the id
+ * Get a link by id
*
* @access public
- * @param integer $link_id
- * Link id
- * @param integer $project_id
- * Specify a project id. Default: -1 to target all projects
+ * @param integer $link_id Link id
* @return array
*/
- public function getById($link_id, $project_id = -1)
+ public function getById($link_id)
{
- return $this->db->table(self::TABLE)
- ->eq(self::TABLE . '.link_id', $link_id)
- ->in('project_id', array(
- - 1,
- $project_id
- ))
- ->join(self::TABLE_LABEL, 'link_id', 'link_id')
- ->findAll();
+ return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne();
}
/**
- * Get the id of the inverse link label by a link label id
+ * Get a link by name
*
* @access public
- * @param integer $link_id
- * Link id
- * @param integer $link_label_id
- * Link label id
- * @return integer
+ * @param string $label
+ * @return array
*/
- public function getInverseLinkLabelId($link_label_id)
+ public function getByLabel($label)
{
- $sql = 'SELECT
- la2.id
- FROM ' . self::TABLE_LABEL . ' la1
- JOIN '.self::TABLE_LABEL.' la2 ON la2.link_id = la1.link_id AND (la2.behaviour=2 OR la2.id != la1.id)
- WHERE la1.id = ?
- ';
- $rq = $this->db->execute($sql, array(
- $link_label_id
- ));
- return $rq->fetchColumn(0);
+ return $this->db->table(self::TABLE)->eq('label', $label)->findOne();
}
/**
- * Return all link labels for a given project
+ * Get the opposite link id
*
* @access public
- * @param integer $project_id
- * Specify a project id. Default: -1 to target all projects
- * @return array
+ * @param integer $link_id Link id
+ * @return integer
*/
- public function getLinkLabels($project_id = -1)
+ public function getOppositeLinkId($link_id)
{
- return $this->db->table(self::TABLE_LABEL)
- ->in(self::TABLE . '.project_id', array(
- - 1,
- $project_id
- ))
- ->join(self::TABLE, 'link_id', 'link_id')
- ->asc(self::TABLE_LABEL.'.link_id', 'behaviour')
- ->columns('id', self::TABLE . '.project_id', self::TABLE_LABEL.'.link_id', 'label', 'behaviour')
- ->findAll();
+ $link = $this->getById($link_id);
+ return $link['opposite_id'] ?: $link_id;
}
/**
- * Return the list of all link labels
- * Used to select a link label
+ * Get all links
*
* @access public
- * @param integer $project_id
- * Specify a project id. Default: -1 to target all projects
* @return array
*/
- public function getLinkLabelList($project_id = -1)
+ public function getAll()
{
- $listing = $this->getLinkLabels($project_id);
- $mergedListing = array();
- foreach ($listing as $link) {
- $suffix = '';
- $prefix = '';
- if (self::BEHAVIOUR_BOTH == $link['behaviour'] || self::BEHAVIOUR_LEFT2RIGTH == $link['behaviour']) {
- $suffix = ' &raquo;';
- }
- if (self::BEHAVIOUR_BOTH == $link['behaviour'] || self::BEHAVIOUR_RIGHT2LEFT == $link['behaviour']) {
- $prefix = '&laquo; ';
- }
- $mergedListing[$link['id']] = $prefix . t($link['label']) . $suffix;
- }
- $listing = $mergedListing;
- return $listing;
+ return $this->db->table(self::TABLE)->findAll();
}
/**
- * Return the list of all links (label + inverse label)
+ * Get merged links
*
* @access public
- * @param integer $project_id
- * Specify a project id. Default: -1 to target all projects
* @return array
*/
- public function getMergedList($project_id = -1)
+ public function getMergedList()
{
- $listing = $this->getLinkLabels($project_id);
- $mergedListing = array();
- $current = null;
- foreach ($listing as $link) {
- if (self::BEHAVIOUR_BOTH == $link['behaviour'] || self::BEHAVIOUR_LEFT2RIGTH == $link['behaviour']) {
- $current = $link;
- }
- else {
- $current['label_inverse'] = $link['label'];
- }
- if (self::BEHAVIOUR_BOTH == $link['behaviour'] || self::BEHAVIOUR_RIGHT2LEFT == $link['behaviour']) {
- $mergedListing[] = $current;
- $current = null;
- }
- }
- $listing = $mergedListing;
- return $listing;
+ return $this->db
+ ->execute('
+ SELECT
+ links.id, links.label, opposite.label as opposite_label
+ FROM links
+ LEFT JOIN links AS opposite ON opposite.id=links.opposite_id
+ ')
+ ->fetchAll(PDO::FETCH_ASSOC);
}
/**
- * Prepare data before insert/update
+ * Get label list
*
* @access public
- * @param array $values
- * Form values
+ * @param integer $exclude_id Exclude this link
+ * @param booelan $prepend Prepend default value
+ * @return array
*/
- public function prepare(array &$values)
+ public function getList($exclude_id = 0, $prepend = true)
{
- // Prepare label 1
- $link = array(
- 'project_id' => $values['project_id']
- );
- $label1 = array(
- 'label' => @$values['label'][0],
- 'behaviour' => (isset($values['behaviour'][0]) || !isset($values['label'][1]) || null == $values['label'][1]) ? self::BEHAVIOUR_BOTH : self::BEHAVIOUR_LEFT2RIGTH
- );
- $label2 = array(
- 'label' => @$values['label'][1],
- 'behaviour' => self::BEHAVIOUR_RIGHT2LEFT
- );
- if (isset($values['link_id'])) {
- $link['link_id'] = $values['link_id'];
- $label1['id'] = $values['id'][0];
- $label2['id'] = @$values['id'][1];
- $label1['link_id'] = $values['link_id'];
- $label2['link_id'] = $values['link_id'];
+ $labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label');
+
+ foreach ($labels as &$value) {
+ $value = t($value);
}
-
- $values = $link;
- $values[] = $label1;
- $values[] = $label2;
- return array(
- $link,
- $label1,
- $label2
- );
+
+ return $prepend ? array('') + $labels : $labels;
}
/**
- * Create a link
+ * Create a new link label
*
* @access public
- * @param array $values
- * Form values
- * @return bool integer
+ * @param string $label
+ * @param string $opposite_label
+ * @return boolean
*/
- public function create(array $values)
+ public function create($label, $opposite_label = '')
{
- list ($link, $label1, $label2) = $this->prepare($values);
- // Create link
$this->db->startTransaction();
- $res = $this->db->table(self::TABLE)->save($link);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
-
- // Create label 1
- $label1['link_id'] = $this->db->getConnection()->lastInsertId(self::TABLE);
- $res = $this->db->table(self::TABLE_LABEL)->save($label1);
- if (! $res) {
+
+ if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) {
$this->db->cancelTransaction();
return false;
}
-
- // Create label 2 if any
- if (null != $label2 && self::BEHAVIOUR_BOTH != $label1['behaviour']) {
- $label2['link_id'] = $label1['link_id'];
- $res = $this->db->table(self::TABLE_LABEL)->save($label2);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
+
+ if ($opposite_label !== '') {
+ $this->createOpposite($opposite_label);
}
+
$this->db->closeTransaction();
- return $res;
+
+ return true;
}
/**
- * Update a link
+ * Create the opposite label (executed inside create() method)
*
- * @access public
- * @param array $values
- * Form values
- * @return bool
+ * @access private
+ * @param string $label
*/
- public function update(array $values)
+ private function createOpposite($label)
{
- list($link, $label1, $label2) = $this->prepare($values);
- // Update link
- $this->db->startTransaction();
- $res = $this->db->table(self::TABLE)
- ->eq('link_id', $link['link_id'])
- ->save($link);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
-
- // Update label 1
- $this->db->startTransaction();
- $res = $this->db->table(self::TABLE_LABEL)
- ->eq('id', $label1['id'])
- ->save($label1);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
-
- // Update label 2 (if label 1 not bidirectional)
- if (null != $label2 && self::BEHAVIOUR_BOTH != $label1['behaviour']) {
- // Create
- if (! isset($label2['id']) || null == $label2['id']) {
- unset($label2['id']);
- $res = $this->db->table(self::TABLE_LABEL)->save($label2);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
- $label2['id'] = $this->db->getConnection()->lastInsertId(self::TABLE_LABEL);
- $this->taskLink->changeLinkLabel($link['link_id'], $label2['id'], true);
- }
- // Update
- else {
- $res = $this->db->table(self::TABLE_LABEL)
- ->eq('id', $label2['id'])
- ->save($label2);
- if (! $res) {
- $this->db->cancelTransaction();
- return false;
- }
- }
- }
- // Remove label 2 (if label 1 bidirectional)
- else {
- $this->taskLink->changeLinkLabel($link['link_id'], $label1['id']);
- $this->db->table(self::TABLE_LABEL)
- ->eq('link_id', $link['link_id'])
- ->neq('id', $label1['id'])
- ->remove();
- }
- $this->db->closeTransaction();
- return $res;
+ $label_id = $this->db->getConnection()->getLastId();
+
+ $this->db
+ ->table(self::TABLE)
+ ->insert(array(
+ 'label' => $label,
+ 'opposite_id' => $label_id,
+ ));
+
+ $this->db
+ ->table(self::TABLE)
+ ->eq('id', $label_id)
+ ->update(array(
+ 'opposite_id' => $this->db->getConnection()->getLastId()
+ ));
}
/**
- * Remove a link
+ * Update a link
*
* @access public
- * @param integer $link_id
- * Link id
- * @return bool
+ * @param array $values
+ * @return boolean
*/
- public function remove($link_id)
+ public function update(array $values)
{
- $this->db->startTransaction();
- if (! $this->db->table(self::TABLE)
- ->eq('link_id', $link_id)
- ->remove()) {
- $this->db->cancelTransaction();
- return false;
- }
- $this->db->closeTransaction();
- return true;
+ return $this->db
+ ->table(self::TABLE)
+ ->eq('id', $values['id'])
+ ->update(array(
+ 'label' => $values['label'],
+ 'opposite_id' => $values['opposite_id'],
+ ));
}
/**
- * Duplicate links from a project to another one, must be executed inside a transaction
+ * Remove a link a the relation to its opposite
*
- * @param integer $src_project_id
- * Source project id
- * @return integer $dst_project_id Destination project id
+ * @access public
+ * @param integer $link_id
* @return boolean
*/
- public function duplicate($src_project_id, $dst_project_id)
+ public function remove($link_id)
{
- $labels = $this->db->table(self::TABLE_LABEL)
- ->columns(self::TABLE_LABEL.'.link_id', 'label', 'behaviour')
- ->eq('project_id', $src_project_id)
- ->join(self::TABLE, 'link_id', 'link_id')
- ->asc(self::TABLE_LABEL.'.link_id', 'behaviour')
- ->findAll();
-
- $this->db->startTransaction();
- $link = array('project_id' => $dst_project_id);
- if (! $this->db->table(self::TABLE)->save($link)) {
- $this->db->cancelTransaction();
- return false;
- }
- $link['id'] = $this->db->getConnection()->lastInsertId(self::TABLE);
-
- foreach ($labels as $label) {
- $label['link_id'] = $link['id'];
- if (! $this->db->table(self::TABLE_LABEL)->save($label)) {
- $this->db->cancelTransaction();
- return false;
- }
- }
- $this->db->closeTransaction();
- return true;
+ $this->db->table(self::TABLE)->eq('opposite_id', $link_id)->update(array('opposite_id' => 0));
+ return $this->db->table(self::TABLE)->eq('id', $link_id)->remove();
}
/**
- * Validate link creation
+ * Validate creation
*
* @access public
- * @param array $values
- * Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
- $v = new Validator($values, $this->commonValidationRules());
-
+ $v = new Validator($values, array(
+ new Validators\Required('label', t('Field required')),
+ new Validators\Unique('label', t('This label must be unique'), $this->db->getConnection(), self::TABLE),
+ new Validators\NotEquals('label', 'opposite_label', t('The labels must be different')),
+ ));
+
return array(
$v->execute(),
$v->getErrors()
@@ -391,47 +211,24 @@ class Link extends Base
}
/**
- * Validate link modification
+ * Validate modification
*
* @access public
- * @param array $values
- * Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateModification(array $values)
{
- $rules = array(
- new Validators\Required('link_id', t('The id is required')),
-// new Validators\Required('id[0]', t('The label id is required'))
- );
- $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
-
+ $v = new Validator($values, array(
+ new Validators\Required('id', t('Field required')),
+ new Validators\Required('opposite_id', t('Field required')),
+ new Validators\Required('label', t('Field required')),
+ new Validators\Unique('label', t('This label must be unique'), $this->db->getConnection(), self::TABLE),
+ ));
+
return array(
$v->execute(),
$v->getErrors()
);
}
-
- /**
- * Common validation rules
- *
- * @access private
- * @return array
- */
- private function commonValidationRules()
- {
- // TODO Update simple-validator to support array in forms
- return array(
- new Validators\Required('project_id', t('The project id required')),
- // new Validators\Required('label[0]', t('The link label is required')),
- new Validators\Integer('project_id', t('The project id must be an integer')),
- new Validators\Integer('link_id', t('The link id must be an integer')),
-// new Validators\Integer('id[0]', t('The link label id must be an integer')),
-// new Validators\Integer('id[1]', t('The link label id must be an integer')),
-// new Validators\Integer('behaviour[0]', t('The link label id must be an integer')),
-// new Validators\Integer('behaviour[1]', t('The link label id must be an integer')),
-// new Validators\MaxLength('label[0]', t('The maximum length is %d characters', 200), 200),
-// new Validators\MaxLength('label[1]', t('The maximum length is %d characters', 200), 200)
- );
- }
}
diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php
index fb6316b6..cd892da4 100644
--- a/app/Model/ProjectPermission.php
+++ b/app/Model/ProjectPermission.php
@@ -338,6 +338,23 @@ class ProjectPermission extends Base
}
/**
+ * Return a list of active project ids where the user is member
+ *
+ * @access public
+ * @param integer $user_id User id
+ * @return []integer
+ */
+ public function getActiveMemberProjectIds($user_id)
+ {
+ return $this->db
+ ->table(Project::TABLE)
+ ->eq('user_id', $user_id)
+ ->eq(Project::TABLE.'.is_active', Project::ACTIVE)
+ ->join(self::TABLE, 'project_id', 'id')
+ ->findAllByColumn('projects.id');
+ }
+
+ /**
* Return a list of active projects where the user is member
*
* @access public
diff --git a/app/Model/TaskDuplication.php b/app/Model/TaskDuplication.php
index faa5467f..bd593dc1 100644
--- a/app/Model/TaskDuplication.php
+++ b/app/Model/TaskDuplication.php
@@ -159,7 +159,6 @@ class TaskDuplication extends Base
if ($new_task_id) {
$this->subtask->duplicate($task_id, $new_task_id);
- $this->taskLink->duplicate($task_id, $new_task_id);
}
return $new_task_id;
diff --git a/app/Model/TaskFilter.php b/app/Model/TaskFilter.php
index b5a90154..31de2795 100644
--- a/app/Model/TaskFilter.php
+++ b/app/Model/TaskFilter.php
@@ -18,6 +18,24 @@ class TaskFilter extends Base
return $this;
}
+ public function excludeTasks(array $task_ids)
+ {
+ $this->query->notin('id', $task_ids);
+ return $this;
+ }
+
+ public function filterByTitle($title)
+ {
+ $this->query->ilike('title', '%'.$title.'%');
+ return $this;
+ }
+
+ public function filterByProjects(array $project_ids)
+ {
+ $this->query->in('project_id', $project_ids);
+ return $this;
+ }
+
public function filterByProject($project_id)
{
if ($project_id > 0) {
@@ -94,6 +112,20 @@ class TaskFilter extends Base
return $this->query->findAll();
}
+ public function toAutoCompletion()
+ {
+ return $this->query->columns('id', 'title')->filter(function(array $results) {
+
+ foreach ($results as &$result) {
+ $result['value'] = $result['title'];
+ $result['label'] = '#'.$result['id'].' - '.$result['title'];
+ }
+
+ return $results;
+
+ })->findAll();
+ }
+
public function toCalendarEvents()
{
$events = array();
diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php
index cf756cd8..98ece4e1 100644
--- a/app/Model/TaskFinder.php
+++ b/app/Model/TaskFinder.php
@@ -3,7 +3,6 @@
namespace Model;
use PDO;
-use Model\TaskLink;
/**
* Task Finder model
@@ -130,29 +129,6 @@ class TaskFinder extends Base
->asc('tasks.position')
->findAll();
}
-
- /**
- * Get ids and names of all (limited by $limit) tasks for a given project and status
- *
- * @access public
- * @param integer $project_id Project id
- * @param integer $status_id Status id
- * @param integer $exclude_id Exclude this task id in the result
- * @param integer $limit Number of tasks to list
- * @return array
- */
- public function getList($project_id, $status_id = Task::STATUS_OPEN, $exclude_id=null, $limit=50)
- {
- $sql = $this->db
- ->hashtable(Task::TABLE)
- ->eq('project_id', $project_id)
- ->eq('is_active', $status_id)
- ->limit($limit);
- if (null != $exclude_id) {
- $sql->neq('id', $exclude_id);
- }
- return $sql->getAll('id', 'title');
- }
/**
* Get all tasks for a given project and status
diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php
index 09f37d2e..c712e5a8 100644
--- a/app/Model/TaskLink.php
+++ b/app/Model/TaskLink.php
@@ -1,20 +1,19 @@
<?php
+
namespace Model;
-use Core\Helper;
use SimpleValidator\Validator;
use SimpleValidator\Validators;
-use PDO;
/**
* TaskLink model
*
* @package model
- * @author Olivier Maridat
+ * @author Olivier Maridat
+ * @author Frederic Guillot
*/
class TaskLink extends Base
{
-
/**
* SQL table name
*
@@ -23,339 +22,121 @@ class TaskLink extends Base
const TABLE = 'task_has_links';
/**
- * Get a link by the task link id
+ * Get a task link
*
* @access public
- * @param integer $task_link_id
- * Task link id
+ * @param integer $task_link_id Task link id
* @return array
*/
public function getById($task_link_id)
{
- $sql = 'SELECT
- tl1.id AS id,
- tl1.link_label_id AS link_label_id,
- tl1.task_id AS task_id,
- tl1.task_inverse_id AS task_inverse_id,
- tl2.id AS task_link_inverse_id
- FROM ' . self::TABLE . ' tl1
- LEFT JOIN ' . Link::TABLE_LABEL . ' l1 ON l1.id = tl1.link_label_id
- LEFT JOIN ' . Link::TABLE_LABEL . ' l2 ON l2.link_id = l1.link_id
- LEFT JOIN ' . self::TABLE . ' tl2 ON tl2.task_id = tl1.task_inverse_id
- AND ( (l1.behaviour = 2 AND tl2.link_label_id = l1.id) OR (tl2.link_label_id = l2.id) )
- WHERE tl1.id = ?
- ';
- $rq = $this->db->execute($sql, array(
- $task_link_id
- ));
- return $rq->fetch();
+ return $this->db->table(self::TABLE)->eq('id', $task_link_id)->findOne();
}
/**
- * Get the id of the inverse task link by a task link id
+ * Get all links attached to a task
*
* @access public
- * @param integer $link_id
- * Task link id
- * @return integer
+ * @param integer $task_id Task id
+ * @return array
*/
- public function getInverseTaskLinkId($task_link_id)
+ public function getLinks($task_id)
{
- $sql = 'SELECT
- tl2.id
- FROM ' . self::TABLE . ' tl1
- LEFT JOIN ' . Link::TABLE_LABEL . ' l1 ON l1.id = tl1.link_label_id
- LEFT JOIN ' . Link::TABLE_LABEL . ' l2 ON l2.link_id = l1.link_id
- LEFT JOIN ' . self::TABLE . ' tl2 ON tl2.task_id = tl1.task_inverse_id
- AND ( (l1.behaviour = 2 AND tl2.link_label_id = l1.id) OR (tl2.link_label_id = l2.id) )
- WHERE tl1.id = ?
- ';
- $rq = $this->db->execute($sql, array(
- $task_link_id
- ));
- return $rq->fetchColumn(0);
+ return $this->db
+ ->table(self::TABLE)
+ ->columns(
+ self::TABLE.'.id',
+ self::TABLE.'.opposite_task_id AS task_id',
+ Link::TABLE.'.label',
+ Task::TABLE.'.title',
+ Task::TABLE.'.is_active',
+ Task::TABLE.'.project_id'
+ )
+ ->eq(self::TABLE.'.task_id', $task_id)
+ ->join(Link::TABLE, 'id', 'link_id')
+ ->join(Task::TABLE, 'id', 'opposite_task_id')
+ ->findAll();
}
/**
- * Return all links for a given task
+ * Create a new link
*
* @access public
- * @param integer $task_id
- * Task id
- * @return array
+ * @param integer $task_id Task id
+ * @param integer $opposite_task_id Opposite task id
+ * @param integer $link_id Link id
+ * @return boolean
*/
- public function getAll($task_id)
+ public function create($task_id, $opposite_task_id, $link_id)
{
- $sql = 'SELECT
- tl1.id,
- l.label AS label,
- t2.id AS task_inverse_id,
- t2.project_id AS task_inverse_project_id,
- t2.title AS task_inverse_title,
- t2.is_active AS task_inverse_is_active,
- t2cat.name AS task_inverse_category
- FROM ' . self::TABLE . ' tl1
- LEFT JOIN ' . Link::TABLE_LABEL . ' l ON l.id = tl1.link_label_id
- LEFT JOIN ' . Task::TABLE . ' t2 ON t2.id = tl1.task_inverse_id
- LEFT JOIN ' . Category::TABLE . ' t2cat ON t2cat.id = t2.category_id
- WHERE tl1.task_id = ?
- ORDER BY l.label, t2cat.name, t2.id
- ';
- $rq = $this->db->execute($sql, array(
- $task_id
+ $this->db->startTransaction();
+
+ // Create the original link
+ $this->db->table(self::TABLE)->insert(array(
+ 'task_id' => $task_id,
+ 'opposite_task_id' => $opposite_task_id,
+ 'link_id' => $link_id,
));
- $res = $rq->fetchAll(PDO::FETCH_ASSOC);
- return $res;
- }
- /**
- * Prepare data before insert/update
- *
- * @access public
- * @param array $values
- * Form values
- */
- public function prepare(array &$values)
- {
- $this->removeFields($values, array(
- 'another_link'
+ $link_id = $this->link->getOppositeLinkId($link_id);
+
+ // Create the opposite link
+ $this->db->table(self::TABLE)->insert(array(
+ 'task_id' => $opposite_task_id,
+ 'opposite_task_id' => $task_id,
+ 'link_id' => $link_id,
));
- $taskLink1 = array(
- 'link_label_id' => $values['link_label_id'],
- 'task_id' => $values['task_id'],
- 'task_inverse_id' => $values['task_inverse_id']
- );
- $taskLink2 = array(
- 'link_label_id' => $this->link->getInverseLinkLabelId($taskLink1['link_label_id']),
- 'task_id' => $values['task_inverse_id'],
- 'task_inverse_id' => $values['task_id']
- );
- if (isset($values['id']) && isset($values['task_link_inverse_id'])) {
- $taskLink1['id'] = $values['id'];
- $taskLink2['id'] = $values['task_link_inverse_id'];
- }
- return array(
- $taskLink1,
- $taskLink2
- );
- }
- /**
- * Create a link
- *
- * @access public
- * @param array $values
- * Form values
- * @return bool integer
- */
- public function create(array $values)
- {
- list($taskLink1, $taskLink2) = $this->prepare($values);
- $this->db->startTransaction();
- if (! $this->db->table(self::TABLE)->save($taskLink1)) {
- $this->db->cancelTransaction();
- return false;
- }
- if (! $this->db->table(self::TABLE)->save($taskLink2)) {
- $this->db->cancelTransaction();
- return false;
- }
$this->db->closeTransaction();
- return true;
- }
- /**
- * Update a link
- *
- * @access public
- * @param array $values
- * Form values
- * @return bool
- */
- public function update(array $values)
- {
- list($taskLink1, $taskLink2) = $this->prepare($values);
- $this->db->startTransaction();
- if (! $this->db->table(self::TABLE)
- ->eq('id', $taskLink1['id'])
- ->save($taskLink1)) {
- $this->db->cancelTransaction();
- return false;
- }
- if (! $this->db->table(self::TABLE)
- ->eq('id', $taskLink2['id'])
- ->save($taskLink2)) {
- $this->db->cancelTransaction();
- return false;
- }
- $this->db->closeTransaction();
return true;
}
/**
- * Remove a link
+ * Remove a link between two tasks
*
* @access public
- * @param integer $task_link_id
- * Task Link id
- * @return bool
+ * @param integer $task_link_id
+ * @return boolean
*/
public function remove($task_link_id)
{
- $task_link_inverse_id = $this->getInverseTaskLinkId($task_link_id);
$this->db->startTransaction();
- if (! $this->db->table(self::TABLE)
- ->eq('id', $task_link_id)
- ->remove()) {
- $this->db->cancelTransaction();
- return false;
- }
- if (! $this->db->table(self::TABLE)
- ->eq('id', $task_link_inverse_id)
- ->remove()) {
- $this->db->cancelTransaction();
- return false;
- }
- $this->db->closeTransaction();
- return true;
- }
- /**
- * Duplicate all links to another task
- *
- * @access public
- * @param integer $src_task_id
- * Source task id
- * @param integer $dst_task_id
- * Destination task id
- * @return bool
- */
- public function duplicate($src_task_id, $dst_task_id)
- {
- return $this->db->transaction(function ($db) use($src_task_id, $dst_task_id)
- {
- $links = $db->table(TaskLink::TABLE)
- ->columns('link_label_id', 'task_id', 'task_inverse_id')
- ->eq('task_id', $src_task_id)
- ->asc('id')
- ->findAll();
- foreach ($links as &$link) {
- $link['task_id'] = $dst_task_id;
- if (! $db->table(TaskLink::TABLE)
- ->save($link)) {
- return false;
- }
- }
-
- $links = $db->table(TaskLink::TABLE)
- ->columns('link_label_id', 'task_id', 'task_inverse_id')
- ->eq('task_inverse_id', $src_task_id)
- ->asc('id')
- ->findAll();
- foreach ($links as &$link) {
- $link['task_inverse_id'] = $dst_task_id;
- if (! $db->table(TaskLink::TABLE)
- ->save($link)) {
- return false;
- }
- }
- });
- }
+ $link = $this->getById($task_link_id);
+ $link_id = $this->link->getOppositeLinkId($link['link_id']);
+
+ $this->db->table(self::TABLE)->eq('id', $task_link_id)->remove();
+
+ $this->db
+ ->table(self::TABLE)
+ ->eq('opposite_task_id', $link['task_id'])
+ ->eq('task_id', $link['opposite_task_id'])
+ ->eq('link_id', $link_id)->remove();
+
+ $this->db->closeTransaction();
- /**
- * Move a task link from a link label to an other
- *
- * @access public
- * @param integer $link_id
- * Link id
- * @param integer $dst_link_label_id
- * Destination link label id
- * @return bool
- */
- public function changeLinkLabel($link_id, $dst_link_label_id, $alternate=false)
- {
- $taskLinks = $this->db->table(Link::TABLE_LABEL)
- ->eq('link_id', $link_id)
- ->neq(Link::TABLE_LABEL.'.id', $dst_link_label_id)
- ->join(self::TABLE, 'link_label_id', 'id')
- ->asc(self::TABLE.'.id')
- ->findAllByColumn(self::TABLE.'.id');
- foreach ($taskLinks as $i => $taskLinkId) {
- if (null == $taskLinkId || ($alternate && 0 != $i % 2)) {
- continue;
- }
- if (! $this->db->table(self::TABLE)
- ->eq('id', $taskLinkId)
- ->save(array('link_label_id' => $dst_link_label_id))) {
- return false;
- }
- }
return true;
}
/**
- * Validate link creation
+ * Validate creation
*
* @access public
- * @param array $values
- * Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
+ * @param array $values Form values
+ * @return array $valid, $errors [0] = Success or not, [1] = List of errors
*/
public function validateCreation(array $values)
{
- $v = new Validator($values, $this->commonValidationRules());
- $res = array(
- $v->execute(),
- $v->getErrors()
- );
- return $res;
- }
+ $v = new Validator($values, array(
+ new Validators\Required('task_id', t('Field required')),
+ new Validators\Required('link_id', t('Field required')),
+ new Validators\Required('title', t('Field required')),
+ ));
- /**
- * Validate link modification
- *
- * @access public
- * @param array $values
- * Form values
- * @return array $valid, $errors [0] = Success or not, [1] = List of errors
- */
- public function validateModification(array $values)
- {
- $rules = array(
- new Validators\Required('id', t('The id is required'))
- );
- $v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
- $res = array(
+ return array(
$v->execute(),
$v->getErrors()
);
- return $res;
- }
-
- /**
- * Common validation rules
- *
- * @access private
- * @return array
- */
- private function commonValidationRules()
- {
- return array(
- new Validators\Required('link_label_id', t('The link type is required')),
- new Validators\Required('task_id', t('The task id is required')),
- new Validators\Required('task_inverse_id', t('The linked task id is required')),
- new Validators\Integer('id', t('The id must be an integer')),
- new Validators\Integer('link_label_id', t('The link id must be an integer')),
- new Validators\Integer('task_id', t('The task id must be an integer')),
- new Validators\Integer('task_inverse_id', t('The related task id must be an integer')),
- new Validators\Integer('task_link_inverse_id', t('The related task link id must be an integer')),
- new Validators\NotEquals('task_inverse_id', 'task_id', t('A task can not be linked to itself')),
- new Validators\Exists('task_inverse_id', t('This linked task id doesn\'t exist'), $this->db->getConnection(), Task::TABLE, 'id'),
- new Validators\Unique(array(
- 'task_inverse_id',
- 'link_label_id',
- 'task_id'
- ), t('The exact same link already exists'), $this->db->getConnection(), self::TABLE)
- );
}
}
diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php
index 82dd8428..947a62b3 100644
--- a/app/Schema/Mysql.php
+++ b/app/Schema/Mysql.php
@@ -10,59 +10,46 @@ const VERSION = 46;
function version_46($pdo)
{
- $pdo->exec("CREATE TABLE link
- (
- link_id INT NOT NULL AUTO_INCREMENT,
- project_id INT NOT NULL DEFAULT -1,
- PRIMARY KEY(link_id)
- ) ENGINE=InnoDB CHARSET=utf8");
- $pdo->exec("CREATE TABLE link_label
- (
- id INT NOT NULL AUTO_INCREMENT,
- link_id INT NOT NULL,
- label TEXT NOT NULL,
- behaviour INT DEFAULT 2,
- PRIMARY KEY(id),
- FOREIGN KEY(link_id) REFERENCES link(link_id) ON DELETE CASCADE
- ) ENGINE=InnoDB CHARSET=utf8");
- $pdo->exec("CREATE TABLE task_has_links
- (
- id INT NOT NULL AUTO_INCREMENT,
- link_label_id INT NOT NULL,
- task_id INT NOT NULL,
- task_inverse_id INT NOT NULL,
- PRIMARY KEY(id),
- FOREIGN KEY(link_label_id) REFERENCES link_label(id) ON DELETE CASCADE,
- FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
- FOREIGN KEY(task_inverse_id) REFERENCES tasks(id) ON DELETE CASCADE
- ) ENGINE=InnoDB CHARSET=utf8");
+ $pdo->exec("CREATE TABLE links (
+ id INT NOT NULL AUTO_INCREMENT,
+ label VARCHAR(255) NOT NULL,
+ opposite_id INT DEFAULT 0,
+ PRIMARY KEY(id),
+ UNIQUE(label)
+ ) ENGINE=InnoDB CHARSET=utf8");
+
+ $pdo->exec("CREATE TABLE task_has_links (
+ id INT NOT NULL AUTO_INCREMENT,
+ link_id INT NOT NULL,
+ task_id INT NOT NULL,
+ opposite_task_id INT NOT NULL,
+ FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ PRIMARY KEY(id)
+ ) ENGINE=InnoDB CHARSET=utf8");
+
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
- $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_label_id, task_id, task_inverse_id)");
- $rq = $pdo->prepare('INSERT INTO link (project_id) VALUES (?)');
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq = $pdo->prepare('INSERT INTO link_label (link_id, label, behaviour) VALUES (?, ?, ?)');
- $rq->execute(array(1, t('relates to'), Link::BEHAVIOUR_BOTH));
- $rq->execute(array(2, t('blocks'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(2, t('is blocked by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(3, t('duplicates'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(3, t('is duplicated by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(4, t('is a child of'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(4, t('is a parent of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(5, t('targets milestone'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(5, t('is a milestone of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(6, t('fixes'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(6, t('is fixed by'), Link::BEHAVIOUR_RIGHT2LEFT));
+ $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
+
+ $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
+ $rq->execute(array('relates to', 0));
+ $rq->execute(array('blocks', 3));
+ $rq->execute(array('is blocked by', 2));
+ $rq->execute(array('duplicates', 5));
+ $rq->execute(array('is duplicated by', 4));
+ $rq->execute(array('is a child of', 7));
+ $rq->execute(array('is a parent of', 6));
+ $rq->execute(array('targets milestone', 9));
+ $rq->execute(array('is a milestone of', 8));
+ $rq->execute(array('fixes', 11));
+ $rq->execute(array('is fixed by', 10));
}
function version_45($pdo)
{
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INT DEFAULT 0');
-
+
/* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0.
* We take max project_activities.date_creation where event_name in task.create','task.move.column
* since creation date is always less than task moves
@@ -174,7 +161,7 @@ function version_38($pdo)
");
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INT DEFAULT 0');
- $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT '".t('Default swimlane')."'");
+ $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'");
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INT DEFAULT 1");
}
diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php
index af1f3170..027401ff 100644
--- a/app/Schema/Postgres.php
+++ b/app/Schema/Postgres.php
@@ -10,50 +10,38 @@ const VERSION = 27;
function version_27($pdo)
{
- $pdo->exec("CREATE TABLE link
- (
- link_id SERIAL PRIMARY KEY,
- project_id INTEGER NOT NULL DEFAULT -1
- ) ENGINE=InnoDB CHARSET=utf8");
- $pdo->exec("CREATE TABLE link_label
- (
- id SERIAL PRIMARY KEY,
- link_id INTEGER NOT NULL,
- label TEXT NOT NULL,
- behaviour INTEGER NOT NULL DEFAULT 2,
- FOREIGN KEY(link_id) REFERENCES link(link_id) ON DELETE CASCADE
- ) ENGINE=InnoDB CHARSET=utf8");
- $pdo->exec("CREATE TABLE task_has_links
- (
- id SERIAL PRIMARY KEY,
- link_label_id INTEGER NOT NULL,
- task_id INTEGER NOT NULL,
- task_inverse_id INTEGER NOT NULL,
- FOREIGN KEY(link_label_id) REFERENCES link_label(id) ON DELETE CASCADE,
- FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
- FOREIGN KEY(task_inverse_id) REFERENCES tasks(id) ON DELETE CASCADE
- ) ENGINE=InnoDB CHARSET=utf8");
+ $pdo->exec('CREATE TABLE links (
+ "id" SERIAL PRIMARY KEY,
+ "label" VARCHAR(255) NOT NULL,
+ "opposite_id" INTEGER DEFAULT 0,
+ UNIQUE("label")
+ )');
+
+ $pdo->exec("CREATE TABLE task_has_links (
+ id SERIAL PRIMARY KEY,
+ link_id INTEGER NOT NULL,
+ task_id INTEGER NOT NULL,
+ opposite_task_id INTEGER NOT NULL,
+ FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE
+ )");
+
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
- $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_label_id, task_id, task_inverse_id)");
- $rq = $pdo->prepare('INSERT INTO link (project_id) VALUES (?)');
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq = $pdo->prepare('INSERT INTO link_label (link_id, label, behaviour) VALUES (?, ?, ?)');
- $rq->execute(array(1, t('relates to'), Link::BEHAVIOUR_BOTH));
- $rq->execute(array(2, t('blocks'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(2, t('is blocked by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(3, t('duplicates'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(3, t('is duplicated by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(4, t('is a child of'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(4, t('is a parent of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(5, t('targets milestone'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(5, t('is a milestone of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(6, t('fixes'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(6, t('is fixed by'), Link::BEHAVIOUR_RIGHT2LEFT));
+ $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
+
+ $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
+ $rq->execute(array('relates to', 0));
+ $rq->execute(array('blocks', 3));
+ $rq->execute(array('is blocked by', 2));
+ $rq->execute(array('duplicates', 5));
+ $rq->execute(array('is duplicated by', 4));
+ $rq->execute(array('is a child of', 7));
+ $rq->execute(array('is a parent of', 6));
+ $rq->execute(array('targets milestone', 9));
+ $rq->execute(array('is a milestone of', 8));
+ $rq->execute(array('fixes', 11));
+ $rq->execute(array('is fixed by', 10));
}
function version_26($pdo)
@@ -169,7 +157,7 @@ function version_19($pdo)
");
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0');
- $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT '".t('Default swimlane')."'");
+ $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane VARCHAR(200) DEFAULT 'Default swimlane'");
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane BOOLEAN DEFAULT '1'");
}
diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php
index 727a32cd..c6dec33f 100644
--- a/app/Schema/Sqlite.php
+++ b/app/Schema/Sqlite.php
@@ -10,56 +10,44 @@ const VERSION = 45;
function version_45($pdo)
{
- $pdo->exec("CREATE TABLE link
- (
- link_id INTEGER PRIMARY KEY,
- project_id INTEGER NOT NULL DEFAULT -1
- )");
- $pdo->exec("CREATE TABLE link_label
- (
- id INTEGER PRIMARY KEY,
- link_id INTEGER NOT NULL,
- label TEXT NOT NULL,
- behaviour INTEGER DEFAULT '2',
- FOREIGN KEY(link_id) REFERENCES link(link_id) ON DELETE CASCADE
- )");
- $pdo->exec("CREATE TABLE task_has_links
- (
- id INTEGER PRIMARY KEY,
- link_label_id INTEGER NOT NULL,
- task_id INTEGER NOT NULL,
- task_inverse_id INTEGER NOT NULL,
- FOREIGN KEY(link_label_id) REFERENCES link_label(id) ON DELETE CASCADE,
- FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
- FOREIGN KEY(task_inverse_id) REFERENCES tasks(id) ON DELETE CASCADE
- )");
+ $pdo->exec("CREATE TABLE links (
+ id INTEGER PRIMARY KEY,
+ label TEXT NOT NULL,
+ opposite_id INTEGER DEFAULT 0,
+ UNIQUE(label)
+ )");
+
+ $pdo->exec("CREATE TABLE task_has_links (
+ id INTEGER PRIMARY KEY,
+ link_id INTEGER NOT NULL,
+ task_id INTEGER NOT NULL,
+ opposite_task_id INTEGER NOT NULL,
+ FOREIGN KEY(link_id) REFERENCES links(id) ON DELETE CASCADE,
+ FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
+ FOREIGN KEY(opposite_task_id) REFERENCES tasks(id) ON DELETE CASCADE
+ )");
+
$pdo->exec("CREATE INDEX task_has_links_task_index ON task_has_links(task_id)");
- $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_label_id, task_id, task_inverse_id)");
- $rq = $pdo->prepare('INSERT INTO link (project_id) VALUES (?)');
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq->execute(array(-1));
- $rq = $pdo->prepare('INSERT INTO link_label (link_id, label, behaviour) VALUES (?, ?, ?)');
- $rq->execute(array(1, t('relates to'), Link::BEHAVIOUR_BOTH));
- $rq->execute(array(2, t('blocks'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(2, t('is blocked by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(3, t('duplicates'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(3, t('is duplicated by'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(4, t('is a child of'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(4, t('is a parent of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(5, t('targets milestone'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(5, t('is a milestone of'), Link::BEHAVIOUR_RIGHT2LEFT));
- $rq->execute(array(6, t('fixes'), Link::BEHAVIOUR_LEFT2RIGTH));
- $rq->execute(array(6, t('is fixed by'), Link::BEHAVIOUR_RIGHT2LEFT));
+ $pdo->exec("CREATE UNIQUE INDEX task_has_links_unique ON task_has_links(link_id, task_id, opposite_task_id)");
+
+ $rq = $pdo->prepare('INSERT INTO links (label, opposite_id) VALUES (?, ?)');
+ $rq->execute(array('relates to', 0));
+ $rq->execute(array('blocks', 3));
+ $rq->execute(array('is blocked by', 2));
+ $rq->execute(array('duplicates', 5));
+ $rq->execute(array('is duplicated by', 4));
+ $rq->execute(array('is a child of', 7));
+ $rq->execute(array('is a parent of', 6));
+ $rq->execute(array('targets milestone', 9));
+ $rq->execute(array('is a milestone of', 8));
+ $rq->execute(array('fixes', 11));
+ $rq->execute(array('is fixed by', 10));
}
function version_44($pdo)
{
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_moved INTEGER DEFAULT 0');
-
+
/* Update tasks.date_moved from project_activities table if tasks.date_moved = null or 0.
* We take max project_activities.date_creation where event_name in task.create','task.move.column
* since creation date is always less than task moves
@@ -169,7 +157,7 @@ function version_37($pdo)
");
$pdo->exec('ALTER TABLE tasks ADD COLUMN swimlane_id INTEGER DEFAULT 0');
- $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane TEXT DEFAULT '".t('Default swimlane')."'");
+ $pdo->exec("ALTER TABLE projects ADD COLUMN default_swimlane TEXT DEFAULT 'Default swimlane'");
$pdo->exec("ALTER TABLE projects ADD COLUMN show_default_swimlane INTEGER DEFAULT 1");
}
diff --git a/app/Template/board/tasklinks.php b/app/Template/board/tasklinks.php
index d7b64e1d..9c4f52ca 100644
--- a/app/Template/board/tasklinks.php
+++ b/app/Template/board/tasklinks.php
@@ -1,28 +1,15 @@
-<section class="tooltip-tasklinks">
-<div>
-<ul>
-<?php
-$previous_link = null;
-foreach ($links as $link): ?>
- <?php if (null == $previous_link || $previous_link != $link['label']): ?>
- <?php if (null != $previous_link): ?>
- </ul>
+<div class="tooltip-tasklinks">
+ <ul>
+ <?php foreach($links as $link): ?>
+ <li>
+ <strong><?= t($link['label']) ?></strong>
+ <?= $this->a(
+ $this->e('#'.$link['task_id'].' - '.$link['title']),
+ 'task', 'show', array('task_id' => $link['task_id'], 'project_id' => $link['project_id']),
+ false,
+ $link['is_active'] ? '' : 'task-link-closed'
+ ) ?>
</li>
- <?php endif ?>
- <?php $previous_link = $link['label']; ?>
- <li><?= t($this->e($link['label'])) ?>
- <ul>
- <?php endif ?>
- <li<?php if (0 == $link['task_inverse_is_active']): ?> class="task-closed"<?php endif ?>>
- <?= $this->e($link['task_inverse_category']) ?>
- <?= $this->a('#'.$this->e($link['task_inverse_id']).' - '.trim($this->e($link['task_inverse_title'])),
- 'task',
- 'show',
- array('task_id' => $link['task_inverse_id'], 'project_id' => $link['task_inverse_project_id'])) ?>
- </li>
-<?php endforeach ?>
- </ul>
- </li>
-</ul>
-</div>
-</section>
+ <?php endforeach ?>
+ </ul>
+</div> \ No newline at end of file
diff --git a/app/Template/config/sidebar.php b/app/Template/config/sidebar.php
index a0ec8b36..89f2c203 100644
--- a/app/Template/config/sidebar.php
+++ b/app/Template/config/sidebar.php
@@ -11,7 +11,7 @@
<?= $this->a(t('Board settings'), 'config', 'board') ?>
</li>
<li>
- <?= $this->a(t('Links settings'), 'link', 'index') ?>
+ <?= $this->a(t('Link settings'), 'link', 'index') ?>
</li>
<li>
<?= $this->a(t('Webhooks'), 'config', 'webhook') ?>
diff --git a/app/Template/link/create.php b/app/Template/link/create.php
new file mode 100644
index 00000000..12589574
--- /dev/null
+++ b/app/Template/link/create.php
@@ -0,0 +1,18 @@
+<div class="page-header">
+ <h2><?= t('Add a new link') ?></h2>
+</div>
+
+<form action="<?= $this->u('link', 'save') ?>" method="post" autocomplete="off">
+
+ <?= $this->formCsrf() ?>
+
+ <?= $this->formLabel(t('Label'), 'label') ?>
+ <?= $this->formText('label', $values, $errors, array('required')) ?>
+
+ <?= $this->formLabel(t('Opposite label'), 'opposite_label') ?>
+ <?= $this->formText('opposite_label', $values, $errors) ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/link/edit.php b/app/Template/link/edit.php
index 71d4f3ca..d9ce280c 100644
--- a/app/Template/link/edit.php
+++ b/app/Template/link/edit.php
@@ -1,49 +1,21 @@
-<section id="link-edit-section">
-<?php use Model\Link;
-if (! isset($edit)): ?>
- <h3><?= t('Add a new link label') ?></h3>
-<?php else: ?>
<div class="page-header">
- <h2><?= t('Edit the link label') ?></h2>
+ <h2><?= t('Link modification') ?></h2>
</div>
-<?php endif ?>
-<form method="post" action="<?= $this->u('link', isset($edit) ? 'update' : 'save', array('project_id' => $project['id'], 'link_id' => @$values['id'])) ?>" autocomplete="off">
+<form action="<?= $this->u('link', 'update', array('link_id' => $link['id'])) ?>" method="post" autocomplete="off">
+
<?= $this->formCsrf() ?>
+ <?= $this->formHidden('id', $values) ?>
- <?php if (isset($edit)): ?>
- <?= $this->formHidden('link_id', $values) ?>
- <?= $this->formHidden('id[0]', $values[0]) ?>
- <?php if (isset($values[1])): ?>
- <?= $this->formHidden('id[1]', $values[1]) ?>
- <?php endif ?>
- <?php endif ?>
- <?= $this->formHidden('project_id', $values) ?>
+ <?= $this->formLabel(t('Label'), 'label') ?>
+ <?= $this->formText('label', $values, $errors, array('required')) ?>
- <?= $this->formLabel(t('Link Label'), 'label[0]') ?>
- <?= $this->formText('label[0]', $values[0], $errors, array('required', 'autofocus', 'placeholder="'.t('precedes').'"')) ?> &raquo;
-
- <?= $this->formCheckbox('behaviour[0]', t('Bidrectional link label'), Link::BEHAVIOUR_BOTH, (isset($values[0]['behaviour']) && Link::BEHAVIOUR_BOTH == $values[0]['behaviour']), 'behaviour') ?>
+ <?= $this->formLabel(t('Opposite label'), 'opposite_id') ?>
+ <?= $this->formSelect('opposite_id', $labels, $values, $errors) ?>
- <div class="link-inverse-label">
- <?= $this->formLabel(t('Link Inverse Label'), 'label[1]') ?>
- &laquo; <?= $this->formText('label[1]', isset($values[1]) ? $values[1] : $values, $errors, array('placeholder="'.t('follows').'"')) ?>
- </div>
-
<div class="form-actions">
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
- <?php if (isset($edit)): ?>
<?= t('or') ?>
- <?= $this->a(t('cancel'), 'link', 'index', array('project_id' => $project['id'])) ?>
- <?php endif ?>
- </div>
- <?php if (! isset($edit)): ?>
- <div class="alert alert-info">
- <strong><?= t('Example:') ?></strong>
- <i><?= t('#9 precedes #10') ?></i>
- <?= t('and therefore') ?>
- <i><?= t('#10 follows #9') ?></i>
+ <?= $this->a(t('cancel'), 'link', 'index') ?>
</div>
- <?php endif ?>
-</form>
-</section> \ No newline at end of file
+</form> \ No newline at end of file
diff --git a/app/Template/link/index.php b/app/Template/link/index.php
index 0c19b614..90d1c357 100644
--- a/app/Template/link/index.php
+++ b/app/Template/link/index.php
@@ -1,30 +1,33 @@
<div class="page-header">
<h2><?= t('Link labels') ?></h2>
</div>
-
-<section>
<?php if (! empty($links)): ?>
<table>
<tr>
- <th width="70%"><?= t('Link labels') ?></th>
+ <th class="column-70"><?= t('Link labels') ?></th>
<th><?= t('Actions') ?></th>
</tr>
<?php foreach ($links as $link): ?>
<tr>
- <td><?= t($this->e($link['label'])) ?><?php if (isset($link['label_inverse']) && !empty($link['label_inverse'])): ?> | <?= t($this->e($link['label_inverse'])) ?><?php endif ?></td>
+ <td>
+ <strong><?= t($link['label']) ?></strong>
+
+ <?php if (! empty($link['opposite_label'])): ?>
+ | <?= t($link['opposite_label']) ?>
+ <?php endif ?>
+ </td>
<td>
<ul>
- <?= $this->a(t('Edit'), 'link', 'edit', array('link_id' => $link['link_id'], 'project_id' => $link['project_id'])) ?>
+ <?= $this->a(t('Edit'), 'link', 'edit', array('link_id' => $link['id'])) ?>
<?= t('or') ?>
- <?= $this->a(t('Remove'), 'link', 'confirm', array('link_id' => $link['link_id'], 'project_id' => $link['project_id'])) ?>
+ <?= $this->a(t('Remove'), 'link', 'confirm', array('link_id' => $link['id'])) ?>
</ul>
</td>
</tr>
<?php endforeach ?>
</table>
<?php else: ?>
- <?= t('There is no link yet.') ?>
+ <?= t('There is no link.') ?>
<?php endif ?>
-</section>
-<?= $this->render('link/edit', array('values' => $values, 'errors' => $errors, 'project' => $project)) ?>
+<?= $this->render('link/create', array('values' => $values, 'errors' => $errors)) ?> \ No newline at end of file
diff --git a/app/Template/link/remove.php b/app/Template/link/remove.php
index d0b14b08..a802feb0 100644
--- a/app/Template/link/remove.php
+++ b/app/Template/link/remove.php
@@ -1,17 +1,15 @@
-<section id="main">
- <div class="page-header">
- <h2><?= t('Remove a link') ?></h2>
- </div>
+<div class="page-header">
+ <h2><?= t('Remove a link') ?></h2>
+</div>
- <div class="confirm">
- <p class="alert alert-info">
- <?= t('Do you really want to remove this link: "%s"?', t($link[0]['label']).(isset($link[1]['label']) ? ' | '.t($link[1]['label']) : '')) ?>
- </p>
+<div class="confirm">
+ <p class="alert alert-info">
+ <?= t('Do you really want to remove this link: "%s"?', $link['label']) ?>
+ </p>
- <div class="form-actions">
- <?= $this->a(t('Yes'), 'link', 'remove', array('project_id' => $project['id'], 'link_id' => $link[0]['link_id']), true, 'btn btn-red') ?>
- <?= t('or') ?>
- <?= $this->a(t('cancel'), 'link', 'index', array('project_id' => $project['id'])) ?>
- </div>
+ <div class="form-actions">
+ <?= $this->a(t('Yes'), 'link', 'remove', array('link_id' => $link['id']), true, 'btn btn-red') ?>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'link', 'index') ?>
</div>
-</section> \ No newline at end of file
+</div> \ No newline at end of file
diff --git a/app/Template/task/public.php b/app/Template/task/public.php
index c66b2433..d7acef9f 100644
--- a/app/Template/task/public.php
+++ b/app/Template/task/public.php
@@ -10,19 +10,19 @@
'is_public' => true
)) ?>
- <?= $this->render('subtask/show', array(
+ <?= $this->render('tasklink/show', array(
'task' => $task,
- 'subtasks' => $subtasks,
+ 'links' => $links,
+ 'project' => $project,
'not_editable' => true
)) ?>
- <?= $this->render('tasklink/show', array(
+ <?= $this->render('subtask/show', array(
'task' => $task,
- 'links' => $links,
- 'project' => $project,
+ 'subtasks' => $subtasks,
'not_editable' => true
)) ?>
-
+
<?= $this->render('task/comments', array(
'task' => $task,
'comments' => $comments,
diff --git a/app/Template/task/show.php b/app/Template/task/show.php
index f968a409..1ff2ef43 100644
--- a/app/Template/task/show.php
+++ b/app/Template/task/show.php
@@ -1,8 +1,8 @@
<?= $this->render('task/details', array('task' => $task, 'project' => $project)) ?>
<?= $this->render('task/time', array('task' => $task, 'values' => $values, 'date_format' => $date_format, 'date_formats' => $date_formats)) ?>
<?= $this->render('task/show_description', array('task' => $task)) ?>
+<?= $this->render('tasklink/show', array('task' => $task, 'links' => $links)) ?>
<?= $this->render('subtask/show', array('task' => $task, 'subtasks' => $subtasks)) ?>
<?= $this->render('task/timesheet', array('task' => $task)) ?>
-<?= $this->render('tasklink/show', array('task' => $task, 'links' => $links, 'link_list' => $link_list, 'task_list' => $task_list)) ?>
<?= $this->render('file/show', array('task' => $task, 'files' => $files)) ?>
<?= $this->render('task/comments', array('task' => $task, 'comments' => $comments, 'project' => $project)) ?>
diff --git a/app/Template/tasklink/create.php b/app/Template/tasklink/create.php
new file mode 100644
index 00000000..fb438cd8
--- /dev/null
+++ b/app/Template/tasklink/create.php
@@ -0,0 +1,27 @@
+<div class="page-header">
+ <h2><?= t('Add a new link') ?></h2>
+</div>
+
+<form action="<?= $this->u('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" autocomplete="off">
+
+ <?= $this->formCsrf() ?>
+ <?= $this->formHidden('task_id', $values) ?>
+ <?= $this->formHidden('opposite_task_id', $values) ?>
+
+ <?= $this->formLabel(t('Label'), 'link_id') ?>
+ <?= $this->formSelect('link_id', $labels, $values, $errors) ?>
+
+ <?= $this->formLabel(t('Task'), 'title') ?>
+ <?= $this->formText(
+ 'title',
+ $values,
+ $errors,
+ array('required', 'data-dst-field="opposite_task_id"', 'data-search-url="'.$this->u('app', 'autocomplete', array('exclude_task_id' => $task['id'])).'"'),
+ 'task-autocomplete') ?>
+
+ <div class="form-actions">
+ <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
+ <?= t('or') ?>
+ <?= $this->a(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
+ </div>
+</form> \ No newline at end of file
diff --git a/app/Template/tasklink/edit.php b/app/Template/tasklink/edit.php
deleted file mode 100644
index e1fcded2..00000000
--- a/app/Template/tasklink/edit.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<div class="page-header">
- <?php if (! isset($edit)): ?>
- <h2><?= t('Add a link') ?></h2>
- <?php else: ?>
- <h2><?= t('Edit a link') ?></h2>
- <?php endif ?>
-</div>
-
-<?php if (!empty($link_list)): ?>
-<form method="post" action="<?= $this->u('tasklink', isset($edit) ? 'update' : 'save', array('task_id' => $task['id'], 'link_id' => @$values['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
-
- <?= $this->formCsrf() ?>
-
- <?php if (isset($edit)): ?>
- <?= $this->formHidden('id', $values) ?>
- <?= $this->formHidden('task_link_inverse_id', $values) ?>
- <?php endif ?>
- <?= $this->formHidden('task_id', $values) ?>
-
- #<?= $task['id'] ?>
- &#160;
- <?= $this->formSelect('link_label_id', $link_list, $values, $errors, 'required autofocus') ?>
- &#160;
- #<?= $this->formNumeric('task_inverse_id', $values, $errors, array('required', 'placeholder="'.t('Task id').'"', 'title="'.t('Linked task id').'"', 'list="task_inverse_ids"')) ?>
- <?php if (!empty($task_list)): ?>
- <datalist id="task_inverse_ids">
- <select>
- <?php foreach ($task_list as $task_inverse_id => $task_inverse_title): ?>
- <option value="<?= $task_inverse_id ?>">#<?= $task_inverse_id.' '.$task_inverse_title ?></option>
- <?php endforeach ?>
- </select>
- </datalist>
- <?php endif ?>
- <br/>
-
- <?php if (! isset($edit)): ?>
- <?= $this->formCheckbox('another_link', t('Create another link'), 1, isset($values['another_link']) && $values['another_link'] == 1) ?>
- <?php endif ?>
-
- <div class="form-actions">
- <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
- <?= t('or') ?>
- <?= $this->a(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
- </div>
-</form>
-<?php else: ?>
-<div class="alert alert-info">
- <?= t('You need to add link labels to this project before to link this task to another one.') ?>
- <ul>
- <li><?= $this->a(t('Add link labels'), 'link', 'index', array('project_id' => $task['project_id'])) ?></li>
- </ul>
-</div>
-<?php endif ?>
diff --git a/app/Template/tasklink/remove.php b/app/Template/tasklink/remove.php
index 2ed87be7..9322ec24 100644
--- a/app/Template/tasklink/remove.php
+++ b/app/Template/tasklink/remove.php
@@ -4,13 +4,11 @@
<div class="confirm">
<p class="alert alert-info">
- <?= t('Do you really want to remove this link with task #%s?', $link['task_inverse_id']) ?>
- <br />
-
+ <?= t('Do you really want to remove this link with task #%d?', $link['opposite_task_id']) ?>
</p>
<div class="form-actions">
- <?= $this->a(t('Yes'), 'tasklink', 'remove', array('task_id' => $task['id'], 'link_id' => $link['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?>
+ <?= $this->a(t('Yes'), 'tasklink', 'remove', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id']), true, 'btn btn-red') ?>
<?= t('or') ?>
<?= $this->a(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</div>
diff --git a/app/Template/tasklink/show.php b/app/Template/tasklink/show.php
index ac49d070..ca4e4383 100644
--- a/app/Template/tasklink/show.php
+++ b/app/Template/tasklink/show.php
@@ -1,68 +1,41 @@
<?php if (! empty($links)): ?>
-<aside id="links" class="task-show-section">
- <div class="page-header">
- <h2><?= t('Links') ?></h2>
- </div>
-
- <table class="link-table">
- <tr>
- <th><?= t('Label') ?></th>
- <th width="70%"><?= t('Task') ?></th>
- <?php if (! isset($not_editable)): ?>
- <th><?= t('Actions') ?></th>
- <?php endif ?>
- </tr>
- <?php $previous_link = null;
- foreach ($links as $link): ?>
- <tr>
+<div class="page-header">
+ <h2><?= t('Links') ?></h2>
+</div>
+<table class="table-fixed" id="links">
+ <tr>
+ <th class="column-30"><?= t('Label') ?></th>
+ <th class="column-60"><?= t('Task') ?></th>
+ <?php if (! isset($not_editable)): ?>
+ <th><?= t('Action') ?></th>
+ <?php endif ?>
+ </tr>
+ <?php foreach ($links as $link): ?>
+ <tr>
+ <td><?= t('This task') ?> <strong><?= t($link['label']) ?></strong></td>
+ <?php if (! isset($not_editable)): ?>
<td>
- <?php if (null == $previous_link || $previous_link != $link['label']):
- $previous_link = $link['label']; ?>
- <?= t($this->e($link['label'])) ?>
- <?php endif ?>
+ <?= $this->a(
+ $this->e('#'.$link['task_id'].' - '.$link['title']),
+ 'task', 'show', array('task_id' => $link['task_id'], 'project_id' => $link['project_id']),
+ false,
+ $link['is_active'] ? '' : 'task-link-closed'
+ ) ?>
</td>
<td>
- <?php if (0 == $link['task_inverse_is_active']): ?><span class="task-closed"><?php endif ?>
- <?= $this->e($link['task_inverse_category']) ?>
- <?php if (! isset($not_editable)): ?>
- <?= $this->a('#'.$this->e($link['task_inverse_id']).' - '.trim($this->e($link['task_inverse_title'])), 'task', 'show', array('task_id' => $link['task_inverse_id'], 'project_id' => $link['task_inverse_project_id'])) ?>
- <?php else: ?>
- <?= $this->a('#'.$this->e($link['task_inverse_id']).' - '.trim($this->e($link['task_inverse_title'])), 'task', 'readonly', array('task_id' => $link['task_inverse_id'], 'project_id' => $link['task_inverse_project_id'], 'token' => $project['token'])) ?>
- <?php endif ?>
- <?php if (0 == $link['task_inverse_is_active']): ?></span><?php endif ?>
+ <?= $this->a(t('Remove'), 'tasklink', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
</td>
- <?php if (! isset($not_editable)): ?>
+ <?php else: ?>
<td>
- <ul>
- <li><?= $this->a(t('Edit'), 'tasklink', 'edit', array('task_id' => $task['id'], 'link_id' => $link['id'], 'project_id' => $task['project_id'])) ?></li>
- <li><?= $this->a(t('Remove'), 'tasklink', 'confirm', array('task_id' => $task['id'], 'link_id' => $link['id'], 'project_id' => $task['project_id'])) ?></li>
- </ul>
+ <?= $this->a(
+ $this->e('#'.$link['task_id'].' - '.$link['title']),
+ 'task', 'readonly', array('task_id' => $link['task_id'], 'token' => $project['token']),
+ false,
+ $link['is_active'] ? '' : 'task-link-closed'
+ ) ?>
</td>
- <?php endif ?>
- </tr>
- <?php endforeach ?>
- </table>
-
- <?php if (! isset($not_editable) && !empty($link_list)): ?>
- <form method="post" action="<?= $this->u('tasklink', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" autocomplete="off">
- <?= $this->formCsrf() ?>
- <?= $this->formHidden('task_id', array('task_id' => $task['id'])) ?>
- #<?= $this->e($task['id']) ?>
- &#160;
- <?= $this->formSelect('link_label_id', $link_list, array(), array(), 'required autofocus') ?>
- &#160;
- #<?= $this->formNumeric('task_inverse_id', array(), array(), array('required', 'placeholder="'.t('Task id').'"', 'title="'.t('Linked task id').'"', 'list="task_inverse_ids"')) ?>
- <?php if (!empty($task_list)): ?>
- <datalist id="task_inverse_ids">
- <select>
- <?php foreach ($task_list as $task_inverse_id => $task_inverse_title): ?>
- <option value="<?= $task_inverse_id ?>">#<?= $task_inverse_id.' '.$task_inverse_title ?></option>
- <?php endforeach ?>
- </select>
- </datalist>
<?php endif ?>
- <input type="submit" value="<?= t('Add') ?>" class="btn btn-blue"/>
- </form>
- <?php endif ?>
-</aside>
-<?php endif ?>
+ </tr>
+ <?php endforeach ?>
+</table>
+<?php endif ?> \ No newline at end of file