Refactoring/simplification of the pull-request about links
This commit is contained in:
@@ -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()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user