看板初始化提交

This commit is contained in:
zephyr
2026-06-01 21:23:12 -07:00
commit 54a842f4ab
2104 changed files with 241695 additions and 0 deletions
+34
View File
@@ -0,0 +1,34 @@
# Pass HTTP Authorization header via environment variable to PHP backend
# to make HTTP Basic Authentication work for Apache/FastCGI/php-fpm
# setups (required to authenticate over the API)
<IfModule mod_setenvif.c>
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0
</IfModule>
<IfModule mod_rewrite.c>
Options -MultiViews
<IfModule mod_env.c>
SetEnv HTTP_MOD_REWRITE On
</IfModule>
# Uncomment this line depending of your Apache configuration
# RewriteBase /
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^ index.php [QSA,L]
############################
## Uncomment the two lines below to enable force HTTPS capabilities
############################
# RewriteCond %{HTTPS} !=on
# RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
</IfModule>
<IfModule pagespeed_module>
ModPagespeed Off
</IfModule>
+140
View File
@@ -0,0 +1,140 @@
# Contributing to Kanboard
Kanboard is a free and open source Kanban project management software that welcomes contributions from the community.
## Project Status
**Important Note**: Kanboard is currently in maintenance mode. This means:
- The original author is not actively developing major new features
- New releases are published regularly based on community contributions
- Pull requests for bug fixes and small improvements are welcomed
- The project follows established guidelines for all contributions
## Ways to Contribute
### 🐛 Bug Reports
If you find a bug, please help us improve Kanboard by reporting it:
1. **Check existing issues** first to avoid duplicates
2. **Use the GitHub issue tracker** to report bugs
3. **Provide detailed information** including:
- Kanboard version
- PHP version
- Web server (Apache, Nginx, etc.)
- Database type and version
- Operating system
- Steps to reproduce the issue
- Expected vs actual behavior
- Screenshots, server and browser logs if applicable
### 🔧 Bug Fixes and Small Improvements
We welcome pull requests that fix bugs or make small improvements:
1. **Fork the repository** and create a new branch
2. **Keep changes focused** - one issue per pull request
3. **Test your changes** thoroughly
4. **Follow the existing code style**
5. **Submit a pull request** with a clear description
### 📚 Documentation
Help improve Kanboard's documentation:
- Fix typos or unclear explanations
- Add missing documentation for features
- Translate documentation to other languages
- Improve code comments
### 🌐 Translations
Kanboard supports multiple languages. Help translate the interface:
1. Check the `app/Locale` directory for existing translations
2. Create or update translation files
3. Follow the existing translation format
4. Test your translations in the application
Refer to the [Translation Guide](https://docs.kanboard.org/v1/dev/translations/) for more details.
## Development Setup
### Prerequisites
- PHP 8.1 or higher
- Web server (Apache, Nginx, or PHP built-in server)
- Database (MySQL, PostgreSQL, or SQLite)
- Composer (for dependency management)
### Local Development
1. **Clone the repository**:
```bash
git clone https://github.com/kanboard/kanboard.git
cd kanboard
```
2. **Install dependencies**:
```bash
composer install
```
3. **Set up your environment**:
- Copy `config.default.php` to `config.php`
- Configure your database settings
- Set up your web server to point to the project directory, or use the PHP built-in server:
```bash
php -S localhost:8000 -t .
```
4. **Run unit tests** to ensure everything is working:
```bash
make test-sqlite # or make test-mysql, make test-postgresql
```
### Testing
- Test your changes in different browsers
- Test with multiple database types (MySQL, PostgreSQL, SQLite)
- Test with different PHP versions if possible
- Ensure existing functionalities are not broken by your changes
- Run the unit tests and integration tests
## Pull Request Guidelines
Before submitting a pull request, please:
1. **Read the pull request template** (`.github/pull_request_template.md`)
2. **Create a focused branch** from `main` for your changes
3. **Write clear commit messages** using the [conventional commit format](https://www.conventionalcommits.org/)
4. **Keep your changes small and focused** - large PRs are harder to review
5. **Test your changes thoroughly** to ensure they work as expected
6. **Ensure your code passes all tests** and does not introduce new issues
7. **Add or update tests** if when appropriate
8. **Review your code for style and quality** before submitting
9. **Update documentation** if needed
## Code Style Guidelines
- Be consistent with existing code style
- Follow PSR-1 and PSR-2 coding standards
- Configure your code editor to use 4 spaces for indentation
- Use meaningful variable and function names
- Add comments for complex logic
- Keep functions and methods focused and small
- Use type hints where appropriate
Refer to [Kanboard's coding standards](https://docs.kanboard.org/v1/dev/coding_standards/) for more details.
## Resources
- **Official Website**: <https://kanboard.org/>
- **Documentation**: <https://docs.kanboard.org/>
- **Forums**: <https://kanboard.discourse.group/> or <https://github.com/orgs/kanboard/discussions>
- **GitHub Issues**: <https://github.com/kanboard/kanboard/issues>
## License
By contributing to Kanboard, you agree that your contributions will be licensed under the same [MIT License](LICENSE) that covers the project.
+2140
View File
File diff suppressed because it is too large Load Diff
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) Frédéric Guillot
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+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);
}
}

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