Added automatic actions based on a daily event
This commit is contained in:
parent
320c7971f6
commit
32e4a932c8
|
|
@ -6,6 +6,13 @@ New features:
|
|||
* Add project owner (Directly Responsible Individual)
|
||||
* Add configurable task priority
|
||||
* Add Greek translation
|
||||
* Add automatic actions to close tasks with no activity
|
||||
* Add automatic actions to send an email when there is no activity on a task
|
||||
* Regroup all daily background tasks in one command: "cronjob"
|
||||
|
||||
Improvements:
|
||||
|
||||
* Show progress for task links in board tooltips
|
||||
|
||||
Version 1.0.24
|
||||
--------------
|
||||
|
|
|
|||
|
|
@ -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).')';
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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())
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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) ?>
|
||||
|
|
|
|||
|
|
@ -62,9 +62,4 @@ This chart show the average lead and cycle time for the last 1000 tasks over tim
|
|||
|
||||
Those metrics are calculated and recorded every day for the whole project.
|
||||
|
||||
Don't forget to run the daily job for statistics calculation
|
||||
-------------------------------------------------------
|
||||
|
||||
To generate accurate analytic data, you should run the daily cronjob **project daily statistics**.
|
||||
|
||||
[Read the documentation of Kanboard CLI](cli.markdown)
|
||||
Note: Don't forget to run the [daily cronjob](cronjob.markdown) to have accurate statistics.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
Centos Installation
|
||||
===================
|
||||
|
||||
Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
||||
Centos 7
|
||||
--------
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Command Line Interface
|
|||
Kanboard provides a simple command line interface that can be used from any Unix terminal.
|
||||
This tool can be used only on the local machine.
|
||||
|
||||
This feature is useful to run commands outside the web server process by example running a huge report.
|
||||
This feature is useful to run commands outside of the web server processes.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
|
@ -28,6 +28,7 @@ Options:
|
|||
-v|vv|vvv, --verbose Increase the verbosity of messages: 1 for normal output, 2 for more verbose output and 3 for debug
|
||||
|
||||
Available commands:
|
||||
cronjob Execute daily cronjob
|
||||
help Displays help for a command
|
||||
list Lists commands
|
||||
export
|
||||
|
|
@ -42,6 +43,8 @@ Available commands:
|
|||
notification:overdue-tasks Send notifications for overdue tasks
|
||||
projects
|
||||
projects:daily-stats Calculate daily statistics for all projects
|
||||
trigger
|
||||
trigger:tasks Trigger scheduler event for all tasks
|
||||
```
|
||||
|
||||
Available commands
|
||||
|
|
@ -116,7 +119,7 @@ Emails will be sent to all users with notifications enabled.
|
|||
You can also display the overdue tasks with the flag `--show`:
|
||||
|
||||
```bash
|
||||
$ ./kanboard notification:overdue-tasks --show
|
||||
./kanboard notification:overdue-tasks --show
|
||||
+-----+---------+------------+------------+--------------+----------+
|
||||
| Id | Title | Due date | Project Id | Project name | Assignee |
|
||||
+-----+---------+------------+------------+--------------+----------+
|
||||
|
|
@ -125,20 +128,22 @@ $ ./kanboard notification:overdue-tasks --show
|
|||
+-----+---------+------------+------------+--------------+----------+
|
||||
```
|
||||
|
||||
Cronjob example:
|
||||
|
||||
```bash
|
||||
# Everyday at 8am we check for due tasks
|
||||
0 8 * * * cd /path/to/kanboard && ./kanboard notification:overdue-tasks >/dev/null 2>&1
|
||||
```
|
||||
|
||||
### Run daily project stats calculation
|
||||
|
||||
You can add a background task to calculate the project statistics every day:
|
||||
This command calculate the statistics of each project:
|
||||
|
||||
```bash
|
||||
$ ./kanboard projects:daily-stats
|
||||
./kanboard projects:daily-stats
|
||||
Run calculation for Project #0
|
||||
Run calculation for Project #1
|
||||
Run calculation for Project #10
|
||||
```
|
||||
|
||||
### Trigger for tasks
|
||||
|
||||
This command send a "daily cronjob event" to all open tasks of each project.
|
||||
|
||||
```bash
|
||||
./kanboard trigger:tasks
|
||||
Trigger task event: project_id=2, nb_tasks=1
|
||||
```
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
Background Job Scheduling
|
||||
=========================
|
||||
|
||||
To work properly, Kanboard requires that a background job run on a daily basis.
|
||||
Usually on Unix platforms, this process is done by `cron`.
|
||||
|
||||
This background job is necessary for these features:
|
||||
|
||||
- Reports and analytics (calculate daily stats of each projects)
|
||||
- Send overdue task notifications
|
||||
- Execute automatic actions connected to the event "Daily background job for tasks"
|
||||
|
||||
Configuration on Unix and Linux platforms
|
||||
-----------------------------------------
|
||||
|
||||
There are multiple ways to define a cronjob on Unix/Linux operating systems, this example is for Ubuntu 14.04.
|
||||
The procedure is similar to other systems.
|
||||
|
||||
Edit the crontab of your web server user:
|
||||
|
||||
```bash
|
||||
sudo crontab -u www-data -e
|
||||
```
|
||||
|
||||
Example to execute the daily cronjob at 8am:
|
||||
|
||||
```bash
|
||||
0 8 * * * cd /path/to/kanboard && ./kanboard cronjob >/dev/null 2>&1
|
||||
```
|
||||
|
||||
Note: the cronjob process must have write access to the database in case you are using Sqlite.
|
||||
Usually, running the cronjob under the web server user is enough.
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
How to install Kanboard on Debian?
|
||||
==================================
|
||||
|
||||
Note: Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
||||
Debian 8 (Jessie)
|
||||
-----------------
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Generally 3 elements have to be installed:
|
|||
Fetch and extract ports...
|
||||
|
||||
```bash
|
||||
$ portsnap fetch
|
||||
$ portsnap fetch
|
||||
$ portsnap extract
|
||||
```
|
||||
|
||||
|
|
@ -122,6 +122,7 @@ there is no need to install it manually.
|
|||
Please note
|
||||
-----------
|
||||
|
||||
Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment,
|
||||
- Port is being hosted on [bitbucket](https://bitbucket.org/if0/freebsd-kanboard/). Feel free to comment,
|
||||
fork and suggest updates!
|
||||
|
||||
- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
||||
|
|
|
|||
|
|
@ -35,5 +35,5 @@ heroku open
|
|||
Limitations
|
||||
-----------
|
||||
|
||||
The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot.
|
||||
We may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3).
|
||||
- The storage of Heroku is ephemeral, that means uploaded files through Kanboard are not persistent after a reboot. You may want to install a plugin to store your files in a cloud storage provider like [Amazon S3](https://github.com/kanboard/plugin-s3).
|
||||
- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
|
|
|||
|
|
@ -103,6 +103,7 @@ Technical details
|
|||
### Configuration
|
||||
|
||||
- [Config file](config.markdown)
|
||||
- [Background tasks](cronjob.markdown)
|
||||
- [Email configuration](email-configuration.markdown)
|
||||
- [URL rewriting](nice-urls.markdown)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,3 +39,8 @@ Security
|
|||
|
||||
- Don't forget to change the default user/password
|
||||
- Don't allow everybody to access to the directory `data` from the URL. There is already a `.htaccess` for Apache but nothing for Nginx.
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- Some features of Kanboard require that you run [a daily background job](cronjob.markdown)
|
||||
|
|
|
|||
|
|
@ -26,3 +26,5 @@ sudo unzip kanboard-latest.zip
|
|||
sudo chown -R www-data:www-data kanboard/data
|
||||
sudo rm kanboard-latest.zip
|
||||
```
|
||||
|
||||
Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
|
|
|||
|
|
@ -123,3 +123,8 @@ Tested configuration
|
|||
--------------------
|
||||
|
||||
- Windows 2008 R2 / Apache 2.4.12 / PHP 5.6.8
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
|
|
|||
|
|
@ -65,3 +65,9 @@ Tested configurations
|
|||
|
||||
- Windows 2008 R2 Standard Edition / IIS 7.5 / PHP 5.5.16
|
||||
- Windows 2012 Standard Edition / IIS 8.5 / PHP 5.3.29
|
||||
|
||||
Notes
|
||||
-----
|
||||
|
||||
- Some features of Kanboard require that you run [a daily background job](cronjob.markdown).
|
||||
|
||||
|
|
|
|||
4
kanboard
4
kanboard
|
|
@ -13,6 +13,8 @@ use Kanboard\Console\ProjectDailyColumnStatsExport;
|
|||
use Kanboard\Console\TransitionExport;
|
||||
use Kanboard\Console\LocaleSync;
|
||||
use Kanboard\Console\LocaleComparator;
|
||||
use Kanboard\Console\TaskTrigger;
|
||||
use Kanboard\Console\Cronjob;
|
||||
|
||||
$container['dispatcher']->dispatch('app.bootstrap', new Event);
|
||||
|
||||
|
|
@ -25,4 +27,6 @@ $application->add(new ProjectDailyColumnStatsExport($container));
|
|||
$application->add(new TransitionExport($container));
|
||||
$application->add(new LocaleSync($container));
|
||||
$application->add(new LocaleComparator($container));
|
||||
$application->add(new TaskTrigger($container));
|
||||
$application->add(new Cronjob($container));
|
||||
$application->run();
|
||||
|
|
|
|||
Loading…
Reference in New Issue