Refactoring/simplification of the pull-request about links
|
|
@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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').' > '.t('Board settings'),
|
||||
)));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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').' > '.t('Board\'s links settings'),
|
||||
'title' => t('Settings').' > '.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'));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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'].' > '.$task['title'],
|
||||
|
|
|
|||
|
|
@ -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,69 +61,23 @@ 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.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
@ -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'])));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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' => '',
|
||||
);
|
||||
|
|
|
|||
|
|
@ -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'),
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
const TABLE = 'links';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param string $label
|
||||
* @return array
|
||||
*/
|
||||
public function getByLabel($label)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('label', $label)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opposite link id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id Link id
|
||||
* @return integer
|
||||
*/
|
||||
public function getInverseLinkLabelId($link_label_id)
|
||||
public function getOppositeLinkId($link_id)
|
||||
{
|
||||
$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);
|
||||
$link = $this->getById($link_id);
|
||||
return $link['opposite_id'] ?: $link_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all link labels for a given project
|
||||
* Get all links
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* Specify a project id. Default: -1 to target all projects
|
||||
* @return array
|
||||
*/
|
||||
public function getLinkLabels($project_id = -1)
|
||||
public function getAll()
|
||||
{
|
||||
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();
|
||||
return $this->db->table(self::TABLE)->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all link labels
|
||||
* Used to select a link label
|
||||
* Get merged 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 getMergedList()
|
||||
{
|
||||
$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 = ' »';
|
||||
}
|
||||
if (self::BEHAVIOUR_BOTH == $link['behaviour'] || self::BEHAVIOUR_RIGHT2LEFT == $link['behaviour']) {
|
||||
$prefix = '« ';
|
||||
}
|
||||
$mergedListing[$link['id']] = $prefix . t($link['label']) . $suffix;
|
||||
}
|
||||
$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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all links (label + inverse label)
|
||||
* Get label list
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id
|
||||
* Specify a project id. Default: -1 to target all projects
|
||||
* @param integer $exclude_id Exclude this link
|
||||
* @param booelan $prepend Prepend default value
|
||||
* @return array
|
||||
*/
|
||||
public function getMergedList($project_id = -1)
|
||||
public function getList($exclude_id = 0, $prepend = true)
|
||||
{
|
||||
$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;
|
||||
}
|
||||
$labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label');
|
||||
|
||||
foreach ($labels as &$value) {
|
||||
$value = t($value);
|
||||
}
|
||||
$listing = $mergedListing;
|
||||
return $listing;
|
||||
|
||||
return $prepend ? array('') + $labels : $labels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before insert/update
|
||||
* Create a new link label
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* Form values
|
||||
* @param string $label
|
||||
* @param string $opposite_label
|
||||
* @return boolean
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
public function create($label, $opposite_label = '')
|
||||
{
|
||||
// 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'];
|
||||
}
|
||||
|
||||
$values = $link;
|
||||
$values[] = $label1;
|
||||
$values[] = $label2;
|
||||
return array(
|
||||
$link,
|
||||
$label1,
|
||||
$label2
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a link
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* Form values
|
||||
* @return bool integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
list ($link, $label1, $label2) = $this->prepare($values);
|
||||
// Create link
|
||||
$this->db->startTransaction();
|
||||
$res = $this->db->table(self::TABLE)->save($link);
|
||||
if (! $res) {
|
||||
|
||||
if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) {
|
||||
$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) {
|
||||
$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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the opposite label (executed inside create() method)
|
||||
*
|
||||
* @access private
|
||||
* @param string $label
|
||||
*/
|
||||
private function createOpposite($label)
|
||||
{
|
||||
$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()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a link
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* Form values
|
||||
* @return bool
|
||||
* @param array $values
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
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;
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $values['id'])
|
||||
->update(array(
|
||||
'label' => $values['label'],
|
||||
'opposite_id' => $values['opposite_id'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a link
|
||||
* Remove a link a the relation to its opposite
|
||||
*
|
||||
* @access public
|
||||
* @param integer $link_id
|
||||
* Link id
|
||||
* @return bool
|
||||
* @param integer $link_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($link_id)
|
||||
{
|
||||
$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;
|
||||
$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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate links from a project to another one, must be executed inside a transaction
|
||||
*
|
||||
* @param integer $src_project_id
|
||||
* Source project id
|
||||
* @return integer $dst_project_id Destination project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($src_project_id, $dst_project_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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -337,6 +337,23 @@ class ProjectPermission extends Base
|
|||
->findAllByColumn('projects.id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
public function getInverseTaskLinkId($task_link_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 all links for a given task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id
|
||||
* Task id
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($task_id)
|
||||
public function getLinks($task_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
|
||||
));
|
||||
$res = $rq->fetchAll(PDO::FETCH_ASSOC);
|
||||
return $res;
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before insert/update
|
||||
* Create a new link
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* Form values
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $opposite_task_id Opposite task id
|
||||
* @param integer $link_id Link id
|
||||
* @return boolean
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
public function create($task_id, $opposite_task_id, $link_id)
|
||||
{
|
||||
$this->removeFields($values, array(
|
||||
'another_link'
|
||||
));
|
||||
$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;
|
||||
}
|
||||
|
||||
// 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,
|
||||
));
|
||||
|
||||
$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,
|
||||
));
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a link
|
||||
* Remove a link between two tasks
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
|
||||
$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();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate all links to another task
|
||||
* Validate creation
|
||||
*
|
||||
* @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;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* @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(
|
||||
$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)
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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'");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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') ?>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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').'"')) ?> »
|
||||
|
||||
<?= $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]') ?>
|
||||
« <?= $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 ?>
|
||||
<?= $this->a(t('cancel'), 'link', 'index') ?>
|
||||
</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>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</form>
|
||||
</section>
|
||||
</form>
|
||||
|
|
@ -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)) ?>
|
||||
|
|
@ -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>
|
||||
</div>
|
||||
|
|
@ -10,19 +10,19 @@
|
|||
'is_public' => true
|
||||
)) ?>
|
||||
|
||||
<?= $this->render('subtask/show', array(
|
||||
'task' => $task,
|
||||
'subtasks' => $subtasks,
|
||||
'not_editable' => true
|
||||
)) ?>
|
||||
|
||||
<?= $this->render('tasklink/show', array(
|
||||
'task' => $task,
|
||||
'links' => $links,
|
||||
'project' => $project,
|
||||
'not_editable' => true
|
||||
)) ?>
|
||||
|
||||
|
||||
<?= $this->render('subtask/show', array(
|
||||
'task' => $task,
|
||||
'subtasks' => $subtasks,
|
||||
'not_editable' => true
|
||||
)) ?>
|
||||
|
||||
<?= $this->render('task/comments', array(
|
||||
'task' => $task,
|
||||
'comments' => $comments,
|
||||
|
|
|
|||
|
|
@ -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)) ?>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
@ -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'] ?>
|
||||
 
