看板初始化提交

This commit is contained in:
zephyr
2026-06-01 21:23:12 -07:00
commit 27411ebedc
1827 changed files with 192340 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
<IfVersion >= 2.3>
Require all denied
</IfVersion>
<IfVersion < 2.3>
Order allow,deny
Deny from all
</IfVersion>
+305
View File
@@ -0,0 +1,305 @@
<?php
namespace Kanboard\Action;
use Kanboard\Event\GenericEvent;
/**
* Base class for automatic actions
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
abstract class Base extends \Kanboard\Core\Base
{
/**
* Extended events
*
* @access private
* @var array
*/
private $compatibleEvents = array();
/**
* Keep history of executed events
*
* @access private
* @var array
*/
private $callStack = [];
/**
* Project id
*
* @access private
* @var integer
*/
private $projectId = 0;
/**
* User parameters
*
* @access private
* @var array
*/
private $params = array();
/**
* Get automatic action name
*
* @final
* @access public
* @return string
*/
final public function getName()
{
return '\\'.get_called_class();
}
/**
* Get automatic action description
*
* @abstract
* @access public
* @return string
*/
abstract public function getDescription();
/**
* Execute the action
*
* @abstract
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
abstract public function doAction(array $data);
/**
* Get the required parameter for the action (defined by the user)
*
* @abstract
* @access public
* @return array
*/
abstract public function getActionRequiredParameters();
/**
* Get the required parameter for the event (check if for the event data)
*
* @abstract
* @access public
* @return array
*/
abstract public function getEventRequiredParameters();
/**
* Get the compatible events
*
* @abstract
* @access public
* @return array
*/
abstract public function getCompatibleEvents();
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
abstract public function hasRequiredCondition(array $data);
/**
* Return class information
*
* @access public
* @return string
*/
public function __toString()
{
$params = array();
foreach ($this->params as $key => $value) {
$params[] = $key.'='.var_export($value, true);
}
return $this->getName().'('.implode('|', $params).')';
}
/**
* Set project id
*
* @access public
* @param integer $project_id
* @return Base
*/
public function setProjectId($project_id)
{
$this->projectId = $project_id;
return $this;
}
/**
* Get project id
*
* @access public
* @return integer
*/
public function getProjectId()
{
return $this->projectId;
}
/**
* Set an user defined parameter
*
* @access public
* @param string $name Parameter name
* @param mixed $value Value
* @return Base
*/
public function setParam($name, $value)
{
$this->params[$name] = $value;
return $this;
}
/**
* Get an user defined parameter
*
* @access public
* @param string $name Parameter name
* @param mixed $default Default value
* @return mixed
*/
public function getParam($name, $default = null)
{
return isset($this->params[$name]) ? $this->params[$name] : $default;
}
/**
* Check if an action is executable (right project and required parameters)
*
* @access public
* @param array $data
* @param string $eventName
* @return bool
*/
public function isExecutable(array $data, $eventName)
{
return $this->hasCompatibleEvent($eventName) &&
$this->hasRequiredProject($data) &&
$this->hasRequiredParameters($data) &&
$this->hasRequiredCondition($data);
}
/**
* Check if the event is compatible with the action
*
* @access public
* @param string $eventName
* @return bool
*/
public function hasCompatibleEvent($eventName)
{
return in_array($eventName, $this->getEvents());
}
/**
* Check if the event data has the required project
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredProject(array $data)
{
return (isset($data['project_id']) && $data['project_id'] == $this->getProjectId()) ||
(isset($data['task']['project_id']) && $data['task']['project_id'] == $this->getProjectId());
}
/**
* Check if the event data has required parameters to execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if all keys are there
*/
public function hasRequiredParameters(array $data, array $parameters = array())
{
$parameters = $parameters ?: $this->getEventRequiredParameters();
foreach ($parameters as $key => $value) {
if (is_array($value)) {
return isset($data[$key]) && $this->hasRequiredParameters($data[$key], $value);
} elseif (! isset($data[$value])) {
return false;
}
}
return true;
}
/**
* Execute the action
*
* @access public
* @param \Kanboard\Event\GenericEvent $event
* @param string $eventName
* @return bool
*/
public function execute(GenericEvent $event, $eventName)
{
$data = $event->getAll();
$hash = md5(serialize($data).$eventName);
// Do not call twice the same action with the same arguments.
if (isset($this->callStack[$hash])) {
return false;
} else {
$this->callStack[$hash] = true;
}
$executable = $this->isExecutable($data, $eventName);
$executed = false;
if ($executable) {
$executed = $this->doAction($data);
}
$this->logger->debug($this.' ['.$eventName.'] => executable='.var_export($executable, true).' exec_success='.var_export($executed, true));
return $executed;
}
/**
* Register a new event for the automatic action
*
* @access public
* @param string $event
* @param string $description
* @return Base
*/
public function addEvent($event, $description = '')
{
if ($description !== '') {
$this->eventManager->register($event, $description);
}
$this->compatibleEvents[] = $event;
return $this;
}
/**
* Get all compatible events of an automatic action
*
* @access public
* @return array
*/
public function getEvents()
{
return array_unique(array_merge($this->getCompatibleEvents(), $this->compatibleEvents));
}
}
+87
View File
@@ -0,0 +1,87 @@
<?php
namespace Kanboard\Action;
/**
* Create automatically a comment from a webhook
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class CommentCreation extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Create a comment from an external provider');
}
/**
* Get the list of compatible events
*
* @access public
* @return string[]
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return string[]
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return array
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
);
}
/**
* Execute the action (create a new comment)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return (bool) $this->commentModel->create(array(
'reference' => isset($data['reference']) ? $data['reference'] : '',
'comment' => $data['comment'],
'task_id' => $data['task_id'],
'user_id' => isset($data['user_id']) && $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['user_id']) ? $data['user_id'] : 0,
));
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return ! empty($data['comment']);
}
}
@@ -0,0 +1,100 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Add a comment of the triggering event to the task description.
*
* @package Kanboard\Action
* @author Oren Ben-Kiki
*/
class CommentCreationMoveTaskColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Add a comment log when moving the task between columns');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array('column_id' => t('Column'));
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'column_id',
'project_id',
),
);
}
/**
* Execute the action (append to the task description).
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
if (! $this->userSession->isLogged()) {
return false;
}
$column = $this->columnModel->getById($data['task']['column_id']);
return (bool) $this->commentModel->create(array(
'comment' => t('Moved to column %s', $column['title']),
'task_id' => $data['task_id'],
'user_id' => $this->userSession->getId(),
));
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,102 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
use Kanboard\Model\SubtaskModel;
/**
* Stop the timer of all subtasks when moving a task to another column.
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class StopSubtaskTimerMoveTaskColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Stop the timer of all subtasks when moving a task to another column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'id',
'column_id',
'project_id',
),
);
}
/**
* Execute the action (append to the task description).
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$subtasks = $this->subtaskModel->getAll($data['task']['id']);
$results = array();
foreach ($subtasks as $subtask) {
$results[] = $this->subtaskModel->update(array('id' => $subtask['id'], 'status' => SubtaskModel::STATUS_DONE));
$results[] = $this->subtaskTimeTrackingModel->logEndTime($subtask['id'], $subtask['user_id']);
}
return !in_array(false, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
use Kanboard\Model\SubtaskModel;
/**
* Create a subtask and activate the timer when moving a task to another column.
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class SubtaskTimerMoveTaskColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Add a subtask and activate the timer when moving a task to another column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'subtask' => t('Subtask Title'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'id',
'column_id',
'project_id',
'creator_id',
),
);
}
/**
* Execute the action (append to the task description).
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$subtaskID = $this->subtaskModel->create(array(
'title' => $this->getParam('subtask'),
'user_id' => $data['task']['creator_id'],
'task_id' => $data['task']['id'],
'status' => SubtaskModel::STATUS_INPROGRESS,
));
if ($subtaskID !== false) {
return $this->subtaskTimeTrackingModel->toggleTimer($subtaskID, $data['task']['creator_id'], SubtaskModel::STATUS_INPROGRESS);
}
return false;
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set a category automatically according to the color
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryColor extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a category based on a color');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'color_id',
),
);
}
/**
* Execute the action (change the category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'category_id' => $this->getParam('category_id'),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['color_id'] == $this->getParam('color_id');
}
}
+91
View File
@@ -0,0 +1,91 @@
<?php
namespace Kanboard\Action;
/**
* Set a category automatically according to a label
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCategoryLabel extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Change the category based on an external label');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'label' => t('Label'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'label',
);
}
/**
* Execute the action (change the category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'category_id' => $this->getParam('category_id'),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['label'] == $this->getParam('label') && empty($data['category_id']);
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskLinkModel;
/**
* Set a category automatically according to a task link
*
* @package Kanboard\Action
* @author Olivier Maridat
* @author Frederic Guillot
*/
class TaskAssignCategoryLink extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a category based on a link');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskLinkModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'category_id' => t('Category'),
'link_id' => t('Link type'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_link' => array(
'task_id',
'link_id',
)
);
}
/**
* Execute the action (change the category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_link']['task_id'],
'category_id' => $this->getParam('category_id'),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
if ($data['task_link']['link_id'] == $this->getParam('link_id')) {
return empty($data['task']['category_id']);
}
return false;
}
}
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a category when the task is moved to a specific swimlane
*
* @package Kanboard\Action
* @author @Interleaved
*/
class TaskAssignCategorySwimlaneChange extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a category when the task is moved to a specific swimlane');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_MOVE_SWIMLANE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'swimlane_id' => t('Swimlane'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'swimlane_id',
)
);
}
/**
* Execute the action (set the task category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'category_id' => $this->getParam('category_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] == $this->getParam('swimlane_id');
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific category
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorCategory extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a color based on a category');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'category_id',
),
);
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['category_id'] == $this->getParam('category_id');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a color when the task is moved to a specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'color_id' => t('Color'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
),
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskLinkModel;
/**
* Assign a color to a specific task link
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorLink extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Change task color when using a specific task link');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskLinkModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
'link_id' => t('Link type'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_link' => array(
'task_id',
'link_id',
)
);
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_link']['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task_link']['link_id'] == $this->getParam('link_id');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a priority
*
* @package Kanboard\Action
* @author Julien Buratto
*/
class TaskAssignColorOnDueDate extends Base
{
/**
* Get action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a color when due date is expired');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_DAILY_CRONJOB,
);
}
/**
* Get the required parameter for the action
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
);
}
/**
* Get all tasks
*
* @access public
* @return array
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
foreach ($data['tasks'] as $task) {
if ($task['date_due'] <= time() && $task['date_due'] > 0 && $task['color_id'] != $this->getParam('color_id')) {
$values = array(
'id' => $task['id'],
'color_id' => $this->getParam('color_id'),
);
$results[] = $this->taskModificationModel->update($values, false);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color when preset start date is reached
*
* @package Kanboard\Action
* @author Christian Wolter
*/
class TaskAssignColorOnStartDate extends Base
{
/**
* Get action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a color when preset start date is reached');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_DAILY_CRONJOB,
);
}
/**
* Get the required parameter for the action
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
);
}
/**
* Get all tasks
*
* @access public
* @return array
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
foreach ($data['tasks'] as $task) {
if ($task['date_started'] <= time() && $task['date_started'] > 0 && $task['color_id'] != $this->getParam('color_id')) {
$values = array(
'id' => $task['id'],
'color_id' => $this->getParam('color_id'),
);
$results[] = $this->taskModificationModel->update($values, false);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a priority
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorPriority extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign automatically a color based on a priority');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
'priority' => t('Priority'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'priority',
),
);
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['priority'] == $this->getParam('priority');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a task based on Swimlane
*
* @package Kanboard\Action
* @author Dave Almond
*/
class TaskAssignColorSwimlane extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a color when the task is moved to a specific swimlane');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_MOVE_SWIMLANE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'swimlane_id' => t('Swimlane'),
'color_id' => t('Color'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'swimlane_id',
),
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] == $this->getParam('swimlane_id');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a color to a specific user
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignColorUser extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a color to a specific user');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_ASSIGNEE_CHANGE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'color_id' => t('Color'),
'user_id' => t('Assignee'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'owner_id',
),
);
}
/**
* Execute the action (change the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'color_id' => $this->getParam('color_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['owner_id'] == $this->getParam('user_id');
}
}
+98
View File
@@ -0,0 +1,98 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a task to the creator
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCreator extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to its creator');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'creator_id',
),
);
}
/**
* Execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'owner_id' => $data['task']['creator_id'],
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUser extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to the person who does the action');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
);
}
/**
* Execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
if (! $this->userSession->isLogged()) {
return false;
}
$values = array(
'id' => $data['task_id'],
'owner_id' => $this->userSession->getId(),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user on column change
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignCurrentUserColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to the person who does the action when the column is changed');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
),
);
}
/**
* Execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
if (! $this->userSession->isLogged()) {
return false;
}
$values = array(
'id' => $data['task_id'],
'owner_id' => $this->userSession->getId(),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,103 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a task to the logged user on column change if no user is assigned already
*
* @package Kanboard\Action
* @author Glukose1
*/
class TaskAssignCurrentUserColumnIfNoUserAlreadySet extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a task to the logged user on column change to specified column if no user is assigned');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/*** Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'owner_id',
),
);
}
/**
* Execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
if (!$this->userSession->isLogged()) {
return false;
}
if (!$data['task']['owner_id']) {
$values = array(
'id' => $data['task_id'],
'owner_id' => $this->userSession->getId(),
);
return $this->taskModificationModel->update($values);
}
return false;
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set the due date of task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignDueDateOnCreation extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Automatically set the due date on task creation');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
),
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'date_due' => strtotime('+'.$this->getParam('duration').'days'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}
@@ -0,0 +1,97 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set the due date when task move away from certain column
*
* @package Kanboard\Action
* @author Stefan Krüger
*/
class TaskAssignDueDateOnMoveColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Automatically set the due date when the task is moved away from a specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
),
'src_column_id',
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'date_due' => strtotime('+'.$this->getParam('duration').'days'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return !empty($data['src_column_id']) && $data['src_column_id'] == $this->getParam('column_id');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set a priority automatically according to the Swimlane
*
* @package Kanboard\Action
* @author Dave Almond
*/
class TaskAssignPrioritySwimlane extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign a priority when the task is moved to a specific swimlane');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_MOVE_SWIMLANE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'swimlane_id' => t('Swimlane'),
'priority' => t('Priority'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'swimlane_id',
),
);
}
/**
* Execute the action (set the priority)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'priority' => $this->getParam('priority'),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] == $this->getParam('swimlane_id');
}
}
+99
View File
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a task to a specific user
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskAssignSpecificUser extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to a specific user');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE,
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'user_id' => t('Assignee'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
),
);
}
/**
* Execute the action (assign the given user)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'owner_id' => $this->getParam('user_id'),
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,109 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a Task to the creator in a specific column
*
* @package Kanboard\Action
* @author Glukose1
*/
class TaskAssignToUserOnCreationInColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to its creator for specific columns if no assignee is set manually');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'creator_id',
),
);
}
/**
* Execute the action
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$assignee_id = $this->userModel->getIdByUsername($data['task']['assignee_username']);
if ($data['task']['assignee_username']) {
$values = array(
'id' => $data['task_id'],
'owner_id' => $assignee_id,
);
return $this->taskModificationModel->update($values);
}
$values = array(
'id' => $data['task_id'],
'owner_id' => $data['task']['creator_id'],
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+88
View File
@@ -0,0 +1,88 @@
<?php
namespace Kanboard\Action;
/**
* Assign a task to someone
*
* @package Kanboard\Actionv
* @author Frederic Guillot
*/
class TaskAssignUser extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Change the assignee based on an external username');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'owner_id',
);
}
/**
* Execute the action (assign the given user)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'owner_id' => $data['owner_id'],
);
return $this->taskModificationModel->update($values);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $this->projectPermissionModel->isAssignable($this->getProjectId(), $data['owner_id']);
}
}
@@ -0,0 +1,99 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Assign a user when the task is moved to a specific swimlane
*
* @package Kanboard\Action
* @author @Interleaved
*/
class TaskAssignUserSwimlaneChange extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Assign the task to a specific user when the task is moved to a specific swimlane');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE,
TaskModel::EVENT_MOVE_SWIMLANE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'swimlane_id' => t('Swimlane'),
'user_id' => t('Assignee'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'swimlane_id',
)
);
}
/**
* Execute the action (set the task category)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'owner_id' => $this->getParam('user_id'),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] == $this->getParam('swimlane_id');
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace Kanboard\Action;
/**
* Close automatically a task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskClose extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('task_id');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskStatusModel->close($data['task_id']);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}
+90
View File
@@ -0,0 +1,90 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Close automatically a task in a specific column
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task in a specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array('column_id' => t('Column'));
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
)
);
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskStatusModel->close($data['task_id']);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+95
View File
@@ -0,0 +1,95 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Close automatically a task after when inactive
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivity extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task when there is no activity');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = (int)$this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_modification'];
if ($duration > $max) {
$results[] = $this->taskStatusModel->close($task['id']);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Close automatically a task after inactive and in a defined column
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNoActivityColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task when there is no activity in a specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'column_id' => t('Column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = (int)$this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_modification'];
if ($duration > $max && $task['column_id'] == $this->getParam('column_id')) {
$results[] = $this->taskStatusModel->close($task['id']);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Close automatically in a defined column after a certain amount of time
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCloseNotMovedColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Close a task in a specific column when not moved during a given period');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'column_id' => t('Column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = (int)$this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_moved'];
if ($duration > $max && $task['column_id'] == $this->getParam('column_id')) {
$results[] = $this->taskStatusModel->close($task['id']);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+89
View File
@@ -0,0 +1,89 @@
<?php
namespace Kanboard\Action;
/**
* Create automatically a task from a webhook
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskCreation extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Create a task from an external provider');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'project_id',
'reference',
'title',
);
}
/**
* Execute the action (create a new task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return (bool) $this->taskCreationModel->create(array(
'project_id' => $data['project_id'],
'title' => $data['title'],
'reference' => $data['reference'],
'description' => isset($data['description']) ? $data['description'] : '',
));
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Duplicate a task to another project
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskDuplicateAnotherProject extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Duplicate the task to another project');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
TaskModel::EVENT_CLOSE,
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'project_id' => t('Project'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
)
);
}
/**
* Execute the action (duplicate the task to another project)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$destination_column_id = $this->columnModel->getFirstColumnId($this->getParam('project_id'));
return (bool) $this->taskProjectDuplicationModel->duplicateToProject(
$data['task_id'],
$this->getParam('project_id'),
null,
$destination_column_id
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}
+119
View File
@@ -0,0 +1,119 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Email a task to someone
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmail extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Send a task by email to someone');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
TaskModel::EVENT_CLOSE,
TaskModel::EVENT_CREATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'user_id' => t('User that will receive the email'),
'subject' => t('Email subject'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
),
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$user = $this->userModel->getById($this->getParam('user_id'));
$subject = $this->getParam('subject');
foreach ($data["task"] as $key => $value) {
if ($value !== null) {
$placeholder = sprintf('{{%s}}', $key);
$subject = str_replace($placeholder, $value, $subject);
}
}
if (! empty($user['email'])) {
$this->emailClient->send(
$user['email'],
$user['name'] ?: $user['username'],
$subject,
$this->template->render('notification/task_create', array(
'task' => $data['task'],
))
);
return true;
}
return false;
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
+124
View File
@@ -0,0 +1,124 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Email a task with no activity
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskEmailNoActivity extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Send email when there is no activity on a task');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_DAILY_CRONJOB,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'user_id' => t('User that will receive the email'),
'subject' => t('Email subject'),
'duration' => t('Duration in days'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = (int)$this->getParam('duration') * 86400;
$user = $this->userModel->getById($this->getParam('user_id'));
if (! empty($user['email'])) {
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_modification'];
if ($duration > $max) {
$results[] = $this->sendEmail($task['id'], $user);
}
}
}
return in_array(true, $results, true);
}
/**
* Send email
*
* @access private
* @param integer $task_id
* @param array $user
* @return boolean
*/
private function sendEmail($task_id, array $user)
{
$task = $this->taskFinderModel->getDetails($task_id);
$this->emailClient->send(
$user['email'],
$user['name'] ?: $user['username'],
$this->getParam('subject'),
$this->template->render('notification/task_create', array('task' => $task))
);
return true;
}
}
+94
View File
@@ -0,0 +1,94 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another project
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveAnotherProject extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another project');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
TaskModel::EVENT_CLOSE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
'project_id' => t('Project'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
)
);
}
/**
* Execute the action (move the task to another project)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskProjectMoveModel->moveToProject($data['task_id'], $this->getParam('project_id'));
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id') && $data['task']['project_id'] != $this->getParam('project_id');
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is set
*
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnAssigned extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when assigned to a user');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_ASSIGNEE_CHANGE,
TaskModel::EVENT_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'owner_id',
'position',
'swimlane_id',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] > 0;
}
}
+105
View File
@@ -0,0 +1,105 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the category is changed
*
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnCategoryChange extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when the category is changed');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_UPDATE,
TaskModel::EVENT_CREATE,
TaskModel::EVENT_CREATE_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'dest_column_id' => t('Destination column'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'category_id',
'position',
'swimlane_id',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['category_id'] == $this->getParam('category_id');
}
}
+102
View File
@@ -0,0 +1,102 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when the task is closed
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveColumnClosed extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when closed');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CLOSE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'swimlane_id',
'is_active',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task']['id'],
$this->getParam('dest_column_id'),
1,
$data['task']['swimlane_id'],
true,
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] != $this->getParam('dest_column_id') && $data['task']['is_active'] == 0;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when not moved during a given period
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskMoveColumnNotMovedPeriod extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when not moved during a given period');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$max = (int)$this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = time() - $task['date_moved'];
if ($duration > $max && $task['column_id'] == $this->getParam('src_column_id')) {
$results[] = $this->taskPositionModel->movePosition(
$task['project_id'],
$task['id'],
$this->getParam('dest_column_id'),
1,
$task['swimlane_id'],
false
);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+103
View File
@@ -0,0 +1,103 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move the task from one to another column when due date is LESS than a certain number of days
*
* @package Kanboard\Action
*/
class TaskMoveColumnOnDueDate extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when the due date is less than a certain number of days');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'duration' => t('Duration in days'),
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
$min = (int)$this->getParam('duration') * 86400;
foreach ($data['tasks'] as $task) {
$duration = $task['date_due'] - time();
if ($task['date_due'] > 0 && $duration < $min && $task['column_id'] == $this->getParam('src_column_id')) {
$results[] = $this->taskPositionModel->movePosition(
$task['project_id'],
$task['id'],
$this->getParam('dest_column_id'),
1,
$task['swimlane_id'],
true
);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+101
View File
@@ -0,0 +1,101 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column once a predefined start date is reached
*
* @package Kanboard\Action
* @author Christian Wolter
*/
class TaskMoveColumnOnStartDate extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column once a predefined start date is reached');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(TaskModel::EVENT_DAILY_CRONJOB);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('tasks');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$results = array();
foreach ($data['tasks'] as $task) {
if ($task['date_started'] <= time() && $task['date_started'] > 0 && $task['column_id'] == $this->getParam('src_column_id')) {
$results[] = $this->taskPositionModel->movePosition(
$task['project_id'],
$task['id'],
$this->getParam('dest_column_id'),
1,
$task['swimlane_id'],
false
);
}
}
return in_array(true, $results, true);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return count($data['tasks']) > 0;
}
}
+104
View File
@@ -0,0 +1,104 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is cleared
*
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveColumnUnAssigned extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another column when assignee is cleared');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_ASSIGNEE_CHANGE,
TaskModel::EVENT_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'src_column_id' => t('Source column'),
'dest_column_id' => t('Destination column')
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'owner_id',
'position',
'swimlane_id',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task_id'],
$this->getParam('dest_column_id'),
$data['task']['position'],
$data['task']['swimlane_id'],
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('src_column_id') && $data['task']['owner_id'] == 0;
}
}
+107
View File
@@ -0,0 +1,107 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another column when an assignee is set
*
* @package Kanboard\Action
* @author Francois Ferrand
*/
class TaskMoveSwimlaneAssigned extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another swimlane when assigned to a user');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_CREATE_UPDATE,
TaskModel::EVENT_ASSIGNEE_CHANGE,
TaskModel::EVENT_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'user_id' => t('Assignee'),
'dest_swimlane_id' => t('Destination swimlane'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'owner_id',
'position',
'swimlane_id',
)
);
}
/**
* Execute the action (move the task to another column)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task_id'],
$data['task']['column_id'],
$data['task']['position'],
$this->getParam('dest_swimlane_id'),
true
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
**/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] != $this->getParam('dest_swimlane_id') && $data['task']['owner_id'] == $this->getParam("user_id");
}
}
@@ -0,0 +1,103 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Move a task to another swimlane when the category is changed
*
* @package Kanboard\Action
* @author @Interleaved
*/
class TaskMoveSwimlaneCategoryChange extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Move the task to another swimlane when the category is changed');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_UPDATE,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'dest_swimlane_id' => t('Destination swimlane'),
'category_id' => t('Category'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
'category_id',
'position',
'swimlane_id',
)
);
}
/**
* Execute the action (move the task to another swimlane)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskPositionModel->movePosition(
$data['task']['project_id'],
$data['task_id'],
$data['task']['column_id'],
$data['task']['position'],
$this->getParam('dest_swimlane_id'),
false
);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['swimlane_id'] != $this->getParam('dest_swimlane_id') && $data['task']['category_id'] == $this->getParam('category_id');
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace Kanboard\Action;
/**
* Open automatically a task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskOpen extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Open a task');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array();
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array();
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array('task_id');
}
/**
* Execute the action (close the task)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
return $this->taskStatusModel->open($data['task_id']);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return true;
}
}
+97
View File
@@ -0,0 +1,97 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set the start date of task
*
* @package Kanboard\Action
* @author Frederic Guillot
*/
class TaskUpdateStartDate extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Automatically update the start date');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
'column_id',
),
);
}
/**
* Execute the action (set the task color)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'date_started' => time(),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return $data['task']['column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,96 @@
<?php
namespace Kanboard\Action;
use Kanboard\Model\TaskModel;
/**
* Set the start date when task move away from certain column
*
* @package Kanboard\Action
*/
class TaskUpdateStartDateOnMoveColumn extends Base
{
/**
* Get automatic action description
*
* @access public
* @return string
*/
public function getDescription()
{
return t('Automatically update the start date when the task is moved away from a specific column');
}
/**
* Get the list of compatible events
*
* @access public
* @return array
*/
public function getCompatibleEvents()
{
return array(
TaskModel::EVENT_MOVE_COLUMN,
);
}
/**
* Get the required parameter for the action (defined by the user)
*
* @access public
* @return array
*/
public function getActionRequiredParameters()
{
return array(
'column_id' => t('Column'),
);
}
/**
* Get the required parameter for the event
*
* @access public
* @return string[]
*/
public function getEventRequiredParameters()
{
return array(
'task_id',
'task' => array(
'project_id',
),
'src_column_id',
);
}
/**
* Execute the action (set the task date_started)
*
* @access public
* @param array $data Event data dictionary
* @return bool True if the action was executed or false when not executed
*/
public function doAction(array $data)
{
$values = array(
'id' => $data['task_id'],
'date_started' => time(),
);
return $this->taskModificationModel->update($values, false);
}
/**
* Check if the event data meet the action condition
*
* @access public
* @param array $data Event data dictionary
* @return bool
*/
public function hasRequiredCondition(array $data)
{
return empty($data['task']['date_started']) && $data['src_column_id'] == $this->getParam('column_id');
}
}
@@ -0,0 +1,116 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
use Kanboard\Model\TaskModel;
/**
* Average Lead and Cycle Time
*
* @package analytic
* @author Frederic Guillot
*/
class AverageLeadCycleTimeAnalytic extends Base
{
/**
* Build report
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function build($project_id)
{
$stats = array(
'count' => 0,
'total_lead_time' => 0,
'total_cycle_time' => 0,
'avg_lead_time' => 0,
'avg_cycle_time' => 0,
);
$tasks = $this->getTasks($project_id);
foreach ($tasks as &$task) {
$stats['count']++;
$stats['total_lead_time'] += $this->calculateLeadTime($task);
$stats['total_cycle_time'] += $this->calculateCycleTime($task);
}
$stats['avg_lead_time'] = $this->calculateAverage($stats, 'total_lead_time');
$stats['avg_cycle_time'] = $this->calculateAverage($stats, 'total_cycle_time');
return $stats;
}
/**
* Calculate average
*
* @access private
* @param array &$stats
* @param string $field
* @return float
*/
private function calculateAverage(array &$stats, $field)
{
if ($stats['count'] > 0) {
return (int) ($stats[$field] / $stats['count']);
}
return 0;
}
/**
* Calculate lead time
*
* @access private
* @param array &$task
* @return integer
*/
private function calculateLeadTime(array &$task)
{
$end = $task['date_completed'] ?: time();
$start = $task['date_creation'];
return $end - $start;
}
/**
* Calculate cycle time
*
* @access private
* @param array &$task
* @return integer
*/
private function calculateCycleTime(array &$task)
{
$end = (int) $task['date_completed'] ?: time();
$start = (int) $task['date_started'];
// Start date can be in the future when defined with the Gantt chart
if ($start > 0 && $end > $start) {
return $end - $start;
}
return 0;
}
/**
* Get the 1000 last created tasks
*
* @access private
* @param integer $project_id
* @return array
*/
private function getTasks($project_id)
{
return $this->db
->table(TaskModel::TABLE)
->columns('date_completed', 'date_creation', 'date_started')
->eq('project_id', $project_id)
->desc('id')
->limit(1000)
->findAll();
}
}
@@ -0,0 +1,154 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
use Kanboard\Model\TaskModel;
/**
* Average Time Spent by Column
*
* @package analytic
* @author Frederic Guillot
*/
class AverageTimeSpentColumnAnalytic extends Base
{
/**
* Build report
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function build($project_id)
{
$stats = $this->initialize($project_id);
$this->processTasks($stats, $project_id);
$this->calculateAverage($stats);
return $stats;
}
/**
* Initialize default values for each column
*
* @access private
* @param integer $project_id
* @return array
*/
private function initialize($project_id)
{
$stats = array();
$columns = $this->columnModel->getList($project_id);
foreach ($columns as $column_id => $column_title) {
$stats[$column_id] = array(
'count' => 0,
'time_spent' => 0,
'average' => 0,
'title' => $column_title,
);
}
return $stats;
}
/**
* Calculate time spent for each tasks for each columns
*
* @access private
* @param array $stats
* @param integer $project_id
*/
private function processTasks(array &$stats, $project_id)
{
$tasks = $this->getTasks($project_id);
foreach ($tasks as &$task) {
foreach ($this->getTaskTimeByColumns($task) as $column_id => $time_spent) {
if (isset($stats[$column_id])) {
$stats[$column_id]['count']++;
$stats[$column_id]['time_spent'] += $time_spent;
}
}
}
}
/**
* Calculate averages
*
* @access private
* @param array $stats
*/
private function calculateAverage(array &$stats)
{
foreach ($stats as &$column) {
$this->calculateColumnAverage($column);
}
}
/**
* Calculate column average
*
* @access private
* @param array $column
*/
private function calculateColumnAverage(array &$column)
{
if ($column['count'] > 0) {
$column['average'] = (int) ($column['time_spent'] / $column['count']);
}
}
/**
* Get time spent for each column for a given task
*
* @access private
* @param array $task
* @return array
*/
private function getTaskTimeByColumns(array &$task)
{
$columns = $this->transitionModel->getTimeSpentByTask($task['id']);
if (! isset($columns[$task['column_id']])) {
$columns[$task['column_id']] = 0;
}
$columns[$task['column_id']] += $this->getTaskTimeSpentInCurrentColumn($task);
return $columns;
}
/**
* Calculate time spent of a task in the current column
*
* @access private
* @param array $task
* @return integer
*/
private function getTaskTimeSpentInCurrentColumn(array &$task)
{
$end = $task['date_completed'] ?: time();
return $end - $task['date_moved'];
}
/**
* Fetch the last 1000 tasks
*
* @access private
* @param integer $project_id
* @return array
*/
private function getTasks($project_id)
{
return $this->db
->table(TaskModel::TABLE)
->columns('id', 'date_completed', 'date_moved', 'column_id')
->eq('project_id', $project_id)
->desc('id')
->limit(1000)
->findAll();
}
}
@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
use Kanboard\Model\TaskModel;
/**
* Estimated vs actual time per column
*
* @package analytic
* @author Frederic Guillot
*/
class EstimatedActualColumnAnalytic extends Base
{
/**
* Build report
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function build($project_id)
{
$rows = $this->db->table(TaskModel::TABLE)
->columns('SUM(time_estimated) AS hours_estimated', 'SUM(time_spent) AS hours_spent', 'column_id')
->eq('project_id', $project_id)
->groupBy('column_id')
->findAll();
$columns = $this->columnModel->getList($project_id);
$metrics = [];
foreach ($columns as $column_id => $column_title) {
$metrics[$column_id] = array(
'hours_spent' => 0,
'hours_estimated' => 0,
'title' => $column_title,
);
}
foreach ($rows as $row) {
$metrics[$row['column_id']]['hours_spent'] = (float) $row['hours_spent'];
$metrics[$row['column_id']]['hours_estimated'] = (float) $row['hours_estimated'];
}
return $metrics;
}
}
@@ -0,0 +1,50 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
use Kanboard\Model\TaskModel;
/**
* Estimated/Spent Time Comparison
*
* @package analytic
* @author Frederic Guillot
*/
class EstimatedTimeComparisonAnalytic extends Base
{
/**
* Build report
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function build($project_id)
{
$rows = $this->db->table(TaskModel::TABLE)
->columns('SUM(time_estimated) AS time_estimated', 'SUM(time_spent) AS time_spent', 'is_active')
->eq('project_id', $project_id)
->groupBy('is_active')
->findAll();
$metrics = array(
'open' => array(
'time_spent' => 0,
'time_estimated' => 0,
),
'closed' => array(
'time_spent' => 0,
'time_estimated' => 0,
),
);
foreach ($rows as $row) {
$key = $row['is_active'] == TaskModel::STATUS_OPEN ? 'open' : 'closed';
$metrics[$key]['time_spent'] = (float) $row['time_spent'];
$metrics[$key]['time_estimated'] = (float) $row['time_estimated'];
}
return $metrics;
}
}
+48
View File
@@ -0,0 +1,48 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
/**
* Task Distribution
*
* @package analytic
* @author Frederic Guillot
*/
class TaskDistributionAnalytic extends Base
{
/**
* Build report
*
* @access public
* @param integer $project_id Project id
* @return array
*/
public function build($project_id)
{
$metrics = array();
$total = 0;
$columns = $this->columnModel->getAll($project_id);
foreach ($columns as $column) {
$nb_tasks = $this->taskFinderModel->countByColumnId($project_id, $column['id']);
$total += $nb_tasks;
$metrics[] = array(
'column_title' => $column['title'],
'nb_tasks' => $nb_tasks,
);
}
if ($total === 0) {
return array();
}
foreach ($metrics as &$metric) {
$metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2);
}
return $metrics;
}
}
+56
View File
@@ -0,0 +1,56 @@
<?php
namespace Kanboard\Analytic;
use Kanboard\Core\Base;
/**
* User Distribution
*
* @package analytic
* @author Frederic Guillot
*/
class UserDistributionAnalytic extends Base
{
/**
* Build Report
*
* @access public
* @param integer $project_id
* @return array
*/
public function build($project_id)
{
$metrics = array();
$total = 0;
$tasks = $this->taskFinderModel->getAll($project_id);
$users = $this->projectUserRoleModel->getAssignableUsersList($project_id);
foreach ($tasks as $task) {
$user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0];
$total++;
if (! isset($metrics[$user])) {
$metrics[$user] = array(
'nb_tasks' => 0,
'percentage' => 0,
'user' => $user,
);
}
$metrics[$user]['nb_tasks']++;
}
if ($total === 0) {
return array();
}
foreach ($metrics as &$metric) {
$metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2);
}
ksort($metrics);
return array_values($metrics);
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ActionAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ActionAuthorization extends ProjectAuthorization
{
public function check($class, $method, $action_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->actionModel->getProjectId($action_id));
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class CategoryAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CategoryAuthorization extends ProjectAuthorization
{
public function check($class, $method, $category_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->categoryModel->getProjectId($category_id));
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class ColumnAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ColumnAuthorization extends ProjectAuthorization
{
public function check($class, $method, $column_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->columnModel->getProjectId($column_id));
}
}
}
@@ -0,0 +1,46 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Security\Role;
/**
* Class CommentAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class CommentAuthorization extends ProjectAuthorization
{
public function check($class, $method, $comment_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->commentModel->getProjectId($comment_id));
$this->checkCommentAccess($comment_id);
}
}
/**
* @param $comment_id ID of the comment to check
* @return void
* @throws AccessDeniedException
*/
protected function checkCommentAccess($comment_id)
{
if (empty($comment_id)) {
throw new AccessDeniedException('Comment Not Found');
}
$commentVisibility = $this->commentModel->getVisibility($comment_id);
$userRole = $this->userSession->getRole();
if ($userRole === Role::APP_MANAGER && $commentVisibility === Role::APP_ADMIN) {
throw new AccessDeniedException('Comment Access Denied');
}
if ($userRole === Role::APP_USER && $commentVisibility !== Role::APP_USER) {
throw new AccessDeniedException('Comment Access Denied');
}
}
}
@@ -0,0 +1,32 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProcedureAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProcedureAuthorization extends Base
{
private $userSpecificProcedures = array(
'getMe',
'getMyDashboard',
'getMyActivityStream',
'createMyPrivateProject',
'getMyProjectsList',
'getMyProjects',
'getMyOverdueTasks',
);
public function check($procedure)
{
if (! $this->userSession->isLogged() && in_array($procedure, $this->userSpecificProcedures)) {
throw new AccessDeniedException('This procedure is not available with the API credentials');
}
}
}
@@ -0,0 +1,35 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class ProjectAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class ProjectAuthorization extends Base
{
public function check($class, $method, $project_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $project_id);
}
}
protected function checkProjectPermission($class, $method, $project_id)
{
if (empty($project_id)) {
throw new AccessDeniedException('Project Not Found');
}
$role = $this->projectUserRoleModel->getUserRole($project_id, $this->userSession->getId());
if (! $this->apiProjectAuthorization->isAllowed($class, $method, $role)) {
throw new AccessDeniedException('Project Access Denied');
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class SubtaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class SubtaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $subtask_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->subtaskModel->getProjectId($subtask_id));
}
}
}
@@ -0,0 +1,23 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TagAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TagAuthorization extends ProjectAuthorization
{
public function check($class, $method, $tag_id)
{
if ($this->userSession->isLogged()) {
$tag = $this->tagModel->getById($tag_id);
if (! empty($tag)) {
$this->checkProjectPermission($class, $method, $tag['project_id']);
}
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskAuthorization extends ProjectAuthorization
{
public function check($class, $method, $task_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFinderModel->getProjectId($task_id));
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskFileAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskFileAuthorization extends ProjectAuthorization
{
public function check($class, $method, $file_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskFileModel->getProjectId($file_id));
}
}
}
@@ -0,0 +1,19 @@
<?php
namespace Kanboard\Api\Authorization;
/**
* Class TaskLinkAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class TaskLinkAuthorization extends ProjectAuthorization
{
public function check($class, $method, $task_link_id)
{
if ($this->userSession->isLogged()) {
$this->checkProjectPermission($class, $method, $this->taskLinkModel->getProjectId($task_link_id));
}
}
}
@@ -0,0 +1,22 @@
<?php
namespace Kanboard\Api\Authorization;
use JsonRPC\Exception\AccessDeniedException;
use Kanboard\Core\Base;
/**
* Class UserAuthorization
*
* @package Kanboard\Api\Authorization
* @author Frederic Guillot
*/
class UserAuthorization extends Base
{
public function check($class, $method)
{
if ($this->userSession->isLogged() && ! $this->apiAuthorization->isAllowed($class, $method, $this->userSession->getRole())) {
throw new AccessDeniedException('You are not allowed to access to this resource');
}
}
}
@@ -0,0 +1,101 @@
<?php
namespace Kanboard\Api\Middleware;
use JsonRPC\Exception\AccessDeniedException;
use JsonRPC\Exception\AuthenticationFailureException;
use JsonRPC\MiddlewareInterface;
use Kanboard\Auth\ApiAccessTokenAuth;
use Kanboard\Core\Base;
use Symfony\Contracts\EventDispatcher\Event;
/**
* Class AuthenticationApiMiddleware
*
* @package Kanboard\Api\Middleware
* @author Frederic Guillot
*/
class AuthenticationMiddleware extends Base implements MiddlewareInterface
{
/**
* Execute Middleware
*
* @access public
* @param string $username
* @param string $password
* @param string $procedureName
* @throws AccessDeniedException
* @throws AuthenticationFailureException
*/
public function execute($username, $password, $procedureName)
{
$this->dispatcher->dispatch(new Event, 'app.bootstrap');
session_set('scope', 'API');
if ($this->isUserAuthenticated($username, $password)) {
$this->userSession->initialize($this->userCacheDecorator->getByUsername($username));
} elseif (! $this->isAppAuthenticated($username, $password)) {
$this->logger->error('API authentication failure for '.$username);
throw new AuthenticationFailureException('Wrong credentials');
}
}
/**
* Check user credentials
*
* @access public
* @param string $username
* @param string $password
* @return boolean
*/
private function isUserAuthenticated($username, $password)
{
if ($username === 'jsonrpc') {
return false;
}
if ($this->userLockingModel->isLocked($username)) {
return false;
}
if ($this->userModel->has2FA($username)) {
$this->logger->info('This API user ('.$username.') as 2FA enabled: only API keys are authorized');
$this->authenticationManager->reset();
$this->authenticationManager->register(new ApiAccessTokenAuth($this->container));
}
return $this->authenticationManager->passwordAuthentication($username, $password);
}
/**
* Check administrative credentials
*
* @access public
* @param string $username
* @param string $password
* @return boolean
*/
private function isAppAuthenticated($username, $password)
{
return $username === 'jsonrpc' && hash_equals($this->getApiToken(), $password);
}
/**
* Get API Token
*
* @access private
* @return string
*/
private function getApiToken()
{
if (defined('API_AUTHENTICATION_TOKEN')) {
return API_AUTHENTICATION_TOKEN;
}
if (getenv('API_AUTHENTICATION_TOKEN')) {
return getenv('API_AUTHENTICATION_TOKEN');
}
return $this->configModel->get('api_token');
}
}
+91
View File
@@ -0,0 +1,91 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ActionAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Action API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ActionProcedure extends BaseProcedure
{
public function getAvailableActions()
{
return (object) $this->actionManager->getAvailableActions();
}
public function getAvailableActionEvents()
{
return (object) $this->eventManager->getAll();
}
public function getCompatibleActionEvents($action_name)
{
return (object) $this->actionManager->getCompatibleEvents($action_name);
}
public function removeAction($action_id)
{
ActionAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAction', $action_id);
return $this->actionModel->remove($action_id);
}
public function getActions($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActions', $project_id);
return $this->actionModel->getAllByProject($project_id);
}
public function createAction($project_id, $event_name, $action_name, array $params)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createAction', $project_id);
$values = array(
'project_id' => $project_id,
'event_name' => $event_name,
'action_name' => $action_name,
'params' => $params,
);
list($valid, ) = $this->actionValidator->validateCreation($values);
if (! $valid) {
return false;
}
// Check if the action exists
$actions = $this->actionManager->getAvailableActions();
if (! isset($actions[$action_name])) {
return false;
}
// Check the event
$action = $this->actionManager->getAction($action_name);
if (! in_array($event_name, $action->getEvents())) {
return false;
}
$required_params = $action->getActionRequiredParameters();
// Check missing parameters
foreach ($required_params as $param => $value) {
if (! isset($params[$param])) {
return false;
}
}
// Check extra parameters
foreach ($params as $param => $value) {
if (! isset($required_params[$param])) {
return false;
}
}
return $this->actionModel->create($values);
}
}
+47
View File
@@ -0,0 +1,47 @@
<?php
namespace Kanboard\Api\Procedure;
/**
* App API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class AppProcedure extends BaseProcedure
{
public function getTimezone()
{
return $this->timezoneModel->getCurrentTimezone();
}
public function getVersion()
{
return APP_VERSION;
}
public function getDefaultTaskColor()
{
return $this->colorModel->getDefaultColor();
}
public function getDefaultTaskColors()
{
return $this->colorModel->getDefaultColors();
}
public function getColorList()
{
return $this->colorModel->getList();
}
public function getApplicationRoles()
{
return $this->role->getApplicationRoles();
}
public function getProjectRoles()
{
return $this->role->getProjectRoles();
}
}
+40
View File
@@ -0,0 +1,40 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProcedureAuthorization;
use Kanboard\Api\Authorization\UserAuthorization;
use Kanboard\Core\Base;
use ReflectionClass;
/**
* Base class
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
abstract class BaseProcedure extends Base
{
public function beforeProcedure($procedure)
{
ProcedureAuthorization::getInstance($this->container)->check($procedure);
UserAuthorization::getInstance($this->container)->check($this->getClassName(), $procedure);
}
protected function filterValues(array $values)
{
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
return $values;
}
protected function getClassName()
{
$reflection = new ReflectionClass(get_called_class());
return $reflection->getShortName();
}
}
+24
View File
@@ -0,0 +1,24 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Board API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class BoardProcedure extends BaseProcedure
{
public function getBoard($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getBoard', $project_id);
return $this->boardFormatter
->withProjectId($project_id)
->withQuery($this->taskFinderModel->getExtendedQuery())
->format();
}
}
+61
View File
@@ -0,0 +1,61 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\CategoryAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Category API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class CategoryProcedure extends BaseProcedure
{
public function getCategory($category_id)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'getCategory', $category_id);
return $this->categoryModel->getById($category_id);
}
public function getAllCategories($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllCategories', $project_id);
return $this->categoryModel->getAll($project_id);
}
public function removeCategory($category_id)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeCategory', $category_id);
return $this->categoryModel->remove($category_id);
}
public function createCategory($project_id, $name, $color_id = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createCategory', $project_id);
$values = array(
'project_id' => $project_id,
'name' => $name,
'color_id' => $color_id,
);
list($valid, ) = $this->categoryValidator->validateCreation($values);
return $valid ? $this->categoryModel->create($values) : false;
}
public function updateCategory($id, $name, $color_id = null)
{
CategoryAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateCategory', $id);
$values = array(
'id' => $id,
'name' => $name,
'color_id' => $color_id,
);
list($valid, ) = $this->categoryValidator->validateModification($values);
return $valid && $this->categoryModel->update($values);
}
}
+66
View File
@@ -0,0 +1,66 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ColumnAuthorization;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Model\TaskModel;
/**
* Column API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ColumnProcedure extends BaseProcedure
{
public function getColumns($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumns', $project_id);
return $this->columnModel->getAll($project_id);
}
public function getColumn($column_id)
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'getColumn', $column_id);
return $this->columnModel->getById($column_id);
}
public function updateColumn($column_id, $title, $task_limit = 0, $description = '')
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateColumn', $column_id);
return $this->columnModel->update($column_id, $title, $task_limit, $description);
}
public function addColumn($project_id, $title, $task_limit = 0, $description = '')
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addColumn', $project_id);
return $this->columnModel->create($project_id, $title, $task_limit, $description);
}
public function removeColumn($column_id)
{
ColumnAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeColumn', $column_id);
$projectId = $this->columnModel->getProjectId($column_id);
$nbTasks = $this->taskFinderModel->countByColumnId($projectId, $column_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED));
if ($nbTasks > 0) {
$this->logger->error(__METHOD__.': This column cannot be removed because it contains '.$nbTasks.' tasks');
return false;
}
return $this->columnModel->remove($column_id);
}
public function changeColumnPosition($project_id, $column_id, $position)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeColumnPosition', $project_id);
if ((int) $this->columnModel->getProjectId($column_id) !== (int) $project_id) {
return false;
}
return $this->columnModel->changePosition($project_id, $column_id, $position);
}
}
+113
View File
@@ -0,0 +1,113 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\CommentAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Core\Security\Role;
/**
* Comment API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class CommentProcedure extends BaseProcedure
{
public function getComment($comment_id)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'getComment', $comment_id);
return $this->commentModel->getById($comment_id);
}
public function getAllComments($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllComments', $task_id);
$comments = $this->commentModel->getAll($task_id);
$filteredComments = [];
$userRole = $this->userSession->getRole();
foreach ($comments as $comment) {
if ($userRole === Role::APP_MANAGER && $comment['visibility'] === Role::APP_ADMIN) {
continue;
}
if ($userRole === Role::APP_USER && $comment['visibility'] !== Role::APP_USER) {
continue;
}
$filteredComments[] = $comment;
}
return $filteredComments;
}
public function removeComment($comment_id)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeComment', $comment_id);
if ($this->userSession->isLogged()) {
$loggedUserID = $this->userSession->getId();
$comment = $this->commentModel->getById($comment_id);
if ($comment['user_id'] != $loggedUserID) {
return false;
}
}
return $this->commentModel->remove($comment_id);
}
public function createComment($task_id, $user_id, $content, $reference = '', $visibility = Role::APP_USER)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createComment', $task_id);
if ($this->userSession->isLogged()) {
$loggedUserID = $this->userSession->getId();
if ($user_id != $loggedUserID) {
return false;
}
$userRole = $this->userSession->getRole();
if ($userRole === Role::APP_MANAGER && $visibility === Role::APP_ADMIN) {
return false;
}
if ($userRole === Role::APP_USER && $visibility !== Role::APP_USER) {
return false;
}
}
$values = array(
'task_id' => $task_id,
'user_id' => $user_id,
'comment' => $content,
'reference' => $reference,
'visibility' => $visibility,
);
list($valid, ) = $this->commentValidator->validateCreation($values);
return $valid ? $this->commentModel->create($values) : false;
}
public function updateComment($id, $content)
{
CommentAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateComment', $id);
if ($this->userSession->isLogged()) {
$loggedUserID = $this->userSession->getId();
$comment = $this->commentModel->getById($id);
if ($comment['user_id'] != $loggedUserID) {
return false;
}
}
$values = array(
'id' => $id,
'comment' => $content,
);
list($valid, ) = $this->commentValidator->validateModification($values);
return $valid && $this->commentModel->update($values);
}
}
@@ -0,0 +1,37 @@
<?php
namespace Kanboard\Api\Procedure;
/**
* Group Member API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class GroupMemberProcedure extends BaseProcedure
{
public function getMemberGroups($user_id)
{
return $this->groupMemberModel->getGroups($user_id);
}
public function getGroupMembers($group_id)
{
return $this->groupMemberModel->getMembers($group_id);
}
public function addGroupMember($group_id, $user_id)
{
return $this->groupMemberModel->addUser($group_id, $user_id);
}
public function removeGroupMember($group_id, $user_id)
{
return $this->groupMemberModel->removeUser($group_id, $user_id);
}
public function isGroupMember($group_id, $user_id)
{
return $this->groupMemberModel->isMember($group_id, $user_id);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Api\Procedure;
/**
* Group API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class GroupProcedure extends BaseProcedure
{
public function createGroup($name, $external_id = '')
{
return $this->groupModel->create($name, $external_id);
}
public function updateGroup($group_id, $name = null, $external_id = null)
{
$values = array(
'id' => $group_id,
'name' => $name,
'external_id' => $external_id,
);
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
return $this->groupModel->update($values);
}
public function removeGroup($group_id)
{
return $this->groupModel->remove($group_id);
}
public function getGroup($group_id)
{
return $this->groupModel->getById($group_id);
}
public function getAllGroups()
{
return $this->groupModel->getAll();
}
}
+111
View File
@@ -0,0 +1,111 @@
<?php
namespace Kanboard\Api\Procedure;
/**
* Link API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class LinkProcedure extends BaseProcedure
{
/**
* Get a link by id
*
* @access public
* @param integer $link_id Link id
* @return array
*/
public function getLinkById($link_id)
{
return $this->linkModel->getById($link_id);
}
/**
* Get a link by name
*
* @access public
* @param string $label
* @return array
*/
public function getLinkByLabel($label)
{
return $this->linkModel->getByLabel($label);
}
/**
* Get the opposite link id
*
* @access public
* @param integer $link_id Link id
* @return integer
*/
public function getOppositeLinkId($link_id)
{
return $this->linkModel->getOppositeLinkId($link_id);
}
/**
* Get all links
*
* @access public
* @return array
*/
public function getAllLinks()
{
return $this->linkModel->getAll();
}
/**
* Create a new link label
*
* @access public
* @param string $label
* @param string $opposite_label
* @return boolean|integer
*/
public function createLink($label, $opposite_label = '')
{
$values = array(
'label' => $label,
'opposite_label' => $opposite_label,
);
list($valid, ) = $this->linkValidator->validateCreation($values);
return $valid ? $this->linkModel->create($label, $opposite_label) : false;
}
/**
* Update a link
*
* @access public
* @param integer $link_id
* @param integer $opposite_link_id
* @param string $label
* @return boolean
*/
public function updateLink($link_id, $opposite_link_id, $label)
{
$values = array(
'id' => $link_id,
'opposite_id' => $opposite_link_id,
'label' => $label,
);
list($valid, ) = $this->linkValidator->validateModification($values);
return $valid && $this->linkModel->update($values);
}
/**
* Remove a link a the relation to its opposite
*
* @access public
* @param integer $link_id
* @return boolean
*/
public function removeLink($link_id)
{
return $this->linkModel->remove($link_id);
}
}
+67
View File
@@ -0,0 +1,67 @@
<?php
namespace Kanboard\Api\Procedure;
/**
* Me API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class MeProcedure extends BaseProcedure
{
public function getMe()
{
return session_get('user');
}
public function getMyDashboard()
{
$userId = $this->userSession->getId();
return $this->taskListSubtaskAssigneeFormatter
->withQuery($this->taskFinderModel->getUserQuery($userId))
->withUserId($userId)
->format();
}
public function getMyActivityStream()
{
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
return $this->helper->projectActivity->getProjectsEvents($project_ids, 100);
}
public function createMyPrivateProject($name, $description = null)
{
if ($this->configModel->get('disable_private_project', 0) == 1) {
return false;
}
$values = array(
'name' => $name,
'description' => $description,
'is_private' => 1,
);
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values, $this->userSession->getId(), true) : false;
}
public function getMyProjectsList()
{
return (object) $this->projectUserRoleModel->getProjectsByUser($this->userSession->getId());
}
public function getMyOverdueTasks()
{
return $this->taskFinderModel->getOverdueTasksByUser($this->userSession->getId());
}
public function getMyProjects()
{
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
$projects = $this->projectModel->getAllByIds($project_ids);
return $this->projectsApiFormatter->withProjects($projects)->format();
}
}
@@ -0,0 +1,80 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* Project File API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectFileProcedure extends BaseProcedure
{
public function getProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectFile', $project_id);
$file = $this->projectFileModel->getById($file_id);
if (empty($file) || (int) $file['project_id'] !== (int) $project_id) {
return false;
}
return $file;
}
public function getAllProjectFiles($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllProjectFiles', $project_id);
return $this->projectFileModel->getAll($project_id);
}
public function downloadProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadProjectFile', $project_id);
try {
$file = $this->projectFileModel->getById($file_id);
if (! empty($file) && (int) $file['project_id'] === (int) $project_id) {
return base64_encode($this->objectStorage->get($file['path']));
}
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return '';
}
public function createProjectFile($project_id, $filename, $blob)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createProjectFile', $project_id);
try {
return $this->projectFileModel->uploadContent($project_id, $filename, $blob);
} catch (ObjectStorageException $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function removeProjectFile($project_id, $file_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectFile', $project_id);
$file = $this->projectFileModel->getById($file_id);
if (empty($file) || (int) $file['project_id'] !== (int) $project_id) {
return false;
}
return $this->projectFileModel->remove($file_id);
}
public function removeAllProjectFiles($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllProjectFiles', $project_id);
return $this->projectFileModel->removeAll($project_id);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Class ProjectMetadataProcedure
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectMetadataProcedure extends BaseProcedure
{
public function getProjectMetadata($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectMetadata', $project_id);
return (object) $this->projectMetadataModel->getAll($project_id);
}
public function getProjectMetadataByName($project_id, $name)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectMetadataByName', $project_id);
return $this->projectMetadataModel->get($project_id, $name);
}
public function saveProjectMetadata($project_id, array $values)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'saveProjectMetadata', $project_id);
return $this->projectMetadataModel->save($project_id, $values);
}
public function removeProjectMetadata($project_id, $name)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectMetadata', $project_id);
return $this->projectMetadataModel->remove($project_id, $name);
}
}
@@ -0,0 +1,69 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Core\Security\Role;
/**
* Project Permission API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectPermissionProcedure extends BaseProcedure
{
public function getProjectUsers($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUsers', $project_id);
return (object) $this->projectUserRoleModel->getAllUsers($project_id);
}
public function getAssignableUsers($project_id, $prepend_unassigned = false)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAssignableUsers', $project_id);
return (object) $this->projectUserRoleModel->getAssignableUsersList($project_id, $prepend_unassigned);
}
public function addProjectUser($project_id, $user_id, $role = Role::PROJECT_MEMBER)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectUser', $project_id);
return $this->projectUserRoleModel->addUser($project_id, $user_id, $role);
}
public function addProjectGroup($project_id, $group_id, $role = Role::PROJECT_MEMBER)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addProjectGroup', $project_id);
return $this->projectGroupRoleModel->addGroup($project_id, $group_id, $role);
}
public function removeProjectUser($project_id, $user_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectUser', $project_id);
return $this->projectUserRoleModel->removeUser($project_id, $user_id);
}
public function removeProjectGroup($project_id, $group_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProjectGroup', $project_id);
return $this->projectGroupRoleModel->removeGroup($project_id, $group_id);
}
public function changeProjectUserRole($project_id, $user_id, $role)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectUserRole', $project_id);
return $this->projectUserRoleModel->changeUserRole($project_id, $user_id, $role);
}
public function changeProjectGroupRole($project_id, $group_id, $role)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeProjectGroupRole', $project_id);
return $this->projectGroupRoleModel->changeGroupRole($project_id, $group_id, $role);
}
public function getProjectUserRole($project_id, $user_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectUserRole', $project_id);
return $this->projectUserRoleModel->getUserRole($project_id, $user_id);
}
}
+152
View File
@@ -0,0 +1,152 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
/**
* Project API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class ProjectProcedure extends BaseProcedure
{
public function getProjectById($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectById', $project_id);
$project = $this->projectModel->getById($project_id);
return $this->projectApiFormatter->withProject($project)->format();
}
public function getProjectByName($name)
{
$project = $this->projectModel->getByName($name);
if (empty($project)) {
return false;
}
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByName', $project['id']);
return $this->projectApiFormatter->withProject($project)->format();
}
public function getProjectByIdentifier($identifier)
{
$project = $this->projectModel->getByIdentifier($identifier);
if (empty($project)) {
return false;
}
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByIdentifier', $project['id']);
return $this->projectApiFormatter->withProject($project)->format();
}
public function getProjectByEmail($email)
{
$project = $this->projectModel->getByEmail($email);
if (empty($project)) {
return false;
}
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectByEmail', $project['id']);
return $this->projectApiFormatter->withProject($project)->format();
}
public function getAllProjects()
{
return $this->projectsApiFormatter->withProjects($this->projectModel->getAll())->format();
}
public function removeProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeProject', $project_id);
return $this->projectModel->remove($project_id);
}
public function enableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProject', $project_id);
return $this->projectModel->enable($project_id);
}
public function disableProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProject', $project_id);
return $this->projectModel->disable($project_id);
}
public function enableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableProjectPublicAccess', $project_id);
return $this->projectModel->enablePublicAccess($project_id);
}
public function disableProjectPublicAccess($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableProjectPublicAccess', $project_id);
return $this->projectModel->disablePublicAccess($project_id);
}
public function getProjectActivities(array $project_ids)
{
foreach ($project_ids as $project_id) {
ProjectAuthorization::getInstance($this->container)
->check($this->getClassName(), 'getProjectActivities', $project_id);
}
return $this->helper->projectActivity->getProjectsEvents($project_ids);
}
public function getProjectActivity($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getProjectActivity', $project_id);
return $this->helper->projectActivity->getProjectEvents($project_id);
}
public function createProject($name, $description = null, $owner_id = 0, $identifier = null, $start_date = null, $end_date = null, $priority_default = null, $priority_start = null, $priority_end = null, $email = null)
{
if ($owner_id === 0 && $this->userSession->isLogged()) {
$owner_id = $this->userSession->getId();
}
$values = $this->filterValues(array(
'name' => $name,
'description' => $description,
'identifier' => $identifier,
'start_date' => $start_date,
'end_date' => $end_date,
'priority_default' => $priority_default,
'priority_start' => $priority_start,
'priority_end' => $priority_end,
'email' => $email,
));
list($valid, ) = $this->projectValidator->validateCreation($values);
return $valid ? $this->projectModel->create($values, $owner_id, $this->userSession->isLogged()) : false;
}
public function updateProject($project_id, $name = null, $description = null, $owner_id = null, $identifier = null, $start_date = null, $end_date = null, $priority_default = null, $priority_start = null, $priority_end = null, $email = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateProject', $project_id);
$values = $this->filterValues(array(
'id' => $project_id,
'name' => $name,
'description' => $description,
'owner_id' => $owner_id,
'identifier' => $identifier,
'start_date' => $start_date,
'end_date' => $end_date,
'priority_default' => $priority_default,
'priority_start' => $priority_start,
'priority_end' => $priority_end,
'email' => $email,
));
list($valid, ) = $this->projectValidator->validateModification($values);
return $valid && $this->projectModel->update($values);
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\SubtaskAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Subtask API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class SubtaskProcedure extends BaseProcedure
{
public function getSubtask($subtask_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtask', $subtask_id);
return $this->subtaskModel->getById($subtask_id);
}
public function getAllSubtasks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSubtasks', $task_id);
return $this->subtaskModel->getAll($task_id);
}
public function removeSubtask($subtask_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSubtask', $subtask_id);
return $this->subtaskModel->remove($subtask_id);
}
public function createSubtask($task_id, $title, $user_id = 0, $time_estimated = 0, $time_spent = 0, $status = 0)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createSubtask', $task_id);
$values = array(
'title' => $title,
'task_id' => $task_id,
'user_id' => $user_id,
'time_estimated' => $time_estimated,
'time_spent' => $time_spent,
'status' => $status,
);
list($valid, ) = $this->subtaskValidator->validateCreation($values);
return $valid ? $this->subtaskModel->create($values) : false;
}
public function updateSubtask($id, $task_id, $title = null, $user_id = null, $time_estimated = null, $time_spent = null, $status = null, $position = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSubtask', $task_id);
$subtask = $this->subtaskModel->getById($id);
if (empty($subtask) || (int) $subtask['task_id'] !== (int) $task_id) {
return false;
}
$values = array(
'id' => $id,
'task_id' => $task_id,
'title' => $title,
'user_id' => $user_id,
'time_estimated' => $time_estimated,
'time_spent' => $time_spent,
'status' => $status,
'position' => $position
);
foreach ($values as $key => $value) {
if (is_null($value)) {
unset($values[$key]);
}
}
list($valid, ) = $this->subtaskValidator->validateApiModification($values);
return $valid && $this->subtaskModel->update($values);
}
}
@@ -0,0 +1,39 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\SubtaskAuthorization;
/**
* Subtask Time Tracking API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
* @author Nikolaos Georgakis
*/
class SubtaskTimeTrackingProcedure extends BaseProcedure
{
public function hasSubtaskTimer($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'hasSubtaskTimer', $subtask_id);
return $this->subtaskTimeTrackingModel->hasTimer($subtask_id, $user_id);
}
public function setSubtaskStartTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskStartTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logStartTime($subtask_id, $user_id);
}
public function setSubtaskEndTime($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setSubtaskEndTime', $subtask_id);
return $this->subtaskTimeTrackingModel->logEndTime($subtask_id, $user_id);
}
public function getSubtaskTimeSpent($subtask_id, $user_id)
{
SubtaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSubtaskTimeSpent', $subtask_id);
return $this->subtaskTimeTrackingModel->getTimeSpent($subtask_id, $user_id);
}
}
+121
View File
@@ -0,0 +1,121 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Model\SwimlaneModel;
/**
* Swimlane API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class SwimlaneProcedure extends BaseProcedure
{
public function getActiveSwimlanes($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getActiveSwimlanes', $project_id);
return $this->swimlaneModel->getAllByStatus($project_id, SwimlaneModel::ACTIVE);
}
public function getAllSwimlanes($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllSwimlanes', $project_id);
return $this->swimlaneModel->getAll($project_id);
}
public function getSwimlaneById($swimlane_id)
{
$swimlane = $this->swimlaneModel->getById($swimlane_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneById', $swimlane['project_id']);
return $swimlane;
}
public function getSwimlaneByName($project_id, $name)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlaneByName', $project_id);
return $this->swimlaneModel->getByName($project_id, $name);
}
public function getSwimlane($swimlane_id)
{
$swimlane = $this->swimlaneModel->getById($swimlane_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getSwimlane', $swimlane['project_id']);
return $swimlane;
}
public function addSwimlane($project_id, $name, $description = '')
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'addSwimlane', $project_id);
return $this->swimlaneModel->create($project_id, $name, $description);
}
public function updateSwimlane($project_id, $swimlane_id, $name, $description = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateSwimlane', $project_id);
$swimlane = $this->swimlaneModel->getById($swimlane_id);
if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) {
return false;
}
$values = array(
'project_id' => $project_id,
'id' => $swimlane_id,
'name' => $name,
);
if (! is_null($description)) {
$values['description'] = $description;
}
list($valid, $errors) = $this->swimlaneValidator->validateModification($values);
if (! $valid) {
$this->logger->debug(__METHOD__.': Validation error: '.var_export($errors, true));
return false;
}
return $this->swimlaneModel->update($swimlane_id, $values);
}
public function removeSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeSwimlane', $project_id);
$swimlane = $this->swimlaneModel->getById($swimlane_id);
if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) {
return false;
}
return $this->swimlaneModel->remove($project_id, $swimlane_id);
}
public function disableSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'disableSwimlane', $project_id);
return $this->swimlaneModel->disable($project_id, $swimlane_id);
}
public function enableSwimlane($project_id, $swimlane_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'enableSwimlane', $project_id);
return $this->swimlaneModel->enable($project_id, $swimlane_id);
}
public function changeSwimlanePosition($project_id, $swimlane_id, $position)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'changeSwimlanePosition', $project_id);
$swimlane = $this->swimlaneModel->getById($swimlane_id);
if (empty($swimlane) || (int) $swimlane['project_id'] !== (int) $project_id) {
return false;
}
return $this->swimlaneModel->changePosition($project_id, $swimlane_id, $position);
}
}
+49
View File
@@ -0,0 +1,49 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TagAuthorization;
/**
* Class TagProcedure
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TagProcedure extends BaseProcedure
{
public function getAllTags()
{
if ($this->userSession->isLogged() && ! $this->userSession->isAdmin()) {
$project_ids = $this->projectPermissionModel->getActiveProjectIds($this->userSession->getId());
return $this->tagModel->getAllByProjectIds($project_ids);
}
return $this->tagModel->getAll();
}
public function getTagsByProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTagsByProject', $project_id);
return $this->tagModel->getAllByProject($project_id);
}
public function createTag($project_id, $tag, $color_id = null)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTag', $project_id);
return $this->tagModel->create($project_id, $tag, $color_id);
}
public function updateTag($tag_id, $tag, $color_id = null)
{
TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTag', $tag_id);
return $this->tagModel->update($tag_id, $tag, $color_id);
}
public function removeTag($tag_id)
{
TagAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTag', $tag_id);
return $this->tagModel->remove($tag_id);
}
}
@@ -0,0 +1,123 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Core\ExternalLink\ExternalLinkManager;
use Kanboard\Core\ExternalLink\ExternalLinkProviderNotFound;
/**
* Task External Link API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskExternalLinkProcedure extends BaseProcedure
{
public function getExternalTaskLinkTypes()
{
return $this->externalLinkManager->getTypes();
}
public function getExternalTaskLinkProviderDependencies($providerName)
{
try {
return $this->externalLinkManager->getProvider($providerName)->getDependencies();
} catch (ExternalLinkProviderNotFound $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function getExternalTaskLinkById($task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLink', $task_id);
$link = $this->taskExternalLinkModel->getById($link_id);
if (empty($link) || (int) $link['task_id'] !== (int) $task_id) {
return false;
}
return $link;
}
public function getAllExternalTaskLinks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getExternalTaskLinks', $task_id);
return $this->taskExternalLinkModel->getAll($task_id);
}
public function createExternalTaskLink($task_id, $url, $dependency, $type = ExternalLinkManager::TYPE_AUTO, $title = '')
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createExternalTaskLink', $task_id);
try {
$provider = $this->externalLinkManager
->setUserInputText($url)
->setUserInputType($type)
->find();
$link = $provider->getLink();
$values = array(
'task_id' => $task_id,
'title' => $title ?: $link->getTitle(),
'url' => $link->getUrl(),
'link_type' => $provider->getType(),
'dependency' => $dependency,
);
list($valid, $errors) = $this->externalLinkValidator->validateCreation($values);
if (! $valid) {
$this->logger->error(__METHOD__.': '.var_export($errors));
return false;
}
return $this->taskExternalLinkModel->create($values);
} catch (ExternalLinkProviderNotFound $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
}
return false;
}
public function updateExternalTaskLink($task_id, $link_id, $title = null, $url = null, $dependency = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateExternalTaskLink', $task_id);
$link = $this->taskExternalLinkModel->getById($link_id);
if (empty($link) || (int) $link['task_id'] !== (int) $task_id) {
return false;
}
$values = $this->filterValues(array(
'title' => $title,
'url' => $url,
'dependency' => $dependency,
));
$values = array_merge($link, $values);
list($valid, $errors) = $this->externalLinkValidator->validateModification($values);
if (! $valid) {
$this->logger->error(__METHOD__.': '.var_export($errors));
return false;
}
return $this->taskExternalLinkModel->update($values);
}
public function removeExternalTaskLink($task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeExternalTaskLink', $task_id);
$link = $this->taskExternalLinkModel->getById($link_id);
if (empty($link) || (int) $link['task_id'] !== (int) $task_id) {
return false;
}
return $this->taskExternalLinkModel->remove($link_id);
}
}
+81
View File
@@ -0,0 +1,81 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskFileAuthorization;
use Kanboard\Core\ObjectStorage\ObjectStorageException;
/**
* Task File API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskFileProcedure extends BaseProcedure
{
public function getTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskFile', $file_id);
return $this->taskFileModel->getById($file_id);
}
public function getAllTaskFiles($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskFiles', $task_id);
return $this->taskFileModel->getAll($task_id);
}
public function downloadTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'downloadTaskFile', $file_id);
try {
$file = $this->taskFileModel->getById($file_id);
if (! empty($file)) {
return base64_encode($this->objectStorage->get($file['path']));
}
} catch (ObjectStorageException $e) {
$this->logger->error($e->getMessage());
}
return '';
}
public function createTaskFile($project_id, $task_id, $filename, $blob)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $project_id);
if (!$this->taskFinderModel->exists($task_id)) {
return false;
}
$taskProjectID = $this->taskFinderModel->getProjectId($task_id);
if ($taskProjectID != $project_id) {
return false;
}
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskFile', $task_id);
try {
return $this->taskFileModel->uploadContent($task_id, $filename, $blob);
} catch (ObjectStorageException $e) {
$this->logger->error(__METHOD__.': '.$e->getMessage());
return false;
}
}
public function removeTaskFile($file_id)
{
TaskFileAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskFile', $file_id);
return $this->taskFileModel->remove($file_id);
}
public function removeAllTaskFiles($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeAllTaskFiles', $task_id);
return $this->taskFileModel->removeAll($task_id);
}
}
+109
View File
@@ -0,0 +1,109 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Api\Authorization\TaskLinkAuthorization;
/**
* TaskLink API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskLinkProcedure extends BaseProcedure
{
/**
* Get a task link
*
* @access public
* @param integer $task_link_id Task link id
* @return array
*/
public function getTaskLinkById($task_link_id)
{
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskLinkById', $task_link_id);
return $this->taskLinkModel->getById($task_link_id);
}
/**
* Get all links attached to a task
*
* @access public
* @param integer $task_id Task id
* @return array
*/
public function getAllTaskLinks($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTaskLinks', $task_id);
return $this->taskLinkModel->getAll($task_id);
}
/**
* Create a new link
*
* @access public
* @param integer $task_id Task id
* @param integer $opposite_task_id Opposite task id
* @param integer $link_id Link id
* @return integer Task link id
*/
public function createTaskLink($task_id, $opposite_task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTaskLink', $task_id);
if ($this->userSession->isLogged()) {
$opposite_task = $this->taskFinderModel->getById($opposite_task_id);
if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) {
return false;
}
}
return $this->taskLinkModel->create($task_id, $opposite_task_id, $link_id);
}
/**
* Update a task link
*
* @access public
* @param integer $task_link_id Task link id
* @param integer $task_id Task id
* @param integer $opposite_task_id Opposite task id
* @param integer $link_id Link id
* @return boolean
*/
public function updateTaskLink($task_link_id, $task_id, $opposite_task_id, $link_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTaskLink', $task_id);
$taskLink = $this->taskLinkModel->getById($task_link_id);
if (empty($taskLink) || (int) $taskLink['task_id'] !== (int) $task_id) {
return false;
}
if ($this->userSession->isLogged()) {
$opposite_task = $this->taskFinderModel->getById($opposite_task_id);
if (! $this->projectPermissionModel->isUserAllowed($opposite_task['project_id'], $this->userSession->getId())) {
return false;
}
}
return $this->taskLinkModel->update($task_link_id, $task_id, $opposite_task_id, $link_id);
}
/**
* Remove a link between two tasks
*
* @access public
* @param integer $task_link_id
* @return boolean
*/
public function removeTaskLink($task_link_id)
{
TaskLinkAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskLink', $task_link_id);
return $this->taskLinkModel->remove($task_link_id);
}
}
@@ -0,0 +1,38 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Class TaskMetadataProcedure
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskMetadataProcedure extends BaseProcedure
{
public function getTaskMetadata($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskMetadata', $task_id);
return (object) $this->taskMetadataModel->getAll($task_id);
}
public function getTaskMetadataByName($task_id, $name)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskMetadataByName', $task_id);
return $this->taskMetadataModel->get($task_id, $name);
}
public function saveTaskMetadata($task_id, array $values)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'saveTaskMetadata', $task_id);
return $this->taskMetadataModel->save($task_id, $values);
}
public function removeTaskMetadata($task_id, $name)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTaskMetadata', $task_id);
return $this->taskMetadataModel->remove($task_id, $name);
}
}
+222
View File
@@ -0,0 +1,222 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
use Kanboard\Filter\TaskProjectFilter;
use Kanboard\Model\TaskModel;
/**
* Task API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskProcedure extends BaseProcedure
{
public function searchTasks($project_id, $query)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'searchTasks', $project_id);
return $this->taskLexer->build($query)->withFilter(new TaskProjectFilter($project_id))->toArray();
}
public function getTask($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTask', $task_id);
$task = $this->taskFinderModel->getById($task_id);
return $this->taskApiFormatter->withTask($task)->format();
}
public function getTaskByReference($project_id, $reference)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskByReference', $project_id);
$task = $this->taskFinderModel->getByReference($project_id, $reference);
return $this->taskApiFormatter->withTask($task)->format();
}
public function getAllTasks($project_id, $status_id = TaskModel::STATUS_OPEN)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getAllTasks', $project_id);
$tasks = $this->taskFinderModel->getAll($project_id, $status_id);
return $this->tasksApiFormatter->withTasks($tasks)->format();
}
public function getOverdueTasks()
{
return $this->taskFinderModel->getOverdueTasks();
}
public function getOverdueTasksByProject($project_id)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'getOverdueTasksByProject', $project_id);
return $this->taskFinderModel->getOverdueTasksByProject($project_id);
}
public function openTask($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'openTask', $task_id);
return $this->taskStatusModel->open($task_id);
}
public function closeTask($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'closeTask', $task_id);
return $this->taskStatusModel->close($task_id);
}
public function removeTask($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'removeTask', $task_id);
return $this->taskModel->remove($task_id);
}
public function moveTaskPosition($project_id, $task_id, $column_id, $position, $swimlane_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $task_id);
$taskProjectId = $this->taskFinderModel->getProjectId($task_id);
if ($taskProjectId !== (int) $project_id) {
return false;
}
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskPosition', $project_id);
return $this->taskPositionModel->movePosition($project_id, $task_id, $column_id, $position, $swimlane_id, true, false);
}
public function moveTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $task_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'moveTaskToProject', $project_id);
return $this->taskProjectMoveModel->moveToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function duplicateTaskToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $task_id);
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'duplicateTaskToProject', $project_id);
return $this->taskProjectDuplicationModel->duplicateToProject($task_id, $project_id, $swimlane_id, $column_id, $category_id, $owner_id);
}
public function createTask(
$title,
$project_id,
$color_id = '',
$column_id = 0,
$owner_id = 0,
$creator_id = 0,
$date_due = '',
$description = '',
$category_id = 0,
$score = 0,
$swimlane_id = null,
$priority = 0,
$recurrence_status = 0,
$recurrence_trigger = 0,
$recurrence_factor = 0,
$recurrence_timeframe = 0,
$recurrence_basedate = 0,
$reference = '',
array $tags = array(),
$date_started = '',
$time_spent = null,
$time_estimated = null
) {
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'createTask', $project_id);
if ($owner_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false;
}
if ($creator_id !== 0 && ! $this->projectPermissionModel->isAssignable($project_id, $creator_id)) {
return false;
}
$values = array(
'title' => $title,
'project_id' => $project_id,
'color_id' => $color_id,
'column_id' => $column_id,
'owner_id' => $owner_id,
'creator_id' => $creator_id,
'date_due' => $date_due,
'description' => $description,
'category_id' => $category_id,
'score' => $score,
'swimlane_id' => $swimlane_id,
'recurrence_status' => $recurrence_status,
'recurrence_trigger' => $recurrence_trigger,
'recurrence_factor' => $recurrence_factor,
'recurrence_timeframe' => $recurrence_timeframe,
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
'tags' => $tags,
'date_started' => $date_started,
'time_spent' => $time_spent,
'time_estimated' => $time_estimated,
);
list($valid, ) = $this->taskValidator->validateCreation($values);
return $valid ? $this->taskCreationModel->create($values) : false;
}
public function updateTask(
$id,
$title = null,
$color_id = null,
$owner_id = null,
$date_due = null,
$description = null,
$category_id = null,
$score = null,
$priority = null,
$recurrence_status = null,
$recurrence_trigger = null,
$recurrence_factor = null,
$recurrence_timeframe = null,
$recurrence_basedate = null,
$reference = null,
$tags = null,
$date_started = null,
$time_spent = null,
$time_estimated = null
) {
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'updateTask', $id);
$project_id = $this->taskFinderModel->getProjectId($id);
if ($project_id === 0) {
return false;
}
if ($owner_id !== null && $owner_id != 0 && ! $this->projectPermissionModel->isAssignable($project_id, $owner_id)) {
return false;
}
$values = $this->filterValues(array(
'id' => $id,
'title' => $title,
'color_id' => $color_id,
'owner_id' => $owner_id,
'date_due' => $date_due,
'description' => $description,
'category_id' => $category_id,
'score' => $score,
'recurrence_status' => $recurrence_status,
'recurrence_trigger' => $recurrence_trigger,
'recurrence_factor' => $recurrence_factor,
'recurrence_timeframe' => $recurrence_timeframe,
'recurrence_basedate' => $recurrence_basedate,
'reference' => $reference,
'priority' => $priority,
'tags' => $tags,
'date_started' => $date_started,
'time_spent' => $time_spent,
'time_estimated' => $time_estimated,
));
list($valid) = $this->taskValidator->validateApiModification($values);
return $valid && $this->taskModificationModel->update($values);
}
}
+28
View File
@@ -0,0 +1,28 @@
<?php
namespace Kanboard\Api\Procedure;
use Kanboard\Api\Authorization\ProjectAuthorization;
use Kanboard\Api\Authorization\TaskAuthorization;
/**
* Class TaskTagProcedure
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class TaskTagProcedure extends BaseProcedure
{
public function setTaskTags($project_id, $task_id, array $tags)
{
ProjectAuthorization::getInstance($this->container)->check($this->getClassName(), 'setTaskTags', $project_id);
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'setTaskTags', $task_id);
return $this->taskTagModel->save($project_id, $task_id, $tags);
}
public function getTaskTags($task_id)
{
TaskAuthorization::getInstance($this->container)->check($this->getClassName(), 'getTaskTags', $task_id);
return (object) $this->taskTagModel->getList($task_id);
}
}
+147
View File
@@ -0,0 +1,147 @@
<?php
namespace Kanboard\Api\Procedure;
use LogicException;
use Kanboard\Core\Security\Role;
use Kanboard\Core\Ldap\Client as LdapClient;
use Kanboard\Core\Ldap\ClientException as LdapException;
use Kanboard\Core\Ldap\User as LdapUser;
use Kanboard\Notification\MailNotification;
use Kanboard\Notification\WebNotification;
/**
* User API controller
*
* @package Kanboard\Api\Procedure
* @author Frederic Guillot
*/
class UserProcedure extends BaseProcedure
{
public function getUser($user_id)
{
return $this->userModel->getById($user_id);
}
public function getUserByName($username)
{
return $this->userModel->getByUsername($username);
}
public function getAllUsers()
{
return $this->userModel->getAll();
}
public function removeUser($user_id)
{
return $this->userModel->remove($user_id);
}
public function disableUser($user_id)
{
return $this->userModel->disable($user_id);
}
public function enableUser($user_id)
{
return $this->userModel->enable($user_id);
}
public function isActiveUser($user_id)
{
return $this->userModel->isActive($user_id);
}
public function createUser($username, $password, $name = '', $email = '', $role = Role::APP_USER)
{
$values = array(
'username' => $username,
'password' => $password,
'confirmation' => $password,
'name' => $name,
'email' => $email,
'role' => $role,
);
list($valid, ) = $this->userValidator->validateCreation($values);
if ($valid) {
$user_id = $this->userModel->create($values);
if ($user_id !== false && $this->configModel->get('notifications_enabled', 0) == 1) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]);
}
return $user_id;
}
return false;
}
/**
* Create LDAP user in the database
*
* Only "anonymous" and "proxy" LDAP authentication are supported by this method
*
* User information will be fetched from the LDAP server
*
* @access public
* @param string $username
* @return bool|int
*/
public function createLdapUser($username)
{
if (LDAP_BIND_TYPE === 'user') {
$this->logger->error('LDAP authentication "user" is not supported by this API call');
return false;
}
try {
$ldap = LdapClient::connect();
$ldap->setLogger($this->logger);
$user = LdapUser::getUser($ldap, $username);
if ($user === null) {
$this->logger->info('User not found in LDAP server');
return false;
}
if ($user->getUsername() === '') {
throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME');
}
$values = array(
'username' => $user->getUsername(),
'name' => $user->getName(),
'email' => $user->getEmail(),
'role' => $user->getRole() ?: Role::APP_USER,
'is_ldap_user' => 1,
);
$user_id = $this->userModel->create($values);
if ($user_id !== false && $this->configModel->get('notifications_enabled', 0) == 1) {
$this->userNotificationTypeModel->saveSelectedTypes($user_id, [MailNotification::TYPE, WebNotification::TYPE]);
}
return $user_id;
} catch (LdapException $e) {
$this->logger->error($e->getMessage());
return false;
}
}
public function updateUser($id, $username = null, $name = null, $email = null, $role = null)
{
$values = $this->filterValues(array(
'id' => $id,
'username' => $username,
'name' => $name,
'email' => $email,
'role' => $role,
));
list($valid, ) = $this->userValidator->validateApiModification($values);
return $valid && $this->userModel->update($values);
}
}
+118
View File
@@ -0,0 +1,118 @@
<?php
namespace Kanboard\Auth;
use Kanboard\Core\Base;
use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
use Kanboard\Model\UserModel;
use Kanboard\User\DatabaseUserProvider;
/**
* API Access Token Authentication Provider
*
* @package Kanboard\Auth
* @author Frederic Guillot
*/
class ApiAccessTokenAuth extends Base implements PasswordAuthenticationProviderInterface
{
/**
* User properties
*
* @access protected
* @var array
*/
protected $userInfo = array();
/**
* Username
*
* @access protected
* @var string
*/
protected $username = '';
/**
* Password
*
* @access protected
* @var string
*/
protected $password = '';
/**
* Get authentication provider name
*
* @access public
* @return string
*/
public function getName()
{
return 'API Access Token';
}
/**
* Authenticate the user
*
* @access public
* @return boolean
*/
public function authenticate()
{
if (session_get('scope') !== 'API') {
return false;
}
$user = $this->db
->table(UserModel::TABLE)
->columns('id', 'password')
->eq('username', $this->username)
->eq('api_access_token', $this->password)
->notNull('api_access_token')
->eq('is_active', 1)
->findOne();
if (! empty($user)) {
$this->userInfo = $user;
return true;
}
return false;
}
/**
* Get user object
*
* @access public
* @return \Kanboard\User\DatabaseUserProvider
*/
public function getUser()
{
if (empty($this->userInfo)) {
return null;
}
return new DatabaseUserProvider($this->userInfo);
}
/**
* Set username
*
* @access public
* @param string $username
*/
public function setUsername($username)
{
$this->username = $username;
}
/**
* Set password
*
* @access public
* @param string $password
*/
public function setPassword($password)
{
$this->password = $password;
}
}
+126
View File
@@ -0,0 +1,126 @@
<?php
namespace Kanboard\Auth;
use Kanboard\Core\Base;
use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
use Kanboard\Core\Security\SessionCheckProviderInterface;
use Kanboard\Model\UserModel;
use Kanboard\User\DatabaseUserProvider;
/**
* Database Authentication Provider
*
* @package Kanboard\Auth
* @author Frederic Guillot
*/
class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterface, SessionCheckProviderInterface
{
/**
* User properties
*
* @access protected
* @var array
*/
protected $userInfo = array();
/**
* Username
*
* @access protected
* @var string
*/
protected $username = '';
/**
* Password
*
* @access protected
* @var string
*/
protected $password = '';
/**
* Get authentication provider name
*
* @access public
* @return string
*/
public function getName()
{
return 'Database';
}
/**
* Authenticate the user
*
* @access public
* @return boolean
*/
public function authenticate()
{
$user = $this->db
->table(UserModel::TABLE)
->columns('id', 'password')
->eq('username', $this->username)
->eq('disable_login_form', 0)
->eq('is_ldap_user', 0)
->eq('is_active', 1)
->findOne();
if (! empty($user) && password_verify($this->password, $user['password'])) {
$this->userInfo = $user;
return true;
}
return false;
}
/**
* Check if the user session is valid
*
* @access public
* @return boolean
*/
public function isValidSession()
{
return $this->userModel->isValidSession($this->userSession->getId(), $this->userSession->getRole());
}
/**
* Get user object
*
* @access public
* @return \Kanboard\User\DatabaseUserProvider
*/
public function getUser()
{
if (empty($this->userInfo)) {
return null;
}
return new DatabaseUserProvider($this->userInfo);
}
/**
* Set username
*
* @access public
* @param string $username
*/
public function setUsername($username)
{
$this->username = $username;
}
/**
* Set password
*
* @access public
* @param string $password
*/
public function setPassword($password)
{
$this->password = $password;
}
}
+176
View File
@@ -0,0 +1,176 @@
<?php
namespace Kanboard\Auth;
use LogicException;
use Kanboard\Core\Base;
use Kanboard\Core\Ldap\Client as LdapClient;
use Kanboard\Core\Ldap\ClientException as LdapException;
use Kanboard\Core\Ldap\User as LdapUser;
use Kanboard\Core\Security\PasswordAuthenticationProviderInterface;
/**
* LDAP Authentication Provider
*
* @package Kanboard\Auth
* @author Frederic Guillot
*/
class LdapAuth extends Base implements PasswordAuthenticationProviderInterface
{
/**
* User properties
*
* @access protected
* @var \Kanboard\User\LdapUserProvider
*/
protected $userInfo = null;
/**
* Username
*
* @access protected
* @var string
*/
protected $username = '';
/**
* Password
*
* @access protected
* @var string
*/
protected $password = '';
/**
* Get authentication provider name
*
* @access public
* @return string
*/
public function getName()
{
return 'LDAP';
}
/**
* Authenticate the user
*
* @access public
* @return boolean
*/
public function authenticate()
{
try {
$client = LdapClient::connect($this->getLdapUsername(), $this->getLdapPassword());
$client->setLogger($this->logger);
$user = LdapUser::getUser($client, $this->username);
if ($user === null) {
$this->logger->info('User ('.$this->username.') not found in LDAP server');
return false;
}
if ($user->getUsername() === '') {
throw new LogicException('Username not found in LDAP profile, check the parameter LDAP_USER_ATTRIBUTE_USERNAME');
}
$this->logger->info('Authenticate this user: '.$user->getDn());
if ($client->authenticate($user->getDn(), $this->password)) {
$this->userInfo = $user;
return true;
}
} catch (LdapException $e) {
$this->logger->error($e->getMessage());
}
return false;
}
/**
* Get user object
*
* @access public
* @return \Kanboard\User\LdapUserProvider
*/
public function getUser()
{
return $this->userInfo;
}
/**
* Set username
*
* @access public
* @param string $username
*/
public function setUsername($username)
{
$this->username = $username;
}
/**
* Set password
*
* @access public
* @param string $password
*/
public function setPassword($password)
{
$this->password = $password;
}
/**
* Get LDAP username (proxy auth)
*
* @access public
* @return string
*/
public function getLdapUsername()
{
switch ($this->getLdapBindType()) {
case 'proxy':
return LDAP_USERNAME;
case 'user':
return sprintf(LDAP_USERNAME, $this->username);
default:
return null;
}
}
/**
* Get LDAP password (proxy auth)
*
* @access public
* @return string
*/
public function getLdapPassword()
{
switch ($this->getLdapBindType()) {
case 'proxy':
return LDAP_PASSWORD;
case 'user':
return $this->password;
default:
return null;
}
}
/**
* Get LDAP bind type
*
* @access public
* @return integer
*/
public function getLdapBindType()
{
if (LDAP_BIND_TYPE !== 'user' && LDAP_BIND_TYPE !== 'proxy' && LDAP_BIND_TYPE !== 'anonymous') {
throw new LogicException('Wrong value for the parameter LDAP_BIND_TYPE');
}
return LDAP_BIND_TYPE;
}
}
+79
View File
@@ -0,0 +1,79 @@
<?php
namespace Kanboard\Auth;
use Kanboard\Core\Base;
use Kanboard\Core\Security\PreAuthenticationProviderInterface;
use Kanboard\User\DatabaseUserProvider;
/**
* RememberMe Cookie Authentication Provider
*
* @package Kanboard\Auth
* @author Frederic Guillot
*/
class RememberMeAuth extends Base implements PreAuthenticationProviderInterface
{
/**
* User properties
*
* @access protected
* @var array
*/
protected $userInfo = array();
/**
* Get authentication provider name
*
* @access public
* @return string
*/
public function getName()
{
return 'RememberMe';
}
/**
* Authenticate the user
*
* @access public
* @return boolean
*/
public function authenticate()
{
$credentials = $this->rememberMeCookie->read();
if ($credentials !== false) {
$session = $this->rememberMeSessionModel->find($credentials['token'], $credentials['sequence']);
if (! empty($session)) {
$this->rememberMeCookie->write(
$session['token'],
$this->rememberMeSessionModel->updateSequence($session['token']),
$session['expiration']
);
$this->userInfo = $this->userModel->getById($session['user_id']);
return true;
}
}
return false;
}
/**
* Get user object
*
* @access public
* @return DatabaseUserProvider
*/
public function getUser()
{
if (empty($this->userInfo)) {
return null;
}
return new DatabaseUserProvider($this->userInfo);
}
}

Some files were not shown because too many files have changed in this diff Show More