Added automatic actions based on a daily event
This commit is contained in:
@@ -125,7 +125,7 @@ abstract class Base extends \Kanboard\Core\Base
|
||||
$params[] = $key.'='.var_export($value, true);
|
||||
}
|
||||
|
||||
return $this->getName().'('.implode('|', $params).'])';
|
||||
return $this->getName().'('.implode('|', $params).')';
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
95
app/Action/TaskCloseNoActivity.php
Normal file
95
app/Action/TaskCloseNoActivity.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\Task;
|
||||
|
||||
/**
|
||||
* Close automatically a task after when inactive
|
||||
*
|
||||
* @package 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(Task::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 = $this->getParam('duration') * 86400;
|
||||
|
||||
foreach ($data['tasks'] as $task) {
|
||||
$duration = time() - $task['date_modification'];
|
||||
|
||||
if ($duration > $max) {
|
||||
$results[] = $this->taskStatus->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;
|
||||
}
|
||||
}
|
||||
124
app/Action/TaskEmailNoActivity.php
Normal file
124
app/Action/TaskEmailNoActivity.php
Normal file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Action;
|
||||
|
||||
use Kanboard\Model\Task;
|
||||
|
||||
/**
|
||||
* Email a task with no activity
|
||||
*
|
||||
* @package 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(
|
||||
Task::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 = $this->getParam('duration') * 86400;
|
||||
$user = $this->user->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->taskFinder->getDetails($task_id);
|
||||
|
||||
$this->emailClient->send(
|
||||
$user['email'],
|
||||
$user['name'] ?: $user['username'],
|
||||
$this->getParam('subject'),
|
||||
$this->template->render('notification/task_create', array('task' => $task, 'application_url' => $this->config->get('application_url')))
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
33
app/Console/Cronjob.php
Normal file
33
app/Console/Cronjob.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\ArrayInput;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Symfony\Component\Console\Output\NullOutput;
|
||||
|
||||
class Cronjob extends Base
|
||||
{
|
||||
private $commands = array(
|
||||
'projects:daily-stats',
|
||||
'notification:overdue-tasks',
|
||||
'trigger:tasks',
|
||||
);
|
||||
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('cronjob')
|
||||
->setDescription('Execute daily cronjob');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach ($this->commands as $command) {
|
||||
$job = $this->getApplication()->find($command);
|
||||
$job->run(new ArrayInput(array('command' => $command)), new NullOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
52
app/Console/TaskTrigger.php
Normal file
52
app/Console/TaskTrigger.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Console;
|
||||
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
use Kanboard\Model\Task;
|
||||
use Kanboard\Event\TaskListEvent;
|
||||
|
||||
class TaskTrigger extends Base
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('trigger:tasks')
|
||||
->setDescription('Trigger scheduler event for all tasks');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
foreach ($this->getProjectIds() as $project_id) {
|
||||
$tasks = $this->taskFinder->getAll($project_id);
|
||||
$nb_tasks = count($tasks);
|
||||
|
||||
if ($nb_tasks > 0) {
|
||||
$output->writeln('Trigger task event: project_id='.$project_id.', nb_tasks='.$nb_tasks);
|
||||
$this->sendEvent($tasks, $project_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function getProjectIds()
|
||||
{
|
||||
$listeners = $this->dispatcher->getListeners(Task::EVENT_DAILY_CRONJOB);
|
||||
$project_ids = array();
|
||||
|
||||
foreach ($listeners as $listener) {
|
||||
$project_ids[] = $listener[0]->getProjectId();
|
||||
}
|
||||
|
||||
return array_unique($project_ids);
|
||||
}
|
||||
|
||||
private function sendEvent(array &$tasks, $project_id)
|
||||
{
|
||||
$event = new TaskListEvent(array('project_id' => $project_id));
|
||||
$event->setTasks($tasks);
|
||||
|
||||
$this->dispatcher->dispatch(Task::EVENT_DAILY_CRONJOB, $event);
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ class EventManager
|
||||
Task::EVENT_CLOSE => t('Closing a task'),
|
||||
Task::EVENT_CREATE_UPDATE => t('Task creation or modification'),
|
||||
Task::EVENT_ASSIGNEE_CHANGE => t('Task assignee change'),
|
||||
Task::EVENT_DAILY_CRONJOB => t('Daily background job for tasks'),
|
||||
);
|
||||
|
||||
$events = array_merge($events, $this->events);
|
||||
|
||||
@@ -7,7 +7,7 @@ use Symfony\Component\EventDispatcher\Event as BaseEvent;
|
||||
|
||||
class GenericEvent extends BaseEvent implements ArrayAccess
|
||||
{
|
||||
private $container = array();
|
||||
protected $container = array();
|
||||
|
||||
public function __construct(array $values = array())
|
||||
{
|
||||
|
||||
11
app/Event/TaskListEvent.php
Normal file
11
app/Event/TaskListEvent.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
namespace Kanboard\Event;
|
||||
|
||||
class TaskListEvent extends GenericEvent
|
||||
{
|
||||
public function setTasks(array &$tasks)
|
||||
{
|
||||
$this->container['tasks'] =& $tasks;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@ class Task extends Base
|
||||
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
|
||||
const EVENT_OVERDUE = 'task.overdue';
|
||||
const EVENT_USER_MENTION = 'task.user.mention';
|
||||
const EVENT_DAILY_CRONJOB = 'task.cronjob.daily';
|
||||
|
||||
/**
|
||||
* Recurrence: status
|
||||
|
||||
@@ -23,12 +23,14 @@ use Kanboard\Action\TaskCloseColumn;
|
||||
use Kanboard\Action\TaskCreation;
|
||||
use Kanboard\Action\TaskDuplicateAnotherProject;
|
||||
use Kanboard\Action\TaskEmail;
|
||||
use Kanboard\Action\TaskEmailNoActivity;
|
||||
use Kanboard\Action\TaskMoveAnotherProject;
|
||||
use Kanboard\Action\TaskMoveColumnAssigned;
|
||||
use Kanboard\Action\TaskMoveColumnCategoryChange;
|
||||
use Kanboard\Action\TaskMoveColumnUnAssigned;
|
||||
use Kanboard\Action\TaskOpen;
|
||||
use Kanboard\Action\TaskUpdateStartDate;
|
||||
use Kanboard\Action\TaskCloseNoActivity;
|
||||
|
||||
/**
|
||||
* Action Provider
|
||||
@@ -63,9 +65,11 @@ class ActionProvider implements ServiceProviderInterface
|
||||
$container['actionManager']->register(new TaskAssignUser($container));
|
||||
$container['actionManager']->register(new TaskClose($container));
|
||||
$container['actionManager']->register(new TaskCloseColumn($container));
|
||||
$container['actionManager']->register(new TaskCloseNoActivity($container));
|
||||
$container['actionManager']->register(new TaskCreation($container));
|
||||
$container['actionManager']->register(new TaskDuplicateAnotherProject($container));
|
||||
$container['actionManager']->register(new TaskEmail($container));
|
||||
$container['actionManager']->register(new TaskEmailNoActivity($container));
|
||||
$container['actionManager']->register(new TaskMoveAnotherProject($container));
|
||||
$container['actionManager']->register(new TaskMoveColumnAssigned($container));
|
||||
$container['actionManager']->register(new TaskMoveColumnCategoryChange($container));
|
||||
|
||||
@@ -15,22 +15,25 @@
|
||||
|
||||
<?php if ($this->text->contains($param_name, 'column_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $columns_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $columns_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'user_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $users_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $users_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'project_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $projects_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $projects_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'color_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $colors_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $colors_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'category_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $categories_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $categories_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'link_id')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->select('params['.$param_name.']', $links_list, $values) ?><br/>
|
||||
<?= $this->form->select('params['.$param_name.']', $links_list, $values) ?>
|
||||
<?php elseif ($this->text->contains($param_name, 'duration')): ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->number('params['.$param_name.']', $values) ?>
|
||||
<?php else: ?>
|
||||
<?= $this->form->label($param_desc, $param_name) ?>
|
||||
<?= $this->form->text('params['.$param_name.']', $values) ?>
|
||||
|
||||
Reference in New Issue
Block a user