|
||||
<?= $this->formSelect('link_label_id', $link_list, $values, $errors, 'required autofocus') ?>
|
||||
 
|
||||
#<?= $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 ?>
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
<td>
|
||||
<?php if (null == $previous_link || $previous_link != $link['label']):
|
||||
$previous_link = $link['label']; ?>
|
||||
<?= t($this->e($link['label'])) ?>
|
||||
<?php endif ?>
|
||||
</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 ?>
|
||||
</td>
|
||||
<?php if (! isset($not_editable)): ?>
|
||||
<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>
|
||||
</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']) ?>
|
||||
 
|
||||
<?= $this->formSelect('link_label_id', $link_list, array(), array(), 'required autofocus') ?>
|
||||
 
|
||||
#<?= $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>
|
||||
<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 ?>
|
||||
<input type="submit" value="<?= t('Add') ?>" class="btn btn-blue"/>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
</aside>
|
||||
<?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>
|
||||
<?= $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>
|
||||
<?= $this->a(t('Remove'), 'tasklink', 'confirm', array('link_id' => $link['id'], 'task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>
|
||||
</td>
|
||||
<?php else: ?>
|
||||
<td>
|
||||
<?= $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 endif ?>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 212 B After Width: | Height: | Size: 212 B |
|
Before Width: | Height: | Size: 208 B After Width: | Height: | Size: 208 B |
|
Before Width: | Height: | Size: 335 B After Width: | Height: | Size: 335 B |
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 207 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 332 B After Width: | Height: | Size: 332 B |
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.4 KiB |
|
|
@ -51,4 +51,10 @@ a.btn-blue:focus,
|
|||
.btn-blue:focus {
|
||||
border-color: #2f5bb7;
|
||||
background: #357ae8;
|
||||
}
|
||||
}
|
||||
|
||||
.btn-blue:disabled {
|
||||
color: #ccc;
|
||||
border: 1px solid #ccc;
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,6 @@ textarea {
|
|||
|
||||
select {
|
||||
max-width: 95%;
|
||||
paddin-top: 5px;
|
||||
paddin-bottom: 5px;
|
||||
}
|
||||
|
||||
::-webkit-input-placeholder {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ a.task-board-nobody {
|
|||
.task-board-date {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
left: 1px;
|
||||
font-weight: bold;
|
||||
color: #000;
|
||||
}
|
||||
|
|
@ -226,6 +226,6 @@ a.task-board-nobody {
|
|||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
|
||||
.task-closed {
|
||||
.task-link-closed {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,9 +64,10 @@ div.ui-tooltip {
|
|||
width: 550px;
|
||||
}
|
||||
|
||||
.tooltip-tasklinks ul{
|
||||
padding-left: 13px;
|
||||
.tooltip-tasklinks li {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.column-tooltip {
|
||||
color: #999;
|
||||
font-size: 0.95em;
|
||||
|
|
|
|||
|
|
@ -1,661 +0,0 @@
|
|||
/*! jQuery UI - v1.10.4 - 2014-05-29
|
||||
* http://jqueryui.com
|
||||
* Includes: jquery.ui.core.css, jquery.ui.datepicker.css, jquery.ui.tooltip.css, jquery.ui.theme.css
|
||||
* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
|
||||
* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
|
||||
|
||||
/* Layout helpers
|
||||
----------------------------------*/
|
||||
.ui-helper-hidden {
|
||||
display: none;
|
||||
}
|
||||
.ui-helper-hidden-accessible {
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
height: 1px;
|
||||
margin: -1px;
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
}
|
||||
.ui-helper-reset {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
line-height: 1.3;
|
||||
text-decoration: none;
|
||||
font-size: 100%;
|
||||
list-style: none;
|
||||
}
|
||||
.ui-helper-clearfix:before,
|
||||
.ui-helper-clearfix:after {
|
||||
content: "";
|
||||
display: table;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.ui-helper-clearfix:after {
|
||||
clear: both;
|
||||
}
|
||||
.ui-helper-clearfix {
|
||||
min-height: 0; /* support: IE7 */
|
||||
}
|
||||
.ui-helper-zfix {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
filter:Alpha(Opacity=0);
|
||||
}
|
||||
|
||||
.ui-front {
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-disabled {
|
||||
cursor: default !important;
|
||||
}
|
||||
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
display: block;
|
||||
text-indent: -99999px;
|
||||
overflow: hidden;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.ui-datepicker {
|
||||
width: 17em;
|
||||
padding: .2em .2em 0;
|
||||
display: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-header {
|
||||
position: relative;
|
||||
padding: .2em 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev,
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
width: 1.8em;
|
||||
height: 1.8em;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover,
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
top: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev {
|
||||
left: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next {
|
||||
right: 2px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev-hover {
|
||||
left: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-next-hover {
|
||||
right: 1px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-prev span,
|
||||
.ui-datepicker .ui-datepicker-next span {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
margin-left: -8px;
|
||||
top: 50%;
|
||||
margin-top: -8px;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title {
|
||||
margin: 0 2.3em;
|
||||
line-height: 1.8em;
|
||||
text-align: center;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-title select {
|
||||
font-size: 1em;
|
||||
margin: 1px 0;
|
||||
}
|
||||
.ui-datepicker select.ui-datepicker-month,
|
||||
.ui-datepicker select.ui-datepicker-year {
|
||||
width: 49%;
|
||||
}
|
||||
.ui-datepicker table {
|
||||
width: 100%;
|
||||
font-size: .9em;
|
||||
border-collapse: collapse;
|
||||
margin: 0 0 .4em;
|
||||
}
|
||||
.ui-datepicker th {
|
||||
padding: .7em .3em;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
}
|
||||
.ui-datepicker td {
|
||||
border: 0;
|
||||
padding: 1px;
|
||||
}
|
||||
.ui-datepicker td span,
|
||||
.ui-datepicker td a {
|
||||
display: block;
|
||||
padding: .2em;
|
||||
text-align: right;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane {
|
||||
background-image: none;
|
||||
margin: .7em 0 0 0;
|
||||
padding: 0 .2em;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button {
|
||||
float: right;
|
||||
margin: .5em .2em .4em;
|
||||
cursor: pointer;
|
||||
padding: .2em .6em .3em .6em;
|
||||
width: auto;
|
||||
overflow: visible;
|
||||
}
|
||||
.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
|
||||
float: left;
|
||||
}
|
||||
|
||||
/* with multiple calendars */
|
||||
.ui-datepicker.ui-datepicker-multi {
|
||||
width: auto;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group table {
|
||||
width: 95%;
|
||||
margin: 0 auto .4em;
|
||||
}
|
||||
.ui-datepicker-multi-2 .ui-datepicker-group {
|
||||
width: 50%;
|
||||
}
|
||||
.ui-datepicker-multi-3 .ui-datepicker-group {
|
||||
width: 33.3%;
|
||||
}
|
||||
.ui-datepicker-multi-4 .ui-datepicker-group {
|
||||
width: 25%;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-left-width: 0;
|
||||
}
|
||||
.ui-datepicker-multi .ui-datepicker-buttonpane {
|
||||
clear: left;
|
||||
}
|
||||
.ui-datepicker-row-break {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
/* RTL support */
|
||||
.ui-datepicker-rtl {
|
||||
direction: rtl;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev {
|
||||
right: 2px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next {
|
||||
left: 2px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-prev:hover {
|
||||
right: 1px;
|
||||
left: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-next:hover {
|
||||
left: 1px;
|
||||
right: auto;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane {
|
||||
clear: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button {
|
||||
float: left;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
|
||||
.ui-datepicker-rtl .ui-datepicker-group {
|
||||
float: right;
|
||||
}
|
||||
.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
|
||||
.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
|
||||
border-right-width: 0;
|
||||
border-left-width: 1px;
|
||||
}
|
||||
.ui-tooltip {
|
||||
padding: 8px;
|
||||
position: absolute;
|
||||
z-index: 9999;
|
||||
max-width: 300px;
|
||||
-webkit-box-shadow: 0 0 5px #aaa;
|
||||
box-shadow: 0 0 5px #aaa;
|
||||
}
|
||||
body .ui-tooltip {
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
/* Component containers
|
||||
----------------------------------*/
|
||||
.ui-widget {
|
||||
font-family: Verdana,Arial,sans-serif;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
.ui-widget .ui-widget {
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget input,
|
||||
.ui-widget select,
|
||||
.ui-widget textarea,
|
||||
.ui-widget button {
|
||||
font-family: Verdana,Arial,sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
.ui-widget-content {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #ffffff url("images/ui-bg_flat_75_ffffff_40x100.png") 50% 50% repeat-x;
|
||||
color: #222222;
|
||||
}
|
||||
.ui-widget-content a {
|
||||
color: #222222;
|
||||
}
|
||||
.ui-widget-header {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #cccccc url("images/ui-bg_highlight-soft_75_cccccc_1x100.png") 50% 50% repeat-x;
|
||||
color: #222222;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-widget-header a {
|
||||
color: #222222;
|
||||
}
|
||||
|
||||
/* Interaction states
|
||||
----------------------------------*/
|
||||
.ui-state-default,
|
||||
.ui-widget-content .ui-state-default,
|
||||
.ui-widget-header .ui-state-default {
|
||||
border: 1px solid #d3d3d3;
|
||||
background: #e6e6e6 url("images/ui-bg_glass_75_e6e6e6_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #555555;
|
||||
}
|
||||
.ui-state-default a,
|
||||
.ui-state-default a:link,
|
||||
.ui-state-default a:visited {
|
||||
color: #555555;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-hover,
|
||||
.ui-widget-content .ui-state-hover,
|
||||
.ui-widget-header .ui-state-hover,
|
||||
.ui-state-focus,
|
||||
.ui-widget-content .ui-state-focus,
|
||||
.ui-widget-header .ui-state-focus {
|
||||
border: 1px solid #999999;
|
||||
background: #dadada url("images/ui-bg_glass_75_dadada_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #212121;
|
||||
}
|
||||
.ui-state-hover a,
|
||||
.ui-state-hover a:hover,
|
||||
.ui-state-hover a:link,
|
||||
.ui-state-hover a:visited,
|
||||
.ui-state-focus a,
|
||||
.ui-state-focus a:hover,
|
||||
.ui-state-focus a:link,
|
||||
.ui-state-focus a:visited {
|
||||
color: #212121;
|
||||
text-decoration: none;
|
||||
}
|
||||
.ui-state-active,
|
||||
.ui-widget-content .ui-state-active,
|
||||
.ui-widget-header .ui-state-active {
|
||||
border: 1px solid #aaaaaa;
|
||||
background: #ffffff url("images/ui-bg_glass_65_ffffff_1x400.png") 50% 50% repeat-x;
|
||||
font-weight: normal;
|
||||
color: #212121;
|
||||
}
|
||||
.ui-state-active a,
|
||||
.ui-state-active a:link,
|
||||
.ui-state-active a:visited {
|
||||
color: #212121;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
/* Interaction Cues
|
||||
----------------------------------*/
|
||||
.ui-state-highlight,
|
||||
.ui-widget-content .ui-state-highlight,
|
||||
.ui-widget-header .ui-state-highlight {
|
||||
border: 1px solid #fcefa1;
|
||||
background: #fbf9ee url("images/ui-bg_glass_55_fbf9ee_1x400.png") 50% 50% repeat-x;
|
||||
color: #363636;
|
||||
}
|
||||
.ui-state-highlight a,
|
||||
.ui-widget-content .ui-state-highlight a,
|
||||
.ui-widget-header .ui-state-highlight a {
|
||||
color: #363636;
|
||||
}
|
||||
.ui-state-error,
|
||||
.ui-widget-content .ui-state-error,
|
||||
.ui-widget-header .ui-state-error {
|
||||
border: 1px solid #cd0a0a;
|
||||
background: #fef1ec url("images/ui-bg_glass_95_fef1ec_1x400.png") 50% 50% repeat-x;
|
||||
color: #cd0a0a;
|
||||
}
|
||||
.ui-state-error a,
|
||||
.ui-widget-content .ui-state-error a,
|
||||
.ui-widget-header .ui-state-error a {
|
||||
color: #cd0a0a;
|
||||
}
|
||||
.ui-state-error-text,
|
||||
.ui-widget-content .ui-state-error-text,
|
||||
.ui-widget-header .ui-state-error-text {
|
||||
color: #cd0a0a;
|
||||
}
|
||||
.ui-priority-primary,
|
||||
.ui-widget-content .ui-priority-primary,
|
||||
.ui-widget-header .ui-priority-primary {
|
||||
font-weight: bold;
|
||||
}
|
||||
.ui-priority-secondary,
|
||||
.ui-widget-content .ui-priority-secondary,
|
||||
.ui-widget-header .ui-priority-secondary {
|
||||
opacity: .7;
|
||||
filter:Alpha(Opacity=70);
|
||||
font-weight: normal;
|
||||
}
|
||||
.ui-state-disabled,
|
||||
.ui-widget-content .ui-state-disabled,
|
||||
.ui-widget-header .ui-state-disabled {
|
||||
opacity: .35;
|
||||
filter:Alpha(Opacity=35);
|
||||
background-image: none;
|
||||
}
|
||||
.ui-state-disabled .ui-icon {
|
||||
filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
|
||||
}
|
||||
|
||||
/* Icons
|
||||
----------------------------------*/
|
||||
|
||||
/* states and images */
|
||||
.ui-icon {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.ui-icon,
|
||||
.ui-widget-content .ui-icon {
|
||||
background-image: url("images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
.ui-widget-header .ui-icon {
|
||||
background-image: url("images/ui-icons_222222_256x240.png");
|
||||
}
|
||||
.ui-state-default .ui-icon {
|
||||
background-image: url("images/ui-icons_888888_256x240.png");
|
||||
}
|
||||
.ui-state-hover .ui-icon,
|
||||
.ui-state-focus .ui-icon {
|
||||
background-image: url("images/ui-icons_454545_256x240.png");
|
||||
}
|
||||
.ui-state-active .ui-icon {
|
||||
background-image: url("images/ui-icons_454545_256x240.png");
|
||||
}
|
||||
.ui-state-highlight .ui-icon {
|
||||
background-image: url("images/ui-icons_2e83ff_256x240.png");
|
||||
}
|
||||
.ui-state-error .ui-icon,
|
||||
.ui-state-error-text .ui-icon {
|
||||
background-image: url("images/ui-icons_cd0a0a_256x240.png");
|
||||
}
|
||||
|
||||
/* positioning */
|
||||
.ui-icon-blank { background-position: 16px 16px; }
|
||||
.ui-icon-carat-1-n { background-position: 0 0; }
|
||||
.ui-icon-carat-1-ne { background-position: -16px 0; }
|
||||
.ui-icon-carat-1-e { background-position: -32px 0; }
|
||||
.ui-icon-carat-1-se { background-position: -48px 0; }
|
||||
.ui-icon-carat-1-s { background-position: -64px 0; }
|
||||
.ui-icon-carat-1-sw { background-position: -80px 0; }
|
||||
.ui-icon-carat-1-w { background-position: -96px 0; }
|
||||
.ui-icon-carat-1-nw { background-position: -112px 0; }
|
||||
.ui-icon-carat-2-n-s { background-position: -128px 0; }
|
||||
.ui-icon-carat-2-e-w { background-position: -144px 0; }
|
||||
.ui-icon-triangle-1-n { background-position: 0 -16px; }
|
||||
.ui-icon-triangle-1-ne { background-position: -16px -16px; }
|
||||
.ui-icon-triangle-1-e { background-position: -32px -16px; }
|
||||
.ui-icon-triangle-1-se { background-position: -48px -16px; }
|
||||
.ui-icon-triangle-1-s { background-position: -64px -16px; }
|
||||
.ui-icon-triangle-1-sw { background-position: -80px -16px; }
|
||||
.ui-icon-triangle-1-w { background-position: -96px -16px; }
|
||||
.ui-icon-triangle-1-nw { background-position: -112px -16px; }
|
||||
.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
|
||||
.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
|
||||
.ui-icon-arrow-1-n { background-position: 0 -32px; }
|
||||
.ui-icon-arrow-1-ne { background-position: -16px -32px; }
|
||||
.ui-icon-arrow-1-e { background-position: -32px -32px; }
|
||||
.ui-icon-arrow-1-se { background-position: -48px -32px; }
|
||||
.ui-icon-arrow-1-s { background-position: -64px -32px; }
|
||||
.ui-icon-arrow-1-sw { background-position: -80px -32px; }
|
||||
.ui-icon-arrow-1-w { background-position: -96px -32px; }
|
||||
.ui-icon-arrow-1-nw { background-position: -112px -32px; }
|
||||
.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
|
||||
.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
|
||||
.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
|
||||
.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
|
||||
.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
|
||||
.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
|
||||
.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
|
||||
.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
|
||||
.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
|
||||
.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
|
||||
.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
|
||||
.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
|
||||
.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
|
||||
.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
|
||||
.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
|
||||
.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
|
||||
.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
|
||||
.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
|
||||
.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
|
||||
.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
|
||||
.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
|
||||
.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
|
||||
.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
|
||||
.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
|
||||
.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
|
||||
.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
|
||||
.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
|
||||
.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
|
||||
.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
|
||||
.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
|
||||
.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
|
||||
.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
|
||||
.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
|
||||
.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
|
||||
.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
|
||||
.ui-icon-arrow-4 { background-position: 0 -80px; }
|
||||
.ui-icon-arrow-4-diag { background-position: -16px -80px; }
|
||||
.ui-icon-extlink { background-position: -32px -80px; }
|
||||
.ui-icon-newwin { background-position: -48px -80px; }
|
||||
.ui-icon-refresh { background-position: -64px -80px; }
|
||||
.ui-icon-shuffle { background-position: -80px -80px; }
|
||||
.ui-icon-transfer-e-w { background-position: -96px -80px; }
|
||||
.ui-icon-transferthick-e-w { background-position: -112px -80px; }
|
||||
.ui-icon-folder-collapsed { background-position: 0 -96px; }
|
||||
.ui-icon-folder-open { background-position: -16px -96px; }
|
||||
.ui-icon-document { background-position: -32px -96px; }
|
||||
.ui-icon-document-b { background-position: -48px -96px; }
|
||||
.ui-icon-note { background-position: -64px -96px; }
|
||||
.ui-icon-mail-closed { background-position: -80px -96px; }
|
||||
.ui-icon-mail-open { background-position: -96px -96px; }
|
||||
.ui-icon-suitcase { background-position: -112px -96px; }
|
||||
.ui-icon-comment { background-position: -128px -96px; }
|
||||
.ui-icon-person { background-position: -144px -96px; }
|
||||
.ui-icon-print { background-position: -160px -96px; }
|
||||
.ui-icon-trash { background-position: -176px -96px; }
|
||||
.ui-icon-locked { background-position: -192px -96px; }
|
||||
.ui-icon-unlocked { background-position: -208px -96px; }
|
||||
.ui-icon-bookmark { background-position: -224px -96px; }
|
||||
.ui-icon-tag { background-position: -240px -96px; }
|
||||
.ui-icon-home { background-position: 0 -112px; }
|
||||
.ui-icon-flag { background-position: -16px -112px; }
|
||||
.ui-icon-calendar { background-position: -32px -112px; }
|
||||
.ui-icon-cart { background-position: -48px -112px; }
|
||||
.ui-icon-pencil { background-position: -64px -112px; }
|
||||
.ui-icon-clock { background-position: -80px -112px; }
|
||||
.ui-icon-disk { background-position: -96px -112px; }
|
||||
.ui-icon-calculator { background-position: -112px -112px; }
|
||||
.ui-icon-zoomin { background-position: -128px -112px; }
|
||||
.ui-icon-zoomout { background-position: -144px -112px; }
|
||||
.ui-icon-search { background-position: -160px -112px; }
|
||||
.ui-icon-wrench { background-position: -176px -112px; }
|
||||
.ui-icon-gear { background-position: -192px -112px; }
|
||||
.ui-icon-heart { background-position: -208px -112px; }
|
||||
.ui-icon-star { background-position: -224px -112px; }
|
||||
.ui-icon-link { background-position: -240px -112px; }
|
||||
.ui-icon-cancel { background-position: 0 -128px; }
|
||||
.ui-icon-plus { background-position: -16px -128px; }
|
||||
.ui-icon-plusthick { background-position: -32px -128px; }
|
||||
.ui-icon-minus { background-position: -48px -128px; }
|
||||
.ui-icon-minusthick { background-position: -64px -128px; }
|
||||
.ui-icon-close { background-position: -80px -128px; }
|
||||
.ui-icon-closethick { background-position: -96px -128px; }
|
||||
.ui-icon-key { background-position: -112px -128px; }
|
||||
.ui-icon-lightbulb { background-position: -128px -128px; }
|
||||
.ui-icon-scissors { background-position: -144px -128px; }
|
||||
.ui-icon-clipboard { background-position: -160px -128px; }
|
||||
.ui-icon-copy { background-position: -176px -128px; }
|
||||
.ui-icon-contact { background-position: -192px -128px; }
|
||||
.ui-icon-image { background-position: -208px -128px; }
|
||||
.ui-icon-video { background-position: -224px -128px; }
|
||||
.ui-icon-script { background-position: -240px -128px; }
|
||||
.ui-icon-alert { background-position: 0 -144px; }
|
||||
.ui-icon-info { background-position: -16px -144px; }
|
||||
.ui-icon-notice { background-position: -32px -144px; }
|
||||
.ui-icon-help { background-position: -48px -144px; }
|
||||
.ui-icon-check { background-position: -64px -144px; }
|
||||
.ui-icon-bullet { background-position: -80px -144px; }
|
||||
.ui-icon-radio-on { background-position: -96px -144px; }
|
||||
.ui-icon-radio-off { background-position: -112px -144px; }
|
||||
.ui-icon-pin-w { background-position: -128px -144px; }
|
||||
.ui-icon-pin-s { background-position: -144px -144px; }
|
||||
.ui-icon-play { background-position: 0 -160px; }
|
||||
.ui-icon-pause { background-position: -16px -160px; }
|
||||
.ui-icon-seek-next { background-position: -32px -160px; }
|
||||
.ui-icon-seek-prev { background-position: -48px -160px; }
|
||||
.ui-icon-seek-end { background-position: -64px -160px; }
|
||||
.ui-icon-seek-start { background-position: -80px -160px; }
|
||||
/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
|
||||
.ui-icon-seek-first { background-position: -80px -160px; }
|
||||
.ui-icon-stop { background-position: -96px -160px; }
|
||||
.ui-icon-eject { background-position: -112px -160px; }
|
||||
.ui-icon-volume-off { background-position: -128px -160px; }
|
||||
.ui-icon-volume-on { background-position: -144px -160px; }
|
||||
.ui-icon-power { background-position: 0 -176px; }
|
||||
.ui-icon-signal-diag { background-position: -16px -176px; }
|
||||
.ui-icon-signal { background-position: -32px -176px; }
|
||||
.ui-icon-battery-0 { background-position: -48px -176px; }
|
||||
.ui-icon-battery-1 { background-position: -64px -176px; }
|
||||
.ui-icon-battery-2 { background-position: -80px -176px; }
|
||||
.ui-icon-battery-3 { background-position: -96px -176px; }
|
||||
.ui-icon-circle-plus { background-position: 0 -192px; }
|
||||
.ui-icon-circle-minus { background-position: -16px -192px; }
|
||||
.ui-icon-circle-close { background-position: -32px -192px; }
|
||||
.ui-icon-circle-triangle-e { background-position: -48px -192px; }
|
||||
.ui-icon-circle-triangle-s { background-position: -64px -192px; }
|
||||
.ui-icon-circle-triangle-w { background-position: -80px -192px; }
|
||||
.ui-icon-circle-triangle-n { background-position: -96px -192px; }
|
||||
.ui-icon-circle-arrow-e { background-position: -112px -192px; }
|
||||
.ui-icon-circle-arrow-s { background-position: -128px -192px; }
|
||||
.ui-icon-circle-arrow-w { background-position: -144px -192px; }
|
||||
.ui-icon-circle-arrow-n { background-position: -160px -192px; }
|
||||
.ui-icon-circle-zoomin { background-position: -176px -192px; }
|
||||
.ui-icon-circle-zoomout { background-position: -192px -192px; }
|
||||
.ui-icon-circle-check { background-position: -208px -192px; }
|
||||
.ui-icon-circlesmall-plus { background-position: 0 -208px; }
|
||||
.ui-icon-circlesmall-minus { background-position: -16px -208px; }
|
||||
.ui-icon-circlesmall-close { background-position: -32px -208px; }
|
||||
.ui-icon-squaresmall-plus { background-position: -48px -208px; }
|
||||
.ui-icon-squaresmall-minus { background-position: -64px -208px; }
|
||||
.ui-icon-squaresmall-close { background-position: -80px -208px; }
|
||||
.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
|
||||
.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
|
||||
.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
|
||||
.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
|
||||
.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
|
||||
.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
|
||||
|
||||
|
||||
/* Misc visuals
|
||||
----------------------------------*/
|
||||
|
||||
/* Corner radius */
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-left,
|
||||
.ui-corner-tl {
|
||||
border-top-left-radius: 4px;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-top,
|
||||
.ui-corner-right,
|
||||
.ui-corner-tr {
|
||||
border-top-right-radius: 4px;
|
||||
}jquery.ui.tooltip.css,
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-left,
|
||||
.ui-corner-bl {
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
.ui-corner-all,
|
||||
.ui-corner-bottom,
|
||||
.ui-corner-right,
|
||||
.ui-corner-br {
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
/* Overlays */
|
||||
.ui-widget-overlay {
|
||||
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
opacity: .3;
|
||||
filter: Alpha(Opacity=30);
|
||||
}
|
||||
.ui-widget-shadow {
|
||||
margin: -8px 0 0 -8px;
|
||||
padding: 8px;
|
||||
background: #aaaaaa url("images/ui-bg_flat_0_aaaaaa_40x100.png") 50% 50% repeat-x;
|
||||
opacity: .3;
|
||||
filter: Alpha(Opacity=30);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
Kanboard.Link = (function() {
|
||||
|
||||
function on_change() {
|
||||
if ($('.behaviour').prop('checked')) {
|
||||
$('.link-inverse-label').hide();
|
||||
}
|
||||
else {
|
||||
$('.link-inverse-label').show();
|
||||
}
|
||||
}
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
if (Kanboard.Exists("link-edit-section")) {
|
||||
on_change();
|
||||
$(".behaviour").click(on_change);
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
Kanboard.Task = (function() {
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
|
||||
if ($(".task-autocomplete").length) {
|
||||
|
||||
$("input[type=submit]").attr('disabled','disabled');
|
||||
|
||||
$(".task-autocomplete").autocomplete({
|
||||
source: $(".task-autocomplete").data("search-url"),
|
||||
minLength: 2,
|
||||
select: function(event, ui) {
|
||||
var field = $(".task-autocomplete").data("dst-field");
|
||||
$("input[name=" + field + "]").val(ui.item.id);
|
||||
|
||||
$("input[type=submit]").removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
})();
|
||||
|
|
@ -1,27 +1,27 @@
|
|||
{
|
||||
"require" : {
|
||||
"ext-mbstring" : "*",
|
||||
"fguillot/simple-validator" : "0.0.2",
|
||||
"swiftmailer/swiftmailer" : "@stable",
|
||||
"fguillot/json-rpc" : "0.0.1",
|
||||
"fguillot/picodb" : "dev-master",
|
||||
"erusev/parsedown" : "1.5.1",
|
||||
"lusitanian/oauth" : "0.3.5",
|
||||
"pimple/pimple" : "~3.0",
|
||||
"symfony/console" : "@stable",
|
||||
"symfony/event-dispatcher" : "~2.6",
|
||||
"fguillot/simpleLogger" : "0.0.1"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-0" : {
|
||||
"" : "app/"
|
||||
},
|
||||
"files" : [
|
||||
"app/functions.php",
|
||||
"app/Libs/password.php"
|
||||
]
|
||||
},
|
||||
"require-dev" : {
|
||||
"symfony/stopwatch" : "~2.6"
|
||||
}
|
||||
"require" : {
|
||||
"ext-mbstring" : "*",
|
||||
"fguillot/simple-validator" : "dev-master",
|
||||
"swiftmailer/swiftmailer" : "@stable",
|
||||
"fguillot/json-rpc" : "0.0.1",
|
||||
"fguillot/picodb" : "dev-master",
|
||||
"erusev/parsedown" : "1.5.1",
|
||||
"lusitanian/oauth" : "0.3.5",
|
||||
"pimple/pimple" : "~3.0",
|
||||
"symfony/console" : "@stable",
|
||||
"symfony/event-dispatcher" : "~2.6",
|
||||
"fguillot/simpleLogger" : "0.0.1"
|
||||
},
|
||||
"autoload" : {
|
||||
"psr-0" : {
|
||||
"" : "app/"
|
||||
},
|
||||
"files" : [
|
||||
"app/functions.php",
|
||||
"app/Libs/password.php"
|
||||
]
|
||||
},
|
||||
"require-dev" : {
|
||||
"symfony/stopwatch" : "~2.6"
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"hash": "7ccc1bcdeca71cacef30015aca5a483a",
|
||||
"hash": "9c0c8b06745d54478604d80815b59b9b",
|
||||
"packages": [
|
||||
{
|
||||
"name": "erusev/parsedown",
|
||||
|
|
@ -121,16 +121,16 @@
|
|||
},
|
||||
{
|
||||
"name": "fguillot/simple-validator",
|
||||
"version": "v0.0.2",
|
||||
"version": "dev-master",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fguillot/simpleValidator.git",
|
||||
"reference": "9e9502c88ce239901c0fee12a1f504948342ab42"
|
||||
"reference": "5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/9e9502c88ce239901c0fee12a1f504948342ab42",
|
||||
"reference": "9e9502c88ce239901c0fee12a1f504948342ab42",
|
||||
"url": "https://api.github.com/repos/fguillot/simpleValidator/zipball/5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7",
|
||||
"reference": "5ebdb6df4c5f3aa2539b633eb4ae94c9e8c4ada7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -154,7 +154,7 @@
|
|||
],
|
||||
"description": "The most easy to use validator library for PHP :)",
|
||||
"homepage": "https://github.com/fguillot/simpleValidator",
|
||||
"time": "2015-01-21 02:00:33"
|
||||
"time": "2015-02-14 21:04:14"
|
||||
},
|
||||
{
|
||||
"name": "fguillot/simpleLogger",
|
||||
|
|
@ -559,12 +559,12 @@
|
|||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": {
|
||||
"fguillot/simple-validator": 20,
|
||||
"swiftmailer/swiftmailer": 0,
|
||||
"fguillot/picodb": 20,
|
||||
"symfony/console": 0
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
"ext-mbstring": "*"
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
app_css="base links title table form button alert tooltip header board task comment subtask markdown listing activity dashboard pagination popover confirm sidebar responsive dropdown"
|
||||
vendor_css="jquery-ui-1.10.4.custom chosen.min fullcalendar.min font-awesome.min"
|
||||
vendor_css="jquery-ui.min chosen.min fullcalendar.min font-awesome.min"
|
||||
|
||||
app_js="base board calendar analytic link swimlane dashboard"
|
||||
vendor_js="jquery-1.11.1.min jquery-ui-1.10.4.custom.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min app.min"
|
||||
app_js="base board calendar analytic task swimlane dashboard"
|
||||
vendor_js="jquery-1.11.1.min jquery-ui.min jquery.ui.touch-punch.min chosen.jquery.min dropit.min moment.min fullcalendar.min mousetrap.min app.min"
|
||||
lang_js="da de es fi fr hu it ja pl pt-br ru sv th zh-cn"
|
||||
|
||||
function merge_css {
|
||||
|
|
@ -13,7 +13,7 @@ function merge_css {
|
|||
|
||||
rm -f $dst_file 2>/dev/null
|
||||
echo "/* DO NOT EDIT: auto-generated file */" > $dst_file
|
||||
|
||||
|
||||
for file in $vendor_css; do cat "assets/css/vendor/${file}.css" >> $dst_file; done
|
||||
for file in $app_css; do cat "assets/css/src/${file}.css" >> $dst_file; done
|
||||
}
|
||||
|
|
@ -43,7 +43,7 @@ function merge_js {
|
|||
local dst_file="assets/js/app.js"
|
||||
|
||||
rm -f $dst_file 2>/dev/null
|
||||
|
||||
|
||||
for file in $vendor_js; do cat "assets/js/vendor/${file}.js" >> $dst_file; done
|
||||
for file in $lang_js; do cat "assets/js/vendor/lang/${file}.js" >> $dst_file; done
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,161 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/Base.php';
|
||||
|
||||
use Model\Link;
|
||||
|
||||
class LinkTest extends Base
|
||||
{
|
||||
public function testCreateLink()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
|
||||
$this->assertTrue($l->create('Link A'));
|
||||
$this->assertFalse($l->create('Link A'));
|
||||
$this->assertTrue($l->create('Link B', 'Link C'));
|
||||
|
||||
$links = $l->getAll();
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(14, $links);
|
||||
|
||||
$link = $l->getByLabel('Link A');
|
||||
$this->assertNotEmpty($link);
|
||||
$this->assertEquals('Link A', $link['label']);
|
||||
$this->assertEquals(0, $link['opposite_id']);
|
||||
|
||||
$link1 = $l->getByLabel('Link B');
|
||||
$this->assertNotEmpty($link1);
|
||||
$this->assertEquals('Link B', $link1['label']);
|
||||
$this->assertNotEmpty($link1['opposite_id']);
|
||||
|
||||
$link2 = $l->getByLabel('Link C');
|
||||
$this->assertNotEmpty($link2);
|
||||
$this->assertEquals('Link C', $link2['label']);
|
||||
$this->assertNotEmpty($link2['opposite_id']);
|
||||
|
||||
$this->assertNotEquals($link1['opposite_id'], $link2['opposite_id']);
|
||||
}
|
||||
|
||||
public function testUpdate()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
|
||||
$this->assertTrue($l->update(array('id' => 2, 'label' => 'test', 'opposite_id' => 0)));
|
||||
|
||||
$link = $l->getById(2);
|
||||
$this->assertNotEmpty($link);
|
||||
$this->assertEquals('test', $link['label']);
|
||||
$this->assertEquals(0, $link['opposite_id']);
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
|
||||
$link = $l->getById(3);
|
||||
$this->assertNotEmpty($link);
|
||||
$this->assertEquals('is blocked by', $link['label']);
|
||||
$this->assertEquals(2, $link['opposite_id']);
|
||||
|
||||
$this->assertTrue($l->remove(2));
|
||||
|
||||
$link = $l->getById(2);
|
||||
$this->assertEmpty($link);
|
||||
|
||||
$link = $l->getById(3);
|
||||
$this->assertNotEmpty($link);
|
||||
$this->assertEquals('is blocked by', $link['label']);
|
||||
$this->assertEquals(0, $link['opposite_id']);
|
||||
}
|
||||
|
||||
public function testGetMergedList()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
$links = $l->getMergedList();
|
||||
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(11, $links);
|
||||
$this->assertEquals('blocks', $links[1]['label']);
|
||||
$this->assertEquals('is blocked by', $links[1]['opposite_label']);
|
||||
}
|
||||
|
||||
public function testGetList()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
$links = $l->getList();
|
||||
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(12, $links);
|
||||
$this->assertEquals('', $links[0]);
|
||||
$this->assertEquals('relates to', $links[1]);
|
||||
|
||||
$links = $l->getList(1);
|
||||
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(11, $links);
|
||||
$this->assertEquals('', $links[0]);
|
||||
$this->assertArrayNotHasKey(1, $links);
|
||||
$this->assertEquals('blocks', $links[2]);
|
||||
|
||||
$links = $l->getList(1, false);
|
||||
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(10, $links);
|
||||
$this->assertArrayNotHasKey(0, $links);
|
||||
$this->assertArrayNotHasKey(1, $links);
|
||||
$this->assertEquals('blocks', $links[2]);
|
||||
|
||||
$links = $l->getList(0, false);
|
||||
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(11, $links);
|
||||
$this->assertArrayNotHasKey(0, $links);
|
||||
$this->assertEquals('relates to', $links[1]);
|
||||
}
|
||||
|
||||
public function testValidateCreation()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
|
||||
$r = $l->validateCreation(array('label' => 'a'));
|
||||
$this->assertTrue($r[0]);
|
||||
|
||||
$r = $l->validateCreation(array('label' => 'a', 'opposite_label' => 'b'));
|
||||
$this->assertTrue($r[0]);
|
||||
|
||||
$r = $l->validateCreation(array('label' => 'relates to'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateCreation(array('label' => 'a', 'opposite_label' => 'a'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateCreation(array('label' => ''));
|
||||
$this->assertFalse($r[0]);
|
||||
}
|
||||
|
||||
public function testValidateModification()
|
||||
{
|
||||
$l = new Link($this->container);
|
||||
|
||||
$r = $l->validateModification(array('id' => 20, 'label' => 'a', 'opposite_id' => 0));
|
||||
$this->assertTrue($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('id' => 20, 'label' => 'a', 'opposite_id' => '1'));
|
||||
$this->assertTrue($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('id' => 20, 'label' => 'relates to', 'opposite_id' => '1'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('id' => 20, 'label' => '', 'opposite_id' => '1'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('label' => '', 'opposite_id' => '1'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('id' => 20, 'opposite_id' => '1'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $l->validateModification(array('label' => 'test'));
|
||||
$this->assertFalse($r[0]);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/Base.php';
|
||||
|
||||
use Model\Link;
|
||||
use Model\TaskLink;
|
||||
use Model\TaskCreation;
|
||||
use Model\Project;
|
||||
|
||||
class TaskLinkTest extends Base
|
||||
{
|
||||
public function testCreateTaskLinkWithNoOpposite()
|
||||
{
|
||||
$tl = new TaskLink($this->container);
|
||||
$p = new Project($this->container);
|
||||
$tc = new TaskCreation($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'test')));
|
||||
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
|
||||
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
|
||||
$this->assertTrue($tl->create(1, 2, 1));
|
||||
|
||||
$links = $tl->getLinks(1);
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(1, $links);
|
||||
$this->assertEquals('relates to', $links[0]['label']);
|
||||
$this->assertEquals('B', $links[0]['title']);
|
||||
$this->assertEquals(2, $links[0]['task_id']);
|
||||
$this->assertEquals(1, $links[0]['is_active']);
|
||||
|
||||
$links = $tl->getLinks(2);
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(1, $links);
|
||||
$this->assertEquals('relates to', $links[0]['label']);
|
||||
$this->assertEquals('A', $links[0]['title']);
|
||||
$this->assertEquals(1, $links[0]['task_id']);
|
||||
$this->assertEquals(1, $links[0]['is_active']);
|
||||
}
|
||||
|
||||
public function testCreateTaskLinkWithOpposite()
|
||||
{
|
||||
$tl = new TaskLink($this->container);
|
||||
$p = new Project($this->container);
|
||||
$tc = new TaskCreation($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'test')));
|
||||
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
|
||||
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
|
||||
$this->assertTrue($tl->create(1, 2, 2));
|
||||
|
||||
$links = $tl->getLinks(1);
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(1, $links);
|
||||
$this->assertEquals('blocks', $links[0]['label']);
|
||||
$this->assertEquals('B', $links[0]['title']);
|
||||
$this->assertEquals(2, $links[0]['task_id']);
|
||||
$this->assertEquals(1, $links[0]['is_active']);
|
||||
|
||||
$links = $tl->getLinks(2);
|
||||
$this->assertNotEmpty($links);
|
||||
$this->assertCount(1, $links);
|
||||
$this->assertEquals('is blocked by', $links[0]['label']);
|
||||
$this->assertEquals('A', $links[0]['title']);
|
||||
$this->assertEquals(1, $links[0]['task_id']);
|
||||
$this->assertEquals(1, $links[0]['is_active']);
|
||||
}
|
||||
|
||||
public function testRemove()
|
||||
{
|
||||
$tl = new TaskLink($this->container);
|
||||
$p = new Project($this->container);
|
||||
$tc = new TaskCreation($this->container);
|
||||
|
||||
$this->assertEquals(1, $p->create(array('name' => 'test')));
|
||||
$this->assertEquals(1, $tc->create(array('project_id' => 1, 'title' => 'A')));
|
||||
$this->assertEquals(2, $tc->create(array('project_id' => 1, 'title' => 'B')));
|
||||
$this->assertTrue($tl->create(1, 2, 2));
|
||||
|
||||
$links = $tl->getLinks(1);
|
||||
$this->assertNotEmpty($links);
|
||||
$links = $tl->getLinks(2);
|
||||
$this->assertNotEmpty($links);
|
||||
|
||||
$this->assertTrue($tl->remove($links[0]['id']));
|
||||
|
||||
$links = $tl->getLinks(1);
|
||||
$this->assertEmpty($links);
|
||||
$links = $tl->getLinks(2);
|
||||
$this->assertEmpty($links);
|
||||
}
|
||||
|
||||
public function testValidation()
|
||||
{
|
||||
$tl = new TaskLink($this->container);
|
||||
|
||||
$r = $tl->validateCreation(array('task_id' => 1, 'link_id' => 1, 'title' => 'a'));
|
||||
$this->assertTrue($r[0]);
|
||||
|
||||
$r = $tl->validateCreation(array('task_id' => 1, 'link_id' => 1));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $tl->validateCreation(array('task_id' => 1, 'title' => 'a'));
|
||||
$this->assertFalse($r[0]);
|
||||
|
||||
$r = $tl->validateCreation(array('link_id' => 1, 'title' => 'a'));
|
||||
$this->assertFalse($r[0]);
|
||||
}
|
||||
}
|
||||