mirror of
https://github.com/YunoHost-Apps/kanboard_ynh.git
synced 2024-09-03 19:36:17 +02:00
Update kanboard v1.0.10
This commit is contained in:
parent
2af3292971
commit
6250d201a8
1431 changed files with 151190 additions and 4870 deletions
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Action;
|
||||
|
||||
use Pimple\Container;
|
||||
use Core\Listener;
|
||||
use Core\Registry;
|
||||
use Core\Tool;
|
||||
|
||||
/**
|
||||
|
@ -15,7 +15,11 @@ use Core\Tool;
|
|||
* @property \Model\Acl $acl
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskModification $taskModification
|
||||
* @property \Model\TaskDuplication $taskDuplication
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskStatus $taskStatus
|
||||
*/
|
||||
abstract class Base implements Listener
|
||||
{
|
||||
|
@ -44,12 +48,12 @@ abstract class Base implements Listener
|
|||
protected $event_name = '';
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $registry;
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
|
@ -101,13 +105,13 @@ abstract class Base implements Listener
|
|||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Regsitry instance
|
||||
* @param integer $project_id Project id
|
||||
* @param string $event_name Attached event name
|
||||
* @param \Pimple\Container $container Container
|
||||
* @param integer $project_id Project id
|
||||
* @param string $event_name Attached event name
|
||||
*/
|
||||
public function __construct(Registry $registry, $project_id, $event_name)
|
||||
public function __construct(Container $container, $project_id, $event_name)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->container = $container;
|
||||
$this->project_id = $project_id;
|
||||
$this->event_name = $event_name;
|
||||
}
|
||||
|
@ -132,7 +136,7 @@ abstract class Base implements Listener
|
|||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -61,7 +61,7 @@ class CommentCreation extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
return $this->comment->create(array(
|
||||
return (bool) $this->comment->create(array(
|
||||
'reference' => $data['reference'],
|
||||
'comment' => $data['comment'],
|
||||
'task_id' => $data['task_id'],
|
||||
|
|
|
@ -67,7 +67,7 @@ class TaskAssignCategoryColor extends Base
|
|||
'category_id' => $this->getParam('category_id'),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -67,7 +67,7 @@ class TaskAssignCategoryLabel extends Base
|
|||
'category_id' => isset($data['category_id']) ? $data['category_id'] : $this->getParam('category_id'),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -67,7 +67,7 @@ class TaskAssignColorCategory extends Base
|
|||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,7 +68,7 @@ class TaskAssignColorUser extends Base
|
|||
'color_id' => $this->getParam('color_id'),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -67,7 +67,7 @@ class TaskAssignCurrentUser extends Base
|
|||
'owner_id' => $this->acl->getUserId(),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,7 +68,7 @@ class TaskAssignSpecificUser extends Base
|
|||
'owner_id' => $this->getParam('user_id'),
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,7 +64,7 @@ class TaskAssignUser extends Base
|
|||
'owner_id' => $data['owner_id'],
|
||||
);
|
||||
|
||||
return $this->task->update($values, false);
|
||||
return $this->taskModification->update($values, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -71,7 +71,7 @@ class TaskClose extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
return $this->task->close($data['task_id']);
|
||||
return $this->taskStatus->close($data['task_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -59,7 +59,7 @@ class TaskCreation extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
return $this->task->create(array(
|
||||
return (bool) $this->taskCreation->create(array(
|
||||
'project_id' => $data['project_id'],
|
||||
'title' => $data['title'],
|
||||
'reference' => $data['reference'],
|
||||
|
|
|
@ -64,9 +64,7 @@ class TaskDuplicateAnotherProject extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
$task = $this->taskFinder->getById($data['task_id']);
|
||||
$this->task->duplicateToAnotherProject($this->getParam('project_id'), $task);
|
||||
return true;
|
||||
return (bool) $this->taskDuplication->duplicateToProject($data['task_id'], $this->getParam('project_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -64,9 +64,7 @@ class TaskMoveAnotherProject extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
$task = $this->taskFinder->getById($data['task_id']);
|
||||
$this->task->moveToAnotherProject($this->getParam('project_id'), $task);
|
||||
return true;
|
||||
return $this->taskDuplication->moveToProject($data['task_id'], $this->getParam('project_id'));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -56,7 +56,7 @@ class TaskOpen extends Base
|
|||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
return $this->task->open($data['task_id']);
|
||||
return $this->taskStatus->open($data['task_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace Auth;
|
||||
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use Pimple\Container;
|
||||
|
||||
/**
|
||||
* Base auth class
|
||||
|
@ -26,34 +26,34 @@ abstract class Base
|
|||
protected $db;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $registry;
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->db = $this->registry->shared('db');
|
||||
$this->container = $container;
|
||||
$this->db = $this->container['db'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Auth;
|
||||
|
||||
require __DIR__.'/../../vendor/OAuth/bootstrap.php';
|
||||
|
||||
use Core\Request;
|
||||
use OAuth\Common\Storage\Session;
|
||||
use OAuth\Common\Consumer\Credentials;
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace Auth;
|
||||
|
||||
require __DIR__.'/../../vendor/OAuth/bootstrap.php';
|
||||
|
||||
use Core\Request;
|
||||
use OAuth\Common\Storage\Session;
|
||||
use OAuth\Common\Consumer\Credentials;
|
||||
|
|
|
@ -104,7 +104,7 @@ class Ldap extends Base
|
|||
{
|
||||
$ldap = $this->connect();
|
||||
|
||||
if ($this->bind($ldap, $username, $password)) {
|
||||
if (is_resource($ldap) && $this->bind($ldap, $username, $password)) {
|
||||
return $this->search($ldap, $username, $password);
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,12 @@ class Ldap extends Base
|
|||
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
ldap_set_option($ldap, LDAP_OPT_NETWORK_TIMEOUT, 1);
|
||||
ldap_set_option($ldap, LDAP_OPT_TIMELIMIT, 1);
|
||||
|
||||
if (LDAP_START_TLS && ! @ldap_start_tls($ldap)) {
|
||||
die('Unable to use ldap_start_tls()');
|
||||
}
|
||||
|
||||
return $ldap;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ namespace Auth;
|
|||
|
||||
use Core\Request;
|
||||
use Core\Security;
|
||||
use Core\Tool;
|
||||
|
||||
/**
|
||||
* RememberMe model
|
||||
|
@ -96,7 +95,7 @@ class RememberMe extends Base
|
|||
// Update the sequence
|
||||
$this->writeCookie(
|
||||
$record['token'],
|
||||
$this->update($record['token'], $record['sequence']),
|
||||
$this->update($record['token']),
|
||||
$record['expiration']
|
||||
);
|
||||
|
||||
|
@ -137,7 +136,7 @@ class RememberMe extends Base
|
|||
// Update the sequence
|
||||
$this->writeCookie(
|
||||
$record['token'],
|
||||
$this->update($record['token'], $record['sequence']),
|
||||
$this->update($record['token']),
|
||||
$record['expiration']
|
||||
);
|
||||
}
|
||||
|
@ -238,17 +237,15 @@ class RememberMe extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @return string
|
||||
*/
|
||||
public function update($token, $sequence)
|
||||
public function update($token)
|
||||
{
|
||||
$new_sequence = Security::generateToken();
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('token', $token)
|
||||
->eq('sequence', $sequence)
|
||||
->update(array('sequence' => $new_sequence));
|
||||
|
||||
return $new_sequence;
|
||||
|
@ -311,7 +308,7 @@ class RememberMe extends Base
|
|||
$expiration,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
Tool::isHTTPS(),
|
||||
Request::isHTTPS(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
@ -344,7 +341,7 @@ class RememberMe extends Base
|
|||
time() - 3600,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
Tool::isHTTPS(),
|
||||
Request::isHTTPS(),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
|
57
sources/app/Console/Base.php
Normal file
57
sources/app/Console/Base.php
Normal file
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Core\Tool;
|
||||
use Pimple\Container;
|
||||
use Symfony\Component\Console\Command\Command;
|
||||
|
||||
/**
|
||||
* Base command class
|
||||
*
|
||||
* @package console
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Model\ProjectDailySummary $projectDailySummary
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
*/
|
||||
abstract class Base extends Command
|
||||
{
|
||||
/**
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
parent::__construct();
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
}
|
29
sources/app/Console/ProjectDailySummaryCalculation.php
Normal file
29
sources/app/Console/ProjectDailySummaryCalculation.php
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?php
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Model\Project;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProjectDailySummaryCalculation extends Base
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('projects:daily-summary')
|
||||
->setDescription('Calculate daily summary data for all projects');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$projects = $this->project->getAllByStatus(Project::ACTIVE);
|
||||
|
||||
foreach ($projects as $project) {
|
||||
$output->writeln('Run calculation for '.$project['name']);
|
||||
$this->projectDailySummary->updateTotals($project['id'], date('Y-m-d'));
|
||||
}
|
||||
}
|
||||
}
|
35
sources/app/Console/ProjectDailySummaryExport.php
Normal file
35
sources/app/Console/ProjectDailySummaryExport.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Core\Tool;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class ProjectDailySummaryExport extends Base
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:daily-project-summary')
|
||||
->setDescription('Daily project summary CSV export (number of tasks per column and per day)')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->projectDailySummary->getAggregatedMetrics(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Tool::csv($data);
|
||||
}
|
||||
}
|
||||
}
|
35
sources/app/Console/TaskExport.php
Normal file
35
sources/app/Console/TaskExport.php
Normal file
|
@ -0,0 +1,35 @@
|
|||
<?php
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Core\Tool;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TaskExport extends Base
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('export:tasks')
|
||||
->setDescription('Tasks CSV export')
|
||||
->addArgument('project_id', InputArgument::REQUIRED, 'Project id')
|
||||
->addArgument('start_date', InputArgument::REQUIRED, 'Start date (YYYY-MM-DD)')
|
||||
->addArgument('end_date', InputArgument::REQUIRED, 'End date (YYYY-MM-DD)');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$data = $this->taskExport->export(
|
||||
$input->getArgument('project_id'),
|
||||
$input->getArgument('start_date'),
|
||||
$input->getArgument('end_date')
|
||||
);
|
||||
|
||||
if (is_array($data)) {
|
||||
Tool::csv($data);
|
||||
}
|
||||
}
|
||||
}
|
69
sources/app/Console/TaskOverdueNotification.php
Normal file
69
sources/app/Console/TaskOverdueNotification.php
Normal file
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
namespace Console;
|
||||
|
||||
use Symfony\Component\Console\Helper\Table;
|
||||
use Symfony\Component\Console\Input\InputArgument;
|
||||
use Symfony\Component\Console\Input\InputInterface;
|
||||
use Symfony\Component\Console\Input\InputOption;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
class TaskOverdueNotification extends Base
|
||||
{
|
||||
protected function configure()
|
||||
{
|
||||
$this
|
||||
->setName('notification:overdue-tasks')
|
||||
->setDescription('Send notifications for overdue tasks')
|
||||
->addOption('show', null, InputOption::VALUE_NONE, 'Show sent overdue tasks');
|
||||
}
|
||||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$projects = array();
|
||||
$tasks = $this->taskFinder->getOverdueTasks();
|
||||
|
||||
// Group tasks by project
|
||||
foreach ($tasks as $task) {
|
||||
$projects[$task['project_id']][] = $task;
|
||||
}
|
||||
|
||||
// Send notifications for each project
|
||||
foreach ($projects as $project_id => $project_tasks) {
|
||||
|
||||
$users = $this->notification->getUsersList($project_id);
|
||||
|
||||
$this->notification->sendEmails(
|
||||
'task_due',
|
||||
$users,
|
||||
array('tasks' => $project_tasks, 'project' => $project_tasks[0]['project_name'])
|
||||
);
|
||||
}
|
||||
|
||||
if ($input->getOption('show')) {
|
||||
$this->showTable($output, $tasks);
|
||||
}
|
||||
}
|
||||
|
||||
public function showTable(OutputInterface $output, array $tasks)
|
||||
{
|
||||
$rows = array();
|
||||
|
||||
foreach ($tasks as $task) {
|
||||
$rows[] = array(
|
||||
$task['id'],
|
||||
$task['title'],
|
||||
date('Y-m-d', $task['date_due']),
|
||||
$task['project_id'],
|
||||
$task['project_name'],
|
||||
$task['assignee_name'] ?: $task['assignee_username'],
|
||||
);
|
||||
}
|
||||
|
||||
$table = new Table($output);
|
||||
$table
|
||||
->setHeaders(array('Id', 'Title', 'Due date', 'Project Id', 'Project name', 'Assignee'))
|
||||
->setRows($rows)
|
||||
->render();
|
||||
}
|
||||
}
|
|
@ -19,7 +19,7 @@ class Action extends Base
|
|||
{
|
||||
$project = $this->getProjectManagement();
|
||||
|
||||
$this->response->html($this->projectLayout('action_index', array(
|
||||
$this->response->html($this->projectLayout('action/index', array(
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'project' => $project,
|
||||
'actions' => $this->action->getAllByProject($project['id']),
|
||||
|
@ -27,11 +27,10 @@ class Action extends Base
|
|||
'available_events' => $this->action->getAvailableEvents(),
|
||||
'available_params' => $this->action->getAllActionParameters(),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'users_list' => $this->projectPermission->getUsersList($project['id']),
|
||||
'users_list' => $this->projectPermission->getMemberList($project['id']),
|
||||
'projects_list' => $this->project->getList(false),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
@ -50,11 +49,10 @@ class Action extends Base
|
|||
$this->response->redirect('?controller=action&action=index&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('action_event', array(
|
||||
$this->response->html($this->projectLayout('action/event', array(
|
||||
'values' => $values,
|
||||
'project' => $project,
|
||||
'events' => $this->action->getCompatibleEvents($values['action_name']),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
@ -83,16 +81,15 @@ class Action extends Base
|
|||
$projects_list = $this->project->getList(false);
|
||||
unset($projects_list[$project['id']]);
|
||||
|
||||
$this->response->html($this->projectLayout('action_params', array(
|
||||
$this->response->html($this->projectLayout('action/params', array(
|
||||
'values' => $values,
|
||||
'action_params' => $action_params,
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'users_list' => $this->projectPermission->getUsersList($project['id']),
|
||||
'users_list' => $this->projectPermission->getMemberList($project['id']),
|
||||
'projects_list' => $projects_list,
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
@ -140,12 +137,11 @@ class Action extends Base
|
|||
{
|
||||
$project = $this->getProjectManagement();
|
||||
|
||||
$this->response->html($this->projectLayout('action_remove', array(
|
||||
$this->response->html($this->projectLayout('action/remove', array(
|
||||
'action' => $this->action->getById($this->request->getIntegerParam('action_id')),
|
||||
'available_events' => $this->action->getAvailableEvents(),
|
||||
'available_actions' => $this->action->getAvailableActions(),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove an action')
|
||||
)));
|
||||
}
|
||||
|
|
128
sources/app/Controller/Analytic.php
Normal file
128
sources/app/Controller/Analytic.php
Normal file
|
@ -0,0 +1,128 @@
|
|||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Project Anaytic controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Analytic extends Base
|
||||
{
|
||||
/**
|
||||
* Common layout for analytic views
|
||||
*
|
||||
* @access private
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
* @return string
|
||||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
$params['analytic_content_for_layout'] = $this->template->load($template, $params);
|
||||
|
||||
return $this->template->layout('analytic/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show tasks distribution graph
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tasks()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$metrics = $this->projectAnalytic->getTaskRepartition($project['id']);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->json(array(
|
||||
'metrics' => $metrics,
|
||||
'labels' => array(
|
||||
'column_title' => t('Column'),
|
||||
'nb_tasks' => t('Number of tasks'),
|
||||
)
|
||||
));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->layout('analytic/tasks', array(
|
||||
'project' => $project,
|
||||
'metrics' => $metrics,
|
||||
'title' => t('Task repartition for "%s"', $project['name']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show users repartition
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$metrics = $this->projectAnalytic->getUserRepartition($project['id']);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->json(array(
|
||||
'metrics' => $metrics,
|
||||
'labels' => array(
|
||||
'user' => t('User'),
|
||||
'nb_tasks' => t('Number of tasks'),
|
||||
)
|
||||
));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->layout('analytic/users', array(
|
||||
'project' => $project,
|
||||
'metrics' => $metrics,
|
||||
'title' => t('User repartition for "%s"', $project['name']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show cumulative flow diagram
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function cfd()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
$from = $this->request->getStringParam('from', date('Y-m-d', strtotime('-1week')));
|
||||
$to = $this->request->getStringParam('to', date('Y-m-d'));
|
||||
|
||||
if (! empty($values)) {
|
||||
$from = $values['from'];
|
||||
$to = $values['to'];
|
||||
}
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->json(array(
|
||||
'columns' => array_values($this->board->getColumnsList($project['id'])),
|
||||
'metrics' => $this->projectDailySummary->getRawMetrics($project['id'], $from, $to),
|
||||
'labels' => array(
|
||||
'column' => t('Column'),
|
||||
'day' => t('Date'),
|
||||
'total' => t('Tasks'),
|
||||
)
|
||||
));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->layout('analytic/cfd', array(
|
||||
'values' => array(
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
),
|
||||
'display_graph' => $this->projectDailySummary->countDays($project['id'], $from, $to) >= 2,
|
||||
'project' => $project,
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => t('Cumulative flow diagram for "%s"', $project['name']),
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@
|
|||
namespace Controller;
|
||||
|
||||
use Model\Project as ProjectModel;
|
||||
use Model\SubTask as SubTaskModel;
|
||||
use Helper;
|
||||
|
||||
/**
|
||||
* Application controller
|
||||
|
@ -12,6 +14,16 @@ use Model\Project as ProjectModel;
|
|||
*/
|
||||
class App extends Base
|
||||
{
|
||||
/**
|
||||
* Check if the user is connected
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function status()
|
||||
{
|
||||
$this->response->text('OK');
|
||||
}
|
||||
|
||||
/**
|
||||
* Dashboard for the current user
|
||||
*
|
||||
|
@ -19,15 +31,155 @@ class App extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$user_id = $this->acl->getUserId();
|
||||
$projects = $this->projectPermission->getAllowedProjects($user_id);
|
||||
$paginate = $this->request->getStringParam('paginate', 'userTasks');
|
||||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$direction = $this->request->getStringParam('direction');
|
||||
$order = $this->request->getStringParam('order');
|
||||
|
||||
$this->response->html($this->template->layout('app_index', array(
|
||||
'board_selector' => $projects,
|
||||
'events' => $this->projectActivity->getProjects(array_keys($projects), 10),
|
||||
'tasks' => $this->taskFinder->getAllTasksByUser($user_id),
|
||||
'menu' => 'dashboard',
|
||||
$user_id = $this->acl->getUserId();
|
||||
$projects = $this->projectPermission->getMemberProjects($user_id);
|
||||
$project_ids = array_keys($projects);
|
||||
|
||||
$params = array(
|
||||
'title' => t('Dashboard'),
|
||||
)));
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($user_id),
|
||||
'events' => $this->projectActivity->getProjects($project_ids, 10),
|
||||
);
|
||||
|
||||
$params += $this->getTaskPagination($user_id, $paginate, $offset, $order, $direction);
|
||||
$params += $this->getSubtaskPagination($user_id, $paginate, $offset, $order, $direction);
|
||||
$params += $this->getProjectPagination($project_ids, $paginate, $offset, $order, $direction);
|
||||
|
||||
$this->response->html($this->template->layout('app/dashboard', $params));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tasks pagination
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
private function getTaskPagination($user_id, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$limit = 10;
|
||||
|
||||
if (! in_array($order, array('tasks.id', 'project_name', 'title', 'date_due'))) {
|
||||
$order = 'tasks.id';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'userTasks') {
|
||||
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'tasks' => $tasks,
|
||||
'task_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'userTasks'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $this->taskPaginator->countUserTasks($user_id),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtasks pagination
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
private function getSubtaskPagination($user_id, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$status = array(SubTaskModel::STATUS_TODO, SubTaskModel::STATUS_INPROGRESS);
|
||||
$limit = 10;
|
||||
|
||||
if (! in_array($order, array('tasks.id', 'project_name', 'status', 'title'))) {
|
||||
$order = 'tasks.id';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'userSubtasks') {
|
||||
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'subtasks' => $subtasks,
|
||||
'subtask_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'userSubtasks'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => $this->subtaskPaginator->countUserSubtasks($user_id, $status),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get projects pagination
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
private function getProjectPagination($project_ids, $paginate, $offset, $order, $direction)
|
||||
{
|
||||
$limit = 5;
|
||||
|
||||
if (! in_array($order, array('id', 'name'))) {
|
||||
$order = 'name';
|
||||
$direction = 'ASC';
|
||||
}
|
||||
|
||||
if ($paginate === 'projectSummaries') {
|
||||
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit, $order, $direction);
|
||||
}
|
||||
else {
|
||||
$offset = 0;
|
||||
$projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit);
|
||||
}
|
||||
|
||||
return array(
|
||||
'projects' => $projects,
|
||||
'project_pagination' => array(
|
||||
'controller' => 'app',
|
||||
'action' => 'index',
|
||||
'params' => array('paginate' => 'projectSummaries'),
|
||||
'direction' => $direction,
|
||||
'order' => $order,
|
||||
'total' => count($project_ids),
|
||||
'offset' => $offset,
|
||||
'limit' => $limit,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render Markdown Text and reply with the HTML Code
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function preview()
|
||||
{
|
||||
$payload = $this->request->getJson();
|
||||
|
||||
if (empty($payload['text'])) {
|
||||
$this->response->html('<p>'.t('Nothing to preview...').'</p>');
|
||||
}
|
||||
else {
|
||||
$this->response->html(Helper\markdown($payload['text']));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,9 +2,13 @@
|
|||
|
||||
namespace Controller;
|
||||
|
||||
use Pimple\Container;
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use Core\Security;
|
||||
use Core\Request;
|
||||
use Core\Response;
|
||||
use Core\Template;
|
||||
use Core\Session;
|
||||
use Model\LastLogin;
|
||||
|
||||
/**
|
||||
|
@ -13,57 +17,65 @@ use Model\LastLogin;
|
|||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Color $color
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskPermission $taskPermission
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\User $user
|
||||
* @property \Model\Webhook $webhook
|
||||
* @property \Model\Acl $acl
|
||||
* @property \Model\Authentication $authentication
|
||||
* @property \Model\Action $action
|
||||
* @property \Model\Board $board
|
||||
* @property \Model\Category $category
|
||||
* @property \Model\Color $color
|
||||
* @property \Model\Comment $comment
|
||||
* @property \Model\Config $config
|
||||
* @property \Model\DateParser $dateParser
|
||||
* @property \Model\File $file
|
||||
* @property \Model\LastLogin $lastLogin
|
||||
* @property \Model\Notification $notification
|
||||
* @property \Model\Project $project
|
||||
* @property \Model\ProjectPermission $projectPermission
|
||||
* @property \Model\ProjectAnalytic $projectAnalytic
|
||||
* @property \Model\ProjectDailySummary $projectDailySummary
|
||||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskModification $taskModification
|
||||
* @property \Model\TaskDuplication $taskDuplication
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskPermission $taskPermission
|
||||
* @property \Model\TaskStatus $taskStatus
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\CommentHistory $commentHistory
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\User $user
|
||||
* @property \Model\Webhook $webhook
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
/**
|
||||
* Request instance
|
||||
*
|
||||
* @accesss public
|
||||
* @accesss protected
|
||||
* @var \Core\Request
|
||||
*/
|
||||
public $request;
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* Response instance
|
||||
*
|
||||
* @accesss public
|
||||
* @accesss protected
|
||||
* @var \Core\Response
|
||||
*/
|
||||
public $response;
|
||||
protected $response;
|
||||
|
||||
/**
|
||||
* Template instance
|
||||
*
|
||||
* @accesss public
|
||||
* @accesss protected
|
||||
* @var \Core\Template
|
||||
*/
|
||||
public $template;
|
||||
protected $template;
|
||||
|
||||
/**
|
||||
* Session instance
|
||||
|
@ -71,37 +83,53 @@ abstract class Base
|
|||
* @accesss public
|
||||
* @var \Core\Session
|
||||
*/
|
||||
public $session;
|
||||
protected $session;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access private
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
private $registry;
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->container = $container;
|
||||
$this->request = new Request;
|
||||
$this->response = new Response;
|
||||
$this->session = new Session;
|
||||
$this->template = new Template;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function __destruct()
|
||||
{
|
||||
// foreach ($this->container['db']->getLogMessages() as $message) {
|
||||
// $this->container['logger']->addDebug($message);
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -112,7 +140,7 @@ abstract class Base
|
|||
public function beforeAction($controller, $action)
|
||||
{
|
||||
// Start the session
|
||||
$this->session->open(BASE_URL_DIRECTORY, SESSION_SAVE_PATH);
|
||||
$this->session->open(BASE_URL_DIRECTORY);
|
||||
|
||||
// HTTP secure headers
|
||||
$this->response->csp(array('style-src' => "'self' 'unsafe-inline'"));
|
||||
|
@ -133,6 +161,11 @@ abstract class Base
|
|||
|
||||
// Authentication
|
||||
if (! $this->authentication->isAuthenticated($controller, $action)) {
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user&action=login&redirect_query='.urlencode($this->request->getQueryString()));
|
||||
}
|
||||
|
||||
|
@ -154,6 +187,7 @@ abstract class Base
|
|||
{
|
||||
$models = array(
|
||||
'projectActivity', // Order is important
|
||||
'projectDailySummary',
|
||||
'action',
|
||||
'project',
|
||||
'webhook',
|
||||
|
@ -173,7 +207,7 @@ abstract class Base
|
|||
*/
|
||||
public function notfound($no_layout = false)
|
||||
{
|
||||
$this->response->html($this->template->layout('app_notfound', array(
|
||||
$this->response->html($this->template->layout('app/notfound', array(
|
||||
'title' => t('Page not found'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
|
@ -187,7 +221,7 @@ abstract class Base
|
|||
*/
|
||||
public function forbidden($no_layout = false)
|
||||
{
|
||||
$this->response->html($this->template->layout('app_forbidden', array(
|
||||
$this->response->html($this->template->layout('app/forbidden', array(
|
||||
'title' => t('Access Forbidden'),
|
||||
'no_layout' => $no_layout,
|
||||
)));
|
||||
|
@ -245,8 +279,10 @@ abstract class Base
|
|||
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['task_content_for_layout'] = $content;
|
||||
$params['title'] = $params['task']['project_name'].' > '.$params['task']['title'];
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
|
||||
return $this->template->layout('task_layout', $params);
|
||||
return $this->template->layout('task/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -261,9 +297,10 @@ abstract class Base
|
|||
{
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['project_content_for_layout'] = $content;
|
||||
$params['menu'] = 'projects';
|
||||
$params['title'] = $params['project']['name'] === $params['title'] ? $params['title'] : $params['project']['name'].' > '.$params['title'];
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
|
||||
return $this->template->layout('project_layout', $params);
|
||||
return $this->template->layout('project/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -42,27 +42,12 @@ class Board extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
$projects = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
$params = array(
|
||||
'errors' => array(),
|
||||
|
||||
$this->response->html($this->template->load('board/assignee', array(
|
||||
'values' => $task,
|
||||
'users_list' => $this->projectPermission->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
|
||||
$this->response->html($this->template->load('board_assignee', $params));
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html($this->template->layout('board_assignee', $params + array(
|
||||
'menu' => 'boards',
|
||||
'title' => t('Change assignee').' - '.$task['title'],
|
||||
)));
|
||||
}
|
||||
'users_list' => $this->projectPermission->getMemberList($project['id']),
|
||||
'project' => $project,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -77,7 +62,7 @@ class Board extends Base
|
|||
|
||||
list($valid,) = $this->taskValidator->validateAssigneeModification($values);
|
||||
|
||||
if ($valid && $this->task->update($values)) {
|
||||
if ($valid && $this->taskModification->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -96,27 +81,12 @@ class Board extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
$projects = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
$params = array(
|
||||
'errors' => array(),
|
||||
|
||||
$this->response->html($this->template->load('board/category', array(
|
||||
'values' => $task,
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
|
||||
$this->response->html($this->template->load('board_category', $params));
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html($this->template->layout('board_category', $params + array(
|
||||
'menu' => 'boards',
|
||||
'title' => t('Change category').' - '.$task['title'],
|
||||
)));
|
||||
}
|
||||
'project' => $project,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,7 +101,7 @@ class Board extends Base
|
|||
|
||||
list($valid,) = $this->taskValidator->validateCategoryModification($values);
|
||||
|
||||
if ($valid && $this->task->update($values)) {
|
||||
if ($valid && $this->taskModification->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -158,7 +128,7 @@ class Board extends Base
|
|||
}
|
||||
|
||||
// Display the board with a specific layout
|
||||
$this->response->html($this->template->layout('board_public', array(
|
||||
$this->response->html($this->template->layout('board/public', array(
|
||||
'project' => $project,
|
||||
'columns' => $this->board->get($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
|
@ -214,15 +184,12 @@ class Board extends Base
|
|||
|
||||
$this->user->storeLastSeenProjectId($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('board_index', array(
|
||||
'users' => $this->projectPermission->getUsersList($project['id'], true, true),
|
||||
'filters' => array('user_id' => UserModel::EVERYBODY_ID),
|
||||
$this->response->html($this->template->layout('board/index', array(
|
||||
'users' => $this->projectPermission->getMemberList($project['id'], true, true),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
'project' => $project,
|
||||
'board' => $this->board->get($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], true, true),
|
||||
'menu' => 'boards',
|
||||
'title' => $project['name'],
|
||||
'board_selector' => $board_selector,
|
||||
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
|
||||
|
@ -246,12 +213,11 @@ class Board extends Base
|
|||
$values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board/edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
@ -287,12 +253,11 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board/edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
@ -326,12 +291,11 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('board_edit', array(
|
||||
$this->response->html($this->projectLayout('board/edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + $data,
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
@ -359,10 +323,9 @@ class Board extends Base
|
|||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('board_remove', array(
|
||||
$this->response->html($this->projectLayout('board/remove', array(
|
||||
'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove a column from a board')
|
||||
)));
|
||||
}
|
||||
|
@ -379,16 +342,16 @@ class Board extends Base
|
|||
if ($project_id > 0 && $this->request->isAjax()) {
|
||||
|
||||
if (! $this->projectPermission->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->status(401);
|
||||
$this->response->text('Forbidden', 403);
|
||||
}
|
||||
|
||||
$values = $this->request->getValues();
|
||||
$values = $this->request->getJson();
|
||||
|
||||
if ($this->task->movePosition($project_id, $values['task_id'], $values['column_id'], $values['position'])) {
|
||||
if ($this->taskPosition->movePosition($project_id, $values['task_id'], $values['column_id'], $values['position'])) {
|
||||
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
$this->template->load('board/show', array(
|
||||
'project' => $this->project->getById($project_id),
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
|
||||
|
@ -403,7 +366,7 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
else {
|
||||
$this->response->status(401);
|
||||
$this->response->status(403);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -420,13 +383,13 @@ class Board extends Base
|
|||
$timestamp = $this->request->getIntegerParam('timestamp');
|
||||
|
||||
if ($project_id > 0 && ! $this->projectPermission->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
$this->response->text('Forbidden', 403);
|
||||
}
|
||||
|
||||
if ($this->project->isModifiedSince($project_id, $timestamp)) {
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
$this->template->load('board/show', array(
|
||||
'project' => $this->project->getById($project_id),
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'),
|
||||
|
@ -439,7 +402,77 @@ class Board extends Base
|
|||
}
|
||||
}
|
||||
else {
|
||||
$this->response->status(401);
|
||||
$this->response->status(403);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get subtasks on mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function subtasks()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->response->html($this->template->load('board/subtasks', array(
|
||||
'subtasks' => $this->subTask->getAll($task['id'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of a subtask from the mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function toggleSubtask()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$this->subTask->toggleStatus($this->request->getIntegerParam('subtask_id'));
|
||||
|
||||
$this->response->html($this->template->load('board/subtasks', array(
|
||||
'subtasks' => $this->subTask->getAll($task['id'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display all attachments during the task mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachments()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->load('board/files', array(
|
||||
'files' => $this->file->getAll($task['id'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display comments during a task mouseover
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function comments()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->load('board/comments', array(
|
||||
'comments' => $this->comment->getAll($task['id'])
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the description
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->template->load('board/description', array(
|
||||
'task' => $task
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,16 +34,15 @@ class Category extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
public function index(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
|
||||
$this->response->html($this->projectLayout('category_index', array(
|
||||
$this->response->html($this->projectLayout('category/index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'errors' => array(),
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
@ -71,14 +70,7 @@ class Category extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('category_index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
$this->index($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -86,16 +78,15 @@ class Category extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('category_edit', array(
|
||||
'values' => $category,
|
||||
'errors' => array(),
|
||||
$this->response->html($this->projectLayout('category/edit', array(
|
||||
'values' => empty($values) ? $category : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
@ -123,13 +114,7 @@ class Category extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('category_edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,10 +127,9 @@ class Category extends Base
|
|||
$project = $this->getProjectManagement();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->projectLayout('category_remove', array(
|
||||
$this->response->html($this->projectLayout('category/remove', array(
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove a category')
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -25,8 +25,7 @@ class Comment extends Base
|
|||
}
|
||||
|
||||
if (! $this->acl->isAdminUser() && $comment['user_id'] != $this->acl->getUserId()) {
|
||||
$this->response->html($this->template->layout('comment_forbidden', array(
|
||||
'menu' => 'tasks',
|
||||
$this->response->html($this->template->layout('comment/forbidden', array(
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
@ -39,18 +38,21 @@ class Comment extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('comment_create', array(
|
||||
'values' => array(
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
'user_id' => $this->acl->getUserId(),
|
||||
'task_id' => $task['id'],
|
||||
),
|
||||
'errors' => array(),
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('comment/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Add a comment')
|
||||
)));
|
||||
}
|
||||
|
@ -79,13 +81,7 @@ class Comment extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#comments');
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('comment_create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Add a comment')
|
||||
)));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -93,17 +89,16 @@ class Comment extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$comment = $this->getComment();
|
||||
|
||||
$this->response->html($this->taskLayout('comment_edit', array(
|
||||
'values' => $comment,
|
||||
'errors' => array(),
|
||||
$this->response->html($this->taskLayout('comment/edit', array(
|
||||
'values' => empty($values) ? $comment : $values,
|
||||
'errors' => $errors,
|
||||
'comment' => $comment,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a comment')
|
||||
)));
|
||||
}
|
||||
|
@ -133,14 +128,7 @@ class Comment extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#comment-'.$comment['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('comment_edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'comment' => $comment,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a comment')
|
||||
)));
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -153,10 +141,9 @@ class Comment extends Base
|
|||
$task = $this->getTask();
|
||||
$comment = $this->getComment();
|
||||
|
||||
$this->response->html($this->taskLayout('comment_remove', array(
|
||||
$this->response->html($this->taskLayout('comment/remove', array(
|
||||
'comment' => $comment,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a comment')
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -20,12 +20,12 @@ class Config extends Base
|
|||
*/
|
||||
private function layout($template, array $params)
|
||||
{
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
$params['values'] = $this->config->getAll();
|
||||
$params['errors'] = array();
|
||||
$params['menu'] = 'config';
|
||||
$params['config_content_for_layout'] = $this->template->load($template, $params);
|
||||
|
||||
return $this->template->layout('config_layout', $params);
|
||||
return $this->template->layout('config/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,9 +59,9 @@ class Config extends Base
|
|||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->response->html($this->layout('config_about', array(
|
||||
$this->response->html($this->layout('config/about', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'title' => t('About'),
|
||||
'title' => t('Settings').' > '.t('About'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -74,11 +74,11 @@ class Config extends Base
|
|||
{
|
||||
$this->common('application');
|
||||
|
||||
$this->response->html($this->layout('config_application', array(
|
||||
'title' => t('Application settings'),
|
||||
$this->response->html($this->layout('config/application', array(
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'title' => t('Settings').' > '.t('Application settings'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -91,9 +91,9 @@ class Config extends Base
|
|||
{
|
||||
$this->common('board');
|
||||
|
||||
$this->response->html($this->layout('config_board', array(
|
||||
'title' => t('Board settings'),
|
||||
$this->response->html($this->layout('config/board', array(
|
||||
'default_columns' => implode(', ', $this->board->getDefaultColumns()),
|
||||
'title' => t('Settings').' > '.t('Board settings'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -106,8 +106,8 @@ class Config extends Base
|
|||
{
|
||||
$this->common('webhook');
|
||||
|
||||
$this->response->html($this->layout('config_webhook', array(
|
||||
'title' => t('Webhook settings'),
|
||||
$this->response->html($this->layout('config/webhook', array(
|
||||
'title' => t('Settings').' > '.t('Webhook settings'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -118,8 +118,8 @@ class Config extends Base
|
|||
*/
|
||||
public function api()
|
||||
{
|
||||
$this->response->html($this->layout('config_api', array(
|
||||
'title' => t('API'),
|
||||
$this->response->html($this->layout('config/api', array(
|
||||
'title' => t('Settings').' > '.t('API'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
|
|
@ -21,11 +21,9 @@ class File extends Base
|
|||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('file_new', array(
|
||||
$this->response->html($this->taskLayout('file/new', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'max_size' => ini_get('upload_max_filesize'),
|
||||
'title' => t('Attach a document')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -77,7 +75,7 @@ class File extends Base
|
|||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
if ($file['task_id'] == $task['id']) {
|
||||
$this->response->html($this->template->load('file_open', array(
|
||||
$this->response->html($this->template->load('file/open', array(
|
||||
'file' => $file
|
||||
)));
|
||||
}
|
||||
|
@ -134,11 +132,9 @@ class File extends Base
|
|||
$task = $this->getTask();
|
||||
$file = $this->file->getById($this->request->getIntegerParam('file_id'));
|
||||
|
||||
$this->response->html($this->taskLayout('file_remove', array(
|
||||
$this->response->html($this->taskLayout('file/remove', array(
|
||||
'task' => $task,
|
||||
'file' => $file,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a file')
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,11 +33,11 @@ class Project extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_index', array(
|
||||
$this->response->html($this->template->layout('project/index', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'active_projects' => $active_projects,
|
||||
'inactive_projects' => $inactive_projects,
|
||||
'nb_projects' => $nb_projects,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Projects').' ('.$nb_projects.')'
|
||||
)));
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ class Project extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->projectLayout('project_show', array(
|
||||
$this->response->html($this->projectLayout('project/show', array(
|
||||
'project' => $project,
|
||||
'stats' => $this->project->getStats($project['id']),
|
||||
'webhook_token' => $this->config->get('webhook_token'),
|
||||
|
@ -64,7 +64,7 @@ class Project extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function export()
|
||||
public function exportTasks()
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
$from = $this->request->getStringParam('from');
|
||||
|
@ -72,14 +72,14 @@ class Project extends Base
|
|||
|
||||
if ($from && $to) {
|
||||
$data = $this->taskExport->export($project['id'], $from, $to);
|
||||
$this->response->forceDownload('Export_'.date('Y_m_d_H_i_S').'.csv');
|
||||
$this->response->forceDownload('Tasks_'.date('Y_m_d_H_i').'.csv');
|
||||
$this->response->csv($data);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_export', array(
|
||||
$this->response->html($this->projectLayout('project/export_tasks', array(
|
||||
'values' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'export',
|
||||
'action' => 'exportTasks',
|
||||
'project_id' => $project['id'],
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
|
@ -92,6 +92,39 @@ class Project extends Base
|
|||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Daily project summary export
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function exportDailyProjectSummary()
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
$from = $this->request->getStringParam('from');
|
||||
$to = $this->request->getStringParam('to');
|
||||
|
||||
if ($from && $to) {
|
||||
$data = $this->projectDailySummary->getAggregatedMetrics($project['id'], $from, $to);
|
||||
$this->response->forceDownload('Daily_Summary_'.date('Y_m_d_H_i').'.csv');
|
||||
$this->response->csv($data);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project/export_daily_summary', array(
|
||||
'values' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'exportDailyProjectSummary',
|
||||
'project_id' => $project['id'],
|
||||
'from' => $from,
|
||||
'to' => $to,
|
||||
),
|
||||
'errors' => array(),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'project' => $project,
|
||||
'title' => t('Daily project summary export')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Public access management
|
||||
*
|
||||
|
@ -115,7 +148,7 @@ class Project extends Base
|
|||
$this->response->redirect('?controller=project&action=share&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_share', array(
|
||||
$this->response->html($this->projectLayout('project/share', array(
|
||||
'project' => $project,
|
||||
'title' => t('Public access'),
|
||||
)));
|
||||
|
@ -126,13 +159,13 @@ class Project extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
|
||||
$this->response->html($this->projectLayout('project_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $project,
|
||||
$this->response->html($this->projectLayout('project/edit', array(
|
||||
'values' => empty($values) ? $project : $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'title' => t('Edit project')
|
||||
)));
|
||||
|
@ -146,7 +179,7 @@ class Project extends Base
|
|||
public function update()
|
||||
{
|
||||
$project = $this->getProjectManagement();
|
||||
$values = $this->request->getValues() + array('is_active' => 0);
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->project->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
@ -160,12 +193,7 @@ class Project extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'project' => $project,
|
||||
'title' => t('Edit Project')
|
||||
)));
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,7 +205,7 @@ class Project extends Base
|
|||
{
|
||||
$project = $this->getProjectManagement();
|
||||
|
||||
$this->response->html($this->projectLayout('project_users', array(
|
||||
$this->response->html($this->projectLayout('project/users', array(
|
||||
'project' => $project,
|
||||
'users' => $this->projectPermission->getAllUsers($project['id']),
|
||||
'title' => t('Edit project access list')
|
||||
|
@ -282,7 +310,7 @@ class Project extends Base
|
|||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_remove', array(
|
||||
$this->response->html($this->projectLayout('project/remove', array(
|
||||
'project' => $project,
|
||||
'title' => t('Remove project')
|
||||
)));
|
||||
|
@ -311,7 +339,7 @@ class Project extends Base
|
|||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_duplicate', array(
|
||||
$this->response->html($this->projectLayout('project/duplicate', array(
|
||||
'project' => $project,
|
||||
'title' => t('Clone this project')
|
||||
)));
|
||||
|
@ -339,7 +367,7 @@ class Project extends Base
|
|||
$this->response->redirect('?controller=project&action=show&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_disable', array(
|
||||
$this->response->html($this->projectLayout('project/disable', array(
|
||||
'project' => $project,
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
|
@ -367,7 +395,7 @@ class Project extends Base
|
|||
$this->response->redirect('?controller=project&action=show&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->projectLayout('project_enable', array(
|
||||
$this->response->html($this->projectLayout('project/enable', array(
|
||||
'project' => $project,
|
||||
'title' => t('Project activation')
|
||||
)));
|
||||
|
@ -388,7 +416,7 @@ class Project extends Base
|
|||
$this->forbidden(true);
|
||||
}
|
||||
|
||||
$this->response->xml($this->template->load('project_feed', array(
|
||||
$this->response->xml($this->template->load('project/feed', array(
|
||||
'events' => $this->projectActivity->getProject($project['id']),
|
||||
'project' => $project,
|
||||
)));
|
||||
|
@ -403,9 +431,9 @@ class Project extends Base
|
|||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->layout('project_activity', array(
|
||||
$this->response->html($this->template->layout('project/activity', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'events' => $this->projectActivity->getProject($project['id']),
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'title' => t('%s\'s activity', $project['name'])
|
||||
)));
|
||||
|
@ -428,11 +456,12 @@ class Project extends Base
|
|||
$limit = 25;
|
||||
|
||||
if ($search !== '') {
|
||||
$tasks = $this->taskFinder->search($project['id'], $search, $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskFinder->countSearch($project['id'], $search);
|
||||
$tasks = $this->taskPaginator->searchTasks($project['id'], $search, $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskPaginator->countSearchTasks($project['id'], $search);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_search', array(
|
||||
$this->response->html($this->template->layout('project/search', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'pagination' => array(
|
||||
|
@ -452,10 +481,9 @@ class Project extends Base
|
|||
'project_id' => $project['id'],
|
||||
),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
|
||||
'title' => t('Search in the project "%s"', $project['name']).($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -472,10 +500,11 @@ class Project extends Base
|
|||
$offset = $this->request->getIntegerParam('offset', 0);
|
||||
$limit = 25;
|
||||
|
||||
$tasks = $this->taskFinder->getClosedTasks($project['id'], $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskFinder->countByProjectId($project['id'], array(TaskModel::STATUS_CLOSED));
|
||||
$tasks = $this->taskPaginator->closedTasks($project['id'], $offset, $limit, $order, $direction);
|
||||
$nb_tasks = $this->taskPaginator->countClosedTasks($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('project_tasks', array(
|
||||
$this->response->html($this->template->layout('project/tasks', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'pagination' => array(
|
||||
'controller' => 'project',
|
||||
'action' => 'tasks',
|
||||
|
@ -487,12 +516,11 @@ class Project extends Base
|
|||
'limit' => $limit,
|
||||
),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'columns' => $this->board->getColumnsList($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'title' => $project['name'].' ('.$nb_tasks.')'
|
||||
'title' => t('Completed tasks for "%s"', $project['name']).' ('.$nb_tasks.')'
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -501,14 +529,15 @@ class Project extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(
|
||||
'is_private' => $this->request->getIntegerParam('private', $this->acl->isRegularUser()),
|
||||
),
|
||||
'title' => t('New project')
|
||||
$is_private = $this->request->getIntegerParam('private', $this->acl->isRegularUser());
|
||||
|
||||
$this->response->html($this->template->layout('project/new', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'values' => empty($values) ? array('is_private' => $is_private) : $values,
|
||||
'errors' => $errors,
|
||||
'title' => $is_private ? t('New private project') : t('New project'),
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -524,19 +553,17 @@ class Project extends Base
|
|||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->create($values, $this->acl->getUserId())) {
|
||||
$project_id = $this->project->create($values, $this->acl->getUserId(), true);
|
||||
|
||||
if ($project_id) {
|
||||
$this->session->flash(t('Your project have been created successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
$this->response->redirect('?controller=project&action=show&project_id='.$project_id);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'title' => t('New Project')
|
||||
)));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,20 +32,22 @@ class Subtask extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
|
||||
$this->response->html($this->taskLayout('subtask_create', array(
|
||||
'values' => array(
|
||||
if (empty($values)) {
|
||||
$values = array(
|
||||
'task_id' => $task['id'],
|
||||
'another_subtask' => $this->request->getIntegerParam('another_subtask', 0)
|
||||
),
|
||||
'errors' => array(),
|
||||
'users_list' => $this->projectPermission->getUsersList($task['project_id']),
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('subtask/create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectPermission->getMemberList($task['project_id']),
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Add a sub-task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -77,14 +79,7 @@ class Subtask extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#subtasks');
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('subtask_create', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectPermission->getUsersList($task['project_id']),
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Add a sub-task')
|
||||
)));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -92,20 +87,18 @@ class Subtask extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
public function edit(array $values = array(), array $errors = array())
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubTask();
|
||||
|
||||
$this->response->html($this->taskLayout('subtask_edit', array(
|
||||
'values' => $subtask,
|
||||
'errors' => array(),
|
||||
'users_list' => $this->projectPermission->getUsersList($task['project_id']),
|
||||
$this->response->html($this->taskLayout('subtask/edit', array(
|
||||
'values' => empty($values) ? $subtask : $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectPermission->getMemberList($task['project_id']),
|
||||
'status_list' => $this->subTask->getStatusList(),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a sub-task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -134,16 +127,7 @@ class Subtask extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#subtasks');
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('subtask_edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'users_list' => $this->projectPermission->getUsersList($task['project_id']),
|
||||
'status_list' => $this->subTask->getStatusList(),
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a sub-task')
|
||||
)));
|
||||
$this->edit($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,11 +140,9 @@ class Subtask extends Base
|
|||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
|
||||
$this->response->html($this->taskLayout('subtask_remove', array(
|
||||
$this->response->html($this->taskLayout('subtask/remove', array(
|
||||
'subtask' => $subtask,
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a sub-task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -193,15 +175,9 @@ class Subtask extends Base
|
|||
public function toggleStatus()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$subtask = $this->getSubtask();
|
||||
$subtask_id = $this->request->getIntegerParam('subtask_id');
|
||||
|
||||
$value = array(
|
||||
'id' => $subtask['id'],
|
||||
'status' => ($subtask['status'] + 1) % 3,
|
||||
'task_id' => $task['id'],
|
||||
);
|
||||
|
||||
if (! $this->subTask->update($value)) {
|
||||
if (! $this->subTask->toggleStatus($subtask_id)) {
|
||||
$this->session->flashError(t('Unable to update your sub-task.'));
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ class Task extends Base
|
|||
$this->notfound(true);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task_public', array(
|
||||
$this->response->html($this->template->layout('task/public', array(
|
||||
'project' => $project,
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'subtasks' => $this->subTask->getAll($task['id']),
|
||||
|
@ -65,7 +65,7 @@ class Task extends Base
|
|||
|
||||
$this->dateParser->format($values, array('date_started'));
|
||||
|
||||
$this->response->html($this->taskLayout('task_show', array(
|
||||
$this->response->html($this->taskLayout('task/show', array(
|
||||
'project' => $this->project->getById($task['project_id']),
|
||||
'files' => $this->file->getAll($task['id']),
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
|
@ -77,8 +77,7 @@ class Task extends Base
|
|||
'colors_list' => $this->color->getList(),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'menu' => 'tasks',
|
||||
'title' => $task['title'],
|
||||
'title' => $task['project_name'].' > '.$task['title'],
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -87,29 +86,33 @@ class Task extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$this->checkProjectPermissions($project_id);
|
||||
$project = $this->getProject();
|
||||
$method = $this->request->isAjax() ? 'load' : 'layout';
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(
|
||||
'project_id' => $project_id,
|
||||
if (empty($values)) {
|
||||
|
||||
$values = array(
|
||||
'column_id' => $this->request->getIntegerParam('column_id'),
|
||||
'color_id' => $this->request->getStringParam('color_id'),
|
||||
'owner_id' => $this->request->getIntegerParam('owner_id'),
|
||||
'another_task' => $this->request->getIntegerParam('another_task'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->$method('task/new', array(
|
||||
'ajax' => $this->request->isAjax(),
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project['id']),
|
||||
'projects_list' => $this->project->getListByStatus(ProjectModel::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($project_id),
|
||||
'users_list' => $this->projectPermission->getUsersList($project_id),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'users_list' => $this->projectPermission->getMemberList($project['id'], true, false, true),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($project_id),
|
||||
'categories_list' => $this->category->getList($project['id']),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
'title' => $project['name'].' > '.t('New task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -120,16 +123,17 @@ class Task extends Base
|
|||
*/
|
||||
public function save()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$values = $this->request->getValues();
|
||||
$values['creator_id'] = $this->acl->getUserId();
|
||||
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->create($values)) {
|
||||
if ($this->taskCreation->create($values)) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
|
||||
if (isset($values['another_task']) && $values['another_task'] == 1) {
|
||||
|
@ -146,19 +150,7 @@ class Task extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'projects_list' => $this->project->getListByStatus(ProjectModel::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($values['project_id']),
|
||||
'users_list' => $this->projectPermission->getUsersList($values['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($values['project_id']),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
)));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -177,21 +169,19 @@ class Task extends Base
|
|||
'values' => $task,
|
||||
'errors' => array(),
|
||||
'task' => $task,
|
||||
'users_list' => $this->projectPermission->getUsersList($task['project_id']),
|
||||
'users_list' => $this->projectPermission->getMemberList($task['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'ajax' => $ajax,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a task')
|
||||
);
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->load('task_edit', $params));
|
||||
$this->response->html($this->template->load('task/edit', $params));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->taskLayout('task_edit', $params));
|
||||
$this->response->html($this->taskLayout('task/edit', $params));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,7 +199,7 @@ class Task extends Base
|
|||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
|
||||
if ($this->request->getIntegerParam('ajax')) {
|
||||
|
@ -224,18 +214,16 @@ class Task extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_edit', array(
|
||||
$this->response->html($this->taskLayout('task/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($values['project_id']),
|
||||
'users_list' => $this->projectPermission->getUsersList($values['project_id']),
|
||||
'users_list' => $this->projectPermission->getMemberList($values['project_id']),
|
||||
'colors_list' => $this->color->getList(),
|
||||
'categories_list' => $this->category->getList($values['project_id']),
|
||||
'date_format' => $this->config->get('application_date_format'),
|
||||
'date_formats' => $this->dateParser->getAvailableFormats(),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a task'),
|
||||
'ajax' => $this->request->isAjax(),
|
||||
)));
|
||||
}
|
||||
|
@ -250,9 +238,9 @@ class Task extends Base
|
|||
$task = $this->getTask();
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->taskValidator->validateTimeModification($values);
|
||||
list($valid,) = $this->taskValidator->validateTimeModification($values);
|
||||
|
||||
if ($valid && $this->task->update($values)) {
|
||||
if ($valid && $this->taskModification->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -275,7 +263,7 @@ class Task extends Base
|
|||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->task->close($task['id'])) {
|
||||
if ($this->taskStatus->close($task['id'])) {
|
||||
$this->session->flash(t('Task closed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to close this task.'));
|
||||
|
@ -284,10 +272,8 @@ class Task extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_close', array(
|
||||
$this->response->html($this->taskLayout('task/close', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Close a task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -304,7 +290,7 @@ class Task extends Base
|
|||
|
||||
$this->checkCSRFParam();
|
||||
|
||||
if ($this->task->open($task['id'])) {
|
||||
if ($this->taskStatus->open($task['id'])) {
|
||||
$this->session->flash(t('Task opened successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to open this task.'));
|
||||
|
@ -313,10 +299,8 @@ class Task extends Base
|
|||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_open', array(
|
||||
$this->response->html($this->taskLayout('task/open', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Open a task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -346,10 +330,8 @@ class Task extends Base
|
|||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_remove', array(
|
||||
$this->response->html($this->taskLayout('task/remove', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -365,7 +347,7 @@ class Task extends Base
|
|||
if ($this->request->getStringParam('confirmation') === 'yes') {
|
||||
|
||||
$this->checkCSRFParam();
|
||||
$task_id = $this->task->duplicateToSameProject($task);
|
||||
$task_id = $this->taskDuplication->duplicate($task['id']);
|
||||
|
||||
if ($task_id) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
|
@ -376,10 +358,8 @@ class Task extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_duplicate', array(
|
||||
$this->response->html($this->taskLayout('task/duplicate', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Duplicate a task')
|
||||
)));
|
||||
}
|
||||
|
||||
|
@ -401,7 +381,7 @@ class Task extends Base
|
|||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
if ($this->taskModification->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
|
@ -426,15 +406,13 @@ class Task extends Base
|
|||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'ajax' => $ajax,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit the description'),
|
||||
);
|
||||
|
||||
if ($ajax) {
|
||||
$this->response->html($this->template->load('task_edit_description', $params));
|
||||
$this->response->html($this->template->load('task/edit_description', $params));
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->taskLayout('task_edit_description', $params));
|
||||
$this->response->html($this->taskLayout('task/edit_description', $params));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,31 +422,11 @@ class Task extends Base
|
|||
* @access public
|
||||
*/
|
||||
public function move()
|
||||
{
|
||||
$this->toAnotherProject('move');
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$this->toAnotherProject('duplicate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Common methods between the actions "move" and "copy"
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
private function toAnotherProject($action)
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
$projects_list = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
$projects_list = $this->projectPermission->getMemberProjects($this->acl->getUserId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
|
@ -478,7 +436,46 @@ class Task extends Base
|
|||
list($valid, $errors) = $this->taskValidator->validateProjectModification($values);
|
||||
|
||||
if ($valid) {
|
||||
$task_id = $this->task->{$action.'ToAnotherProject'}($values['project_id'], $task);
|
||||
|
||||
if ($this->taskDuplication->moveToProject($task['id'], $values['project_id'])) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task/move_project', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'projects_list' => $projects_list,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function copy()
|
||||
{
|
||||
$task = $this->getTask();
|
||||
$values = $task;
|
||||
$errors = array();
|
||||
$projects_list = $this->projectPermission->getMemberProjects($this->acl->getUserId());
|
||||
|
||||
unset($projects_list[$task['project_id']]);
|
||||
|
||||
if ($this->request->isPost()) {
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->taskValidator->validateProjectModification($values);
|
||||
|
||||
if ($valid) {
|
||||
$task_id = $this->taskDuplication->duplicateToProject($task['id'], $values['project_id']);
|
||||
if ($task_id) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task_id);
|
||||
|
@ -489,13 +486,11 @@ class Task extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_'.$action.'_project', array(
|
||||
$this->response->html($this->taskLayout('task/duplicate_project', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'task' => $task,
|
||||
'projects_list' => $projects_list,
|
||||
'menu' => 'tasks',
|
||||
'title' => t(ucfirst($action).' the task to another project')
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,15 @@ class User extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function login()
|
||||
public function login(array $values = array(), array $errors = array())
|
||||
{
|
||||
if ($this->acl->isLogged()) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
$this->response->html($this->template->layout('user/login', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'no_layout' => true,
|
||||
'redirect_query' => $this->request->getStringParam('redirect_query'),
|
||||
'title' => t('Login')
|
||||
|
@ -63,13 +63,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'no_layout' => true,
|
||||
'redirect_query' => $redirect_query,
|
||||
'title' => t('Login')
|
||||
)));
|
||||
$this->login($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -84,13 +78,13 @@ class User extends Base
|
|||
{
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['user_content_for_layout'] = $content;
|
||||
$params['menu'] = 'users';
|
||||
$params['board_selector'] = $this->projectPermission->getAllowedProjects($this->acl->getUserId());
|
||||
|
||||
if (isset($params['user'])) {
|
||||
$params['title'] = $params['user']['name'] ?: $params['user']['username'];
|
||||
$params['title'] = ($params['user']['name'] ?: $params['user']['username']).' (#'.$params['user']['id'].')';
|
||||
}
|
||||
|
||||
return $this->template->layout('user_layout', $params);
|
||||
return $this->template->layout('user/layout', $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,11 +124,11 @@ class User extends Base
|
|||
$nb_users = $this->user->count();
|
||||
|
||||
$this->response->html(
|
||||
$this->template->layout('user_index', array(
|
||||
$this->template->layout('user/index', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'projects' => $this->project->getList(),
|
||||
'nb_users' => $nb_users,
|
||||
'users' => $users,
|
||||
'menu' => 'users',
|
||||
'title' => t('Users').' ('.$nb_users.')',
|
||||
'pagination' => array(
|
||||
'controller' => 'user',
|
||||
|
@ -154,13 +148,13 @@ class User extends Base
|
|||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
public function create(array $values = array(), array $errors = array())
|
||||
{
|
||||
$this->response->html($this->template->layout('user_new', array(
|
||||
$this->response->html($this->template->layout('user/new', array(
|
||||
'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()),
|
||||
'projects' => $this->project->getList(),
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
'menu' => 'users',
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'title' => t('New user')
|
||||
)));
|
||||
}
|
||||
|
@ -186,13 +180,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_new', array(
|
||||
'projects' => $this->project->getList(),
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'users',
|
||||
'title' => t('New user')
|
||||
)));
|
||||
$this->create($values, $errors);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -203,7 +191,7 @@ class User extends Base
|
|||
public function show()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_show', array(
|
||||
$this->response->html($this->layout('user/show', array(
|
||||
'projects' => $this->projectPermission->getAllowedProjects($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -217,7 +205,7 @@ class User extends Base
|
|||
public function last()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_last', array(
|
||||
$this->response->html($this->layout('user/last', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -231,7 +219,7 @@ class User extends Base
|
|||
public function sessions()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_sessions', array(
|
||||
$this->response->html($this->layout('user/sessions', array(
|
||||
'sessions' => $this->authentication->backend('rememberMe')->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -266,7 +254,7 @@ class User extends Base
|
|||
$this->response->redirect('?controller=user&action=notifications&user_id='.$user['id']);
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_notifications', array(
|
||||
$this->response->html($this->layout('user/notifications', array(
|
||||
'projects' => $this->projectPermission->getAllowedProjects($user['id']),
|
||||
'notifications' => $this->notification->readSettings($user['id']),
|
||||
'user' => $user,
|
||||
|
@ -281,7 +269,7 @@ class User extends Base
|
|||
public function external()
|
||||
{
|
||||
$user = $this->getUser();
|
||||
$this->response->html($this->layout('user_external', array(
|
||||
$this->response->html($this->layout('user/external', array(
|
||||
'last_logins' => $this->lastLogin->getAll($user['id']),
|
||||
'user' => $user,
|
||||
)));
|
||||
|
@ -316,7 +304,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_password', array(
|
||||
$this->response->html($this->layout('user/password', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'user' => $user,
|
||||
|
@ -365,7 +353,7 @@ class User extends Base
|
|||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_edit', array(
|
||||
$this->response->html($this->layout('user/edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'projects' => $this->projectPermission->filterProjects($this->project->getList(), $user['id']),
|
||||
|
@ -395,7 +383,7 @@ class User extends Base
|
|||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
|
||||
$this->response->html($this->layout('user_remove', array(
|
||||
$this->response->html($this->layout('user/remove', array(
|
||||
'user' => $user,
|
||||
)));
|
||||
}
|
||||
|
@ -431,7 +419,7 @@ class User extends Base
|
|||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
$this->response->html($this->template->layout('user/login', array(
|
||||
'errors' => array('login' => t('Google authentication failed')),
|
||||
'values' => array(),
|
||||
'no_layout' => true,
|
||||
|
@ -493,7 +481,7 @@ class User extends Base
|
|||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
$this->response->html($this->template->layout('user/login', array(
|
||||
'errors' => array('login' => t('GitHub authentication failed')),
|
||||
'values' => array(),
|
||||
'no_layout' => true,
|
||||
|
|
|
@ -35,7 +35,7 @@ class Webhook extends Base
|
|||
|
||||
list($valid,) = $this->taskValidator->validateCreation($values);
|
||||
|
||||
if ($valid && $this->task->create($values)) {
|
||||
if ($valid && $this->taskCreation->create($values)) {
|
||||
$this->response->text('OK');
|
||||
}
|
||||
|
||||
|
@ -57,7 +57,7 @@ class Webhook extends Base
|
|||
|
||||
$result = $this->githubWebhook->parsePayload(
|
||||
$this->request->getHeader('X-Github-Event'),
|
||||
$this->request->getBody()
|
||||
$this->request->getJson()
|
||||
);
|
||||
|
||||
echo $result ? 'PARSED' : 'IGNORED';
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use Closure;
|
||||
|
||||
/**
|
||||
* CLI class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Cli
|
||||
{
|
||||
/**
|
||||
* Default command name
|
||||
*
|
||||
* @access public
|
||||
* @var string
|
||||
*/
|
||||
public $default_command = 'help';
|
||||
|
||||
/**
|
||||
* List of registered commands
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $commands = array();
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @access public
|
||||
* @param string $command Command name
|
||||
* @param Closure $callback Command callback
|
||||
*/
|
||||
public function register($command, Closure $callback)
|
||||
{
|
||||
$this->commands[$command] = $callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command
|
||||
*
|
||||
* @access public
|
||||
* @param string $command Command name
|
||||
*/
|
||||
public function call($command)
|
||||
{
|
||||
if (isset($this->commands[$command])) {
|
||||
$this->commands[$command]();
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine which command to execute
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
if (php_sapi_name() !== 'cli') {
|
||||
die('This script work only from the command line.');
|
||||
}
|
||||
|
||||
if ($GLOBALS['argc'] === 1) {
|
||||
$this->call($this->default_command);
|
||||
}
|
||||
|
||||
$this->call($GLOBALS['argv'][1]);
|
||||
$this->call($this->default_command);
|
||||
}
|
||||
}
|
|
@ -69,7 +69,7 @@ class Event
|
|||
{
|
||||
if (! $this->isEventTriggered($eventName)) {
|
||||
|
||||
$this->events[] = $eventName;
|
||||
$this->events[$eventName] = $data;
|
||||
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
|
||||
|
@ -118,6 +118,17 @@ class Event
|
|||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of triggered events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getEventData($eventName)
|
||||
{
|
||||
return isset($this->events[$eventName]) ? $this->events[$eventName] : array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an event have been triggered
|
||||
*
|
||||
|
@ -127,7 +138,7 @@ class Event
|
|||
*/
|
||||
public function isEventTriggered($eventName)
|
||||
{
|
||||
return in_array($eventName, $this->events);
|
||||
return isset($this->events[$eventName]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Loader class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* List of paths
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $paths = array();
|
||||
|
||||
/**
|
||||
* Load the missing class
|
||||
*
|
||||
* @access public
|
||||
* @param string $class Class name with namespace
|
||||
*/
|
||||
public function load($class)
|
||||
{
|
||||
foreach ($this->paths as $path) {
|
||||
|
||||
$filename = $path.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require $filename;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the autoloader
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
spl_autoload_register(array($this, 'load'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a new path
|
||||
*
|
||||
* @access public
|
||||
* @param string $path Path
|
||||
* @return Core\Loader
|
||||
*/
|
||||
public function setPath($path)
|
||||
{
|
||||
$this->paths[] = $path;
|
||||
return $this;
|
||||
}
|
||||
}
|
|
@ -1,83 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* The registry class is a dependency injection container
|
||||
*
|
||||
* @property mixed db
|
||||
* @property mixed event
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Registry
|
||||
{
|
||||
/**
|
||||
* Contains all dependencies
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $container = array();
|
||||
|
||||
/**
|
||||
* Contains all instances
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $instances = array();
|
||||
|
||||
/**
|
||||
* Set a dependency
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Unique identifier for the service/parameter
|
||||
* @param mixed $value The value of the parameter or a closure to define an object
|
||||
*/
|
||||
public function __set($name, $value)
|
||||
{
|
||||
$this->container[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a dependency
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Unique identifier for the service/parameter
|
||||
* @return mixed The value of the parameter or an object
|
||||
* @throws RuntimeException If the identifier is not found
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
if (isset($this->container[$name])) {
|
||||
|
||||
if (is_callable($this->container[$name])) {
|
||||
return $this->container[$name]();
|
||||
}
|
||||
else {
|
||||
return $this->container[$name];
|
||||
}
|
||||
}
|
||||
|
||||
throw new \RuntimeException('Identifier not found in the registry: '.$name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a shared instance of a dependency
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Unique identifier for the service/parameter
|
||||
* @return mixed Same object instance of the dependency
|
||||
*/
|
||||
public function shared($name)
|
||||
{
|
||||
if (! isset($this->instances[$name])) {
|
||||
$this->instances[$name] = $this->$name;
|
||||
}
|
||||
|
||||
return $this->instances[$name];
|
||||
}
|
||||
}
|
|
@ -75,6 +75,17 @@ class Request
|
|||
return file_get_contents('php://input');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Json request body
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getJson()
|
||||
{
|
||||
return json_decode($this->getBody(), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content of an uploaded file
|
||||
*
|
||||
|
@ -113,6 +124,20 @@ class Request
|
|||
return $this->getHeader('X-Requested-With') === 'XMLHttpRequest';
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page is requested through HTTPS
|
||||
*
|
||||
* Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isHTTPS()
|
||||
{
|
||||
return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off';
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a HTTP header value
|
||||
*
|
||||
|
|
|
@ -246,7 +246,7 @@ class Response
|
|||
*/
|
||||
public function hsts()
|
||||
{
|
||||
if (Tool::isHTTPS()) {
|
||||
if (Request::isHTTPS()) {
|
||||
header('Strict-Transport-Security: max-age=31536000');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Core;
|
||||
|
||||
use Pimple\Container;
|
||||
|
||||
/**
|
||||
* Router class
|
||||
*
|
||||
|
@ -27,24 +29,24 @@ class Router
|
|||
private $action = '';
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access private
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
private $registry;
|
||||
private $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Registry $registry Registry instance
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @param \Pimple\Container $container Container instance
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
*/
|
||||
public function __construct(Registry $registry, $controller = '', $action = '')
|
||||
public function __construct(Container $container, $controller = '', $action = '')
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->container = $container;
|
||||
$this->controller = empty($_GET['controller']) ? $controller : $_GET['controller'];
|
||||
$this->action = empty($_GET['action']) ? $action : $_GET['action'];
|
||||
}
|
||||
|
@ -81,11 +83,7 @@ class Router
|
|||
return false;
|
||||
}
|
||||
|
||||
$instance = new $class($this->registry);
|
||||
$instance->request = new Request;
|
||||
$instance->response = new Response;
|
||||
$instance->session = new Session;
|
||||
$instance->template = new Template;
|
||||
$instance = new $class($this->container);
|
||||
$instance->beforeAction($this->controller, $this->action);
|
||||
$instance->$method();
|
||||
|
||||
|
|
|
@ -36,32 +36,30 @@ class Session
|
|||
*
|
||||
* @access public
|
||||
* @param string $base_path Cookie path
|
||||
* @param string $save_path Custom session save path
|
||||
*/
|
||||
public function open($base_path = '/', $save_path = '')
|
||||
public function open($base_path = '/')
|
||||
{
|
||||
if ($save_path !== '') {
|
||||
session_save_path($save_path);
|
||||
}
|
||||
|
||||
// HttpOnly and secure flags for session cookie
|
||||
session_set_cookie_params(
|
||||
self::SESSION_LIFETIME,
|
||||
$base_path ?: '/',
|
||||
null,
|
||||
Tool::isHTTPS(),
|
||||
Request::isHTTPS(),
|
||||
true
|
||||
);
|
||||
|
||||
// Avoid session id in the URL
|
||||
ini_set('session.use_only_cookies', '1');
|
||||
|
||||
// Enable strict mode
|
||||
ini_set('session.use_strict_mode', '1');
|
||||
|
||||
// Ensure session ID integrity
|
||||
ini_set('session.entropy_file', '/dev/urandom');
|
||||
ini_set('session.entropy_length', '32');
|
||||
ini_set('session.hash_bits_per_character', 6);
|
||||
|
||||
// If session was autostarted with session.auto_start = 1 in php.ini destroy it, otherwise we cannot login
|
||||
// If session was autostarted with session.auto_start = 1 in php.ini destroy it
|
||||
if (isset($_SESSION)) {
|
||||
session_destroy();
|
||||
}
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Core;
|
||||
|
||||
use Pimple\Container;
|
||||
|
||||
/**
|
||||
* Tool class
|
||||
*
|
||||
|
@ -37,31 +39,17 @@ class Tool
|
|||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param Core\Registry $registry DPI container
|
||||
* @param string $name Model name
|
||||
* @param Pimple\Container $container Container instance
|
||||
* @param string $name Model name
|
||||
* @return mixed
|
||||
*/
|
||||
public static function loadModel(Registry $registry, $name)
|
||||
public static function loadModel(Container $container, $name)
|
||||
{
|
||||
if (! isset($registry->$name)) {
|
||||
if (! isset($container[$name])) {
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$registry->$name = new $class($registry);
|
||||
$container[$name] = new $class($container);
|
||||
}
|
||||
|
||||
return $registry->shared($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the page is requested through HTTPS
|
||||
*
|
||||
* Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public static function isHTTPS()
|
||||
{
|
||||
return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off';
|
||||
return $container[$name];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
namespace Event;
|
||||
|
||||
use Pimple\Container;
|
||||
use Core\Listener;
|
||||
use Core\Registry;
|
||||
use Core\Tool;
|
||||
|
||||
/**
|
||||
|
@ -22,22 +22,22 @@ use Core\Tool;
|
|||
abstract class Base implements Listener
|
||||
{
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $registry;
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Regsitry instance
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->container = $container;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +60,7 @@ abstract class Base implements Listener
|
|||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +73,7 @@ abstract class Base implements Listener
|
|||
*/
|
||||
public function getEventNamespace()
|
||||
{
|
||||
$event_name = $this->registry->event->getLastTriggeredEvent();
|
||||
$event_name = $this->container['event']->getLastTriggeredEvent();
|
||||
return substr($event_name, 0, strpos($event_name, '.'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ class ProjectActivityListener extends Base
|
|||
$values['task']['project_id'],
|
||||
$values['task']['id'],
|
||||
$this->acl->getUserId(),
|
||||
$this->registry->event->getLastTriggeredEvent(),
|
||||
$this->container['event']->getLastTriggeredEvent(),
|
||||
$values
|
||||
);
|
||||
}
|
||||
|
|
28
sources/app/Event/ProjectDailySummaryListener.php
Normal file
28
sources/app/Event/ProjectDailySummaryListener.php
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
/**
|
||||
* Project daily summary listener
|
||||
*
|
||||
* @package event
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectDailySummaryListener extends Base
|
||||
{
|
||||
/**
|
||||
* 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 execute(array $data)
|
||||
{
|
||||
if (isset($data['project_id'])) {
|
||||
return $this->projectDailySummary->updateTotals($data['project_id'], date('Y-m-d'));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Kompleksitet',
|
||||
'limit' => 'Begrænsning',
|
||||
'Task limit' => 'Opgave begrænsning',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Denne værdi skal være større end %d',
|
||||
'Edit project access list' => 'Rediger adgangstilladelser for projektet',
|
||||
'Edit users access' => 'Rediger brugertilladelser',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -57,7 +57,7 @@ return array(
|
|||
'Column %d' => 'Spalte %d',
|
||||
'Add this column' => 'Diese Spalte hinzufügen',
|
||||
'%d tasks on the board' => '%d Aufgaben auf dieser Pinnwand',
|
||||
'%d tasks in total' => '%d Aufgaben gesamt',
|
||||
'%d tasks in total' => '%d Aufgaben insgesamt',
|
||||
'Unable to update this board.' => 'Ändern dieser Pinnwand nicht möglich.',
|
||||
'Edit board' => 'Pinnwand bearbeiten',
|
||||
'Disable' => 'Deaktivieren',
|
||||
|
@ -94,7 +94,7 @@ return array(
|
|||
'User settings' => 'Benutzereinstellungen',
|
||||
'My default project:' => 'Standardprojekt:',
|
||||
'Close a task' => 'Aufgabe abschließen',
|
||||
'Do you really want to close this task: "%s"?' => 'Soll diese Aufgabe wirklich abgeschlossen werden: "%s"?',
|
||||
'Do you really want to close this task: "%s"?' => 'Soll diese Aufgabe wirklich geschlossen werden: "%s"?',
|
||||
'Edit a task' => 'Aufgabe bearbeiten',
|
||||
'Column' => 'Spalte',
|
||||
'Color' => 'Farbe',
|
||||
|
@ -108,8 +108,8 @@ return array(
|
|||
'There is nobody assigned' => 'Die Aufgabe wurde niemandem zugewiesen',
|
||||
'Column on the board:' => 'Spalte:',
|
||||
'Status is open' => 'Status ist geöffnet',
|
||||
'Status is closed' => 'Status ist abgeschlossen',
|
||||
'Close this task' => 'Aufgabe abschließen',
|
||||
'Status is closed' => 'Status ist geschlossen',
|
||||
'Close this task' => 'Aufgabe schließen',
|
||||
'Open this task' => 'Aufgabe wieder öffnen',
|
||||
'There is no description.' => 'Keine Beschreibung vorhanden.',
|
||||
'Add a new task' => 'Neue Aufgabe hinzufügen',
|
||||
|
@ -147,9 +147,9 @@ return array(
|
|||
'Project disabled successfully.' => 'Projekt erfolgreich deaktiviert.',
|
||||
'Unable to disable this project.' => 'Deaktivieren des Projekts nicht möglich.',
|
||||
'Unable to open this task.' => 'Wiedereröffnung der Aufgabe nicht möglich.',
|
||||
'Task opened successfully.' => 'Aufgabe erfolgreich wieder eröffnet.',
|
||||
'Task opened successfully.' => 'Aufgabe erfolgreich wieder geöffnet.',
|
||||
'Unable to close this task.' => 'Abschließen der Aufgabe nicht möglich.',
|
||||
'Task closed successfully.' => 'Aufgabe erfolgreich abgeschlossen.',
|
||||
'Task closed successfully.' => 'Aufgabe erfolgreich geschlossen.',
|
||||
'Unable to update your task.' => 'Aktualisieren der Aufgabe nicht möglich.',
|
||||
'Task updated successfully.' => 'Aufgabe erfolgreich aktualisiert.',
|
||||
'Unable to create your task.' => 'Erstellen der Aufgabe nicht möglich.',
|
||||
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Komplexität',
|
||||
'limit' => 'Limit',
|
||||
'Task limit' => 'Maximale Anzahl von Aufgaben',
|
||||
'Task count' => 'Aufgabenanzahl',
|
||||
'This value must be greater than %d' => 'Dieser Wert muss größer sein als %d',
|
||||
'Edit project access list' => 'Zugriffsberechtigungen des Projektes bearbeiten',
|
||||
'Edit users access' => 'Benutzerzugriff ändern',
|
||||
|
@ -342,27 +343,27 @@ return array(
|
|||
'Time tracking' => 'Zeiterfassung',
|
||||
'Estimate:' => 'Geschätzt:',
|
||||
'Spent:' => 'Aufgewendet:',
|
||||
'Do you really want to remove this sub-task?' => 'Soll diese Unteraufgabe wirklich gelöscht werden: "%s"?',
|
||||
'Do you really want to remove this sub-task?' => 'Soll diese Teilaufgabe wirklich gelöscht werden: "%s"?',
|
||||
'Remaining:' => 'Verbleibend:',
|
||||
'hours' => 'Stunden',
|
||||
'spent' => 'aufgewendet',
|
||||
'estimated' => 'geschätzt',
|
||||
'Sub-Tasks' => 'Unteraufgaben',
|
||||
'Add a sub-task' => 'Unteraufgabe anlegen',
|
||||
'Sub-Tasks' => 'Teilaufgaben',
|
||||
'Add a sub-task' => 'Teilaufgabe anlegen',
|
||||
'Original estimate' => 'Geschätzter Aufwand',
|
||||
'Create another sub-task' => 'Weitere Unteraufgabe anlegen',
|
||||
'Create another sub-task' => 'Weitere Teilaufgabe anlegen',
|
||||
'Time spent' => 'Aufgewendete Zeit',
|
||||
'Edit a sub-task' => 'Unteraufgabe bearbeiten',
|
||||
'Remove a sub-task' => 'Unteraufgabe löschen',
|
||||
'Edit a sub-task' => 'Teilaufgabe bearbeiten',
|
||||
'Remove a sub-task' => 'Teilaufgabe löschen',
|
||||
'The time must be a numeric value' => 'Zeit nur als nummerische Angabe',
|
||||
'Todo' => 'Nicht gestartet',
|
||||
'In progress' => 'In Bearbeitung',
|
||||
'Sub-task removed successfully.' => 'Unteraufgabe erfolgreich gelöscht.',
|
||||
'Unable to remove this sub-task.' => 'Löschen der Unteraufgabe nicht möglich.',
|
||||
'Sub-task updated successfully.' => 'Unteraufgabe erfolgreich aktualisiert.',
|
||||
'Unable to update your sub-task.' => 'Aktualisieren der Unteraufgabe nicht möglich.',
|
||||
'Unable to create your sub-task.' => 'Erstellen der Unteraufgabe nicht möglich.',
|
||||
'Sub-task added successfully.' => 'Unteraufgabe erfolgreich angelegt.',
|
||||
'Sub-task removed successfully.' => 'Teilaufgabe erfolgreich gelöscht.',
|
||||
'Unable to remove this sub-task.' => 'Löschen der Teilaufgabe nicht möglich.',
|
||||
'Sub-task updated successfully.' => 'Teilaufgabe erfolgreich aktualisiert.',
|
||||
'Unable to update your sub-task.' => 'Aktualisieren der Teilaufgabe nicht möglich.',
|
||||
'Unable to create your sub-task.' => 'Erstellen der Teilaufgabe nicht möglich.',
|
||||
'Sub-task added successfully.' => 'Teilaufgabe erfolgreich angelegt.',
|
||||
'Maximum size: ' => 'Maximalgröße: ',
|
||||
'Unable to upload the file.' => 'Hochladen der Datei nicht möglich.',
|
||||
'Display another project' => 'Zu Projekt wechseln...',
|
||||
|
@ -396,12 +397,12 @@ return array(
|
|||
'Task position:' => 'Position der Aufgabe',
|
||||
'The task #%d have been opened.' => 'Die Aufgabe #%d wurde geöffnet.',
|
||||
'The task #%d have been closed.' => 'Die Aufgabe #%d wurde geschlossen.',
|
||||
'Sub-task updated' => 'Unteraufgabe aktualisiert',
|
||||
'Sub-task updated' => 'Teilaufgabe aktualisiert',
|
||||
'Title:' => 'Titel',
|
||||
'Status:' => 'Status',
|
||||
'Assignee:' => 'Zuständigkeit:',
|
||||
'Time tracking:' => 'Zeittracking',
|
||||
'New sub-task' => 'Neue Unteraufgabe',
|
||||
'New sub-task' => 'Neue Teilaufgabe',
|
||||
'New attachment added "%s"' => 'Neuer Anhang "%s" wurde hinzugefügt.',
|
||||
'Comment updated' => 'Kommentar wurde aktualisiert',
|
||||
'New comment posted by %s' => 'Neuer Kommentar verfasst durch %s',
|
||||
|
@ -409,8 +410,8 @@ return array(
|
|||
'[%s][New attachment] %s (#%d)' => '[%s][Neuer Anhang] %s (#%d)',
|
||||
'[%s][New comment] %s (#%d)' => '[%s][Neuer Kommentar] %s (#%d)',
|
||||
'[%s][Comment updated] %s (#%d)' => '[%s][Kommentar aktualisisiert] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Neue Unteraufgabe] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Unteraufgabe aktualisisert] %s (#%d)',
|
||||
'[%s][New subtask] %s (#%d)' => '[%s][Neue Teilaufgabe] %s (#%d)',
|
||||
'[%s][Subtask updated] %s (#%d)' => '[%s][Teilaufgabe aktualisisert] %s (#%d)',
|
||||
'[%s][New task] %s (#%d)' => '[%s][Neue Aufgabe] %s (#%d)',
|
||||
'[%s][Task updated] %s (#%d)' => '[%s][Aufgabe aktualisiert] %s (#%d)',
|
||||
'[%s][Task closed] %s (#%d)' => '[%s][Aufgabe geschlossen] %s (#%d)',
|
||||
|
@ -473,8 +474,8 @@ return array(
|
|||
'%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '%s hat die Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> in die Spalte "%s" verschoben',
|
||||
'%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat die Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> angelegt',
|
||||
'%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat die Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> geschlossen',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat eine Unteraufgabe für die Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> angelegt',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat eine Unteraufgabe der Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> verändert',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat eine Teilaufgabe für die Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> angelegt',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat eine Teilaufgabe der Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> verändert',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'An %s zugewiesen mit einer Schätzung von %s/%s Stunden',
|
||||
'Not assigned, estimate of %sh' => 'Nicht zugewiesen, Schätzung von %s Stunden',
|
||||
'%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s hat einen Kommentat der Aufgabe <a href="?controller=task&action=show&task_id=%d">#%d</a> aktualisiert',
|
||||
|
@ -484,8 +485,8 @@ return array(
|
|||
'RSS feed' => 'RSS Feed',
|
||||
'%s updated a comment on the task #%d' => '%s hat einen Kommentar der Aufgabe #%d aktualisiert',
|
||||
'%s commented on the task #%d' => '%s hat die Aufgabe #%d kommentiert',
|
||||
'%s updated a subtask for the task #%d' => '%s hat eine Unteraufgabe der Aufgabe #%d aktualisiert',
|
||||
'%s created a subtask for the task #%d' => '%s hat eine Unteraufgabe der Aufgabe #%d angelegt',
|
||||
'%s updated a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d aktualisiert',
|
||||
'%s created a subtask for the task #%d' => '%s hat eine Teilaufgabe der Aufgabe #%d angelegt',
|
||||
'%s updated the task #%d' => '%s hat die Aufgabe #%d aktualisiert',
|
||||
'%s created the task #%d' => '%s hat die Aufgabe #%d angelegt',
|
||||
'%s closed the task #%d' => '%s hat die Aufgabe #%d geschlossen',
|
||||
|
@ -537,7 +538,7 @@ return array(
|
|||
'ISO format is always accepted, example: "%s" and "%s"' => 'ISO Format wird immer akzeptiert, z.B.: "%s" und "%s"',
|
||||
'New private project' => 'Neues privates Projekt',
|
||||
'This project is private' => 'Dieses Projekt ist privat',
|
||||
'Type here to create a new sub-task' => 'Hier tippen, um eine neue Unteraufgabe zu erstellen',
|
||||
'Type here to create a new sub-task' => 'Hier tippen, um eine neue Teilaufgabe zu erstellen',
|
||||
'Add' => 'Hinzufügen',
|
||||
'Estimated time: %s hours' => 'Geplante Zeit: %s Stunden',
|
||||
'Time spent: %s hours' => 'Aufgewendete Zeit: %s Stunden',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
'Help on Github webhook' => 'Hilfe bei einem Github Webhook',
|
||||
'Create a comment from an external provider' => 'Kommentar eines externen Providers hinzufügen',
|
||||
'Github issue comment created' => 'Github Fehler Kommentar hinzugefügt',
|
||||
'Configure' => 'konfigurieren',
|
||||
'Project management' => 'Projektmanagement',
|
||||
'My projects' => 'Meine Projekte',
|
||||
'Columns' => 'Spalten',
|
||||
'Task' => 'Aufgabe',
|
||||
'Your are not member of any project.' => 'Sie sind nicht Mitglied eines Projekts.',
|
||||
'Percentage' => 'Prozentsatz',
|
||||
'Number of tasks' => 'Anzahl an Aufgaben',
|
||||
'Task distribution' => 'Aufgabenverteilung',
|
||||
'Reportings' => 'Berichte',
|
||||
'Task repartition for "%s"' => 'Aufgabenzuweisung für "%s"',
|
||||
'Analytics' => 'Analyse',
|
||||
'Subtask' => 'Teilaufgabe',
|
||||
'My subtasks' => 'Meine Teilaufgaben',
|
||||
'User repartition' => 'Benutzerverteilung',
|
||||
'User repartition for "%s"' => 'Benutzerverteilung für "%s"',
|
||||
'Clone this project' => 'Projekt kopieren',
|
||||
'Column removed successfully.' => 'Spalte erfolgreich entfernt.',
|
||||
'Edit Project' => 'Projekt bearbeiten',
|
||||
'Github Issue' => 'Github Issue',
|
||||
'Not enough data to show the graph.' => 'Nicht genügend Daten, um die Grafik zu zeigen.',
|
||||
'Previous' => 'Vorherige',
|
||||
'The id must be an integer' => 'Die Id muss eine ganze Zahl sein',
|
||||
'The project id must be an integer' => 'Der Projektid muss eine ganze Zahl sein',
|
||||
'The status must be an integer' => 'Der Status muss eine ganze Zahl sein',
|
||||
'The subtask id is required' => 'Die Teilaufgabenid ist benötigt',
|
||||
'The subtask id must be an integer' => 'Die Teilaufgabenid muss eine ganze Zahl sein',
|
||||
'The task id is required' => 'Die Aufgabenid ist benötigt',
|
||||
'The task id must be an integer' => 'Die Aufgabenid muss eine ganze Zahl sein',
|
||||
'The user id must be an integer' => 'Die Userid muss eine ganze Zahl sein',
|
||||
'This value is required' => 'Dieser Wert ist erforderlich',
|
||||
'This value must be numeric' => 'Dieser Wert muss numerisch sein',
|
||||
'Unable to create this task.' => 'Diese Aufgabe kann nicht erstellt werden',
|
||||
'Cumulative flow diagram' => 'Kumulatives Flussdiagramm',
|
||||
'Cumulative flow diagram for "%s"' => 'Kumulatives Flussdiagramm für "%s"',
|
||||
'Daily project summary' => 'Tägliche Projektzusammenfassung',
|
||||
'Daily project summary export' => 'Export der täglichen Projektzusammenfassung',
|
||||
'Daily project summary export for "%s"' => 'Export der täglichen Projektzusammenfassung für "%s"',
|
||||
'Exports' => 'Exporte',
|
||||
'This export contains the number of tasks per column grouped per day.' => 'Dieser Export enthält die Anzahl der Aufgaben pro Spalte nach Tagen gruppiert.',
|
||||
'Nothing to preview...' => 'Nichts in der Vorschau anzuzeigen ...',
|
||||
'Preview' => 'Vorschau',
|
||||
'Write' => 'Ändern',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Complejidad',
|
||||
'limit' => 'límite',
|
||||
'Task limit' => 'Número máximo de tareas',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Este valor no debe de ser más grande que %d',
|
||||
'Edit project access list' => 'Editar los permisos del proyecto',
|
||||
'Edit users access' => 'Editar los permisos de usuario',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Monimutkaisuus',
|
||||
'limit' => 'raja',
|
||||
'Task limit' => 'Tehtävien maksimimäärä',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Arvon täytyy olla suurempi kuin %d',
|
||||
'Edit project access list' => 'Muuta projektin käyttäjiä',
|
||||
'Edit users access' => 'Muuta käyttäjien pääsyä',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Complexité',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Nombre maximum de tâches',
|
||||
'Task count' => 'Nombre de tâches',
|
||||
'This value must be greater than %d' => 'Cette valeur doit être plus grande que %d',
|
||||
'Edit project access list' => 'Modifier l\'accès au projet',
|
||||
'Edit users access' => 'Modifier les utilisateurs autorisés',
|
||||
|
@ -456,7 +457,7 @@ return array(
|
|||
'Edit profile' => 'Modifier le profil',
|
||||
'Change password' => 'Changer le mot de passe',
|
||||
'Password modification' => 'Changement de mot de passe',
|
||||
'External authentications' => 'Authentifications externe',
|
||||
'External authentications' => 'Authentifications externes',
|
||||
'Google Account' => 'Compte Google',
|
||||
'Github Account' => 'Compte Github',
|
||||
'Never connected.' => 'Jamais connecté.',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
'Help on Github webhook' => 'Aide sur les webhooks Github',
|
||||
'Create a comment from an external provider' => 'Créer un commentaire depuis un fournisseur externe',
|
||||
'Github issue comment created' => 'Commentaire créé sur un ticket Github',
|
||||
'Configure' => 'Configurer',
|
||||
'Project management' => 'Gestion des projets',
|
||||
'My projects' => 'Mes projets',
|
||||
'Columns' => 'Colonnes',
|
||||
'Task' => 'Tâche',
|
||||
'Your are not member of any project.' => 'Vous n\'êtes membre d\'aucun projet.',
|
||||
'Percentage' => 'Pourcentage',
|
||||
'Number of tasks' => 'Nombre de tâches',
|
||||
'Task distribution' => 'Répartition des tâches',
|
||||
'Reportings' => 'Rapports',
|
||||
'Task repartition for "%s"' => 'Répartition des tâches pour « %s »',
|
||||
'Analytics' => 'Analytique',
|
||||
'Subtask' => 'Sous-tâche',
|
||||
'My subtasks' => 'Mes sous-tâches',
|
||||
'User repartition' => 'Répartition des utilisateurs',
|
||||
'User repartition for "%s"' => 'Répartition des utilisateurs pour « %s »',
|
||||
'Clone this project' => 'Cloner ce projet',
|
||||
'Column removed successfully.' => 'Colonne supprimée avec succès.',
|
||||
'Edit Project' => 'Modifier le projet',
|
||||
'Github Issue' => 'Ticket Github',
|
||||
'Not enough data to show the graph.' => 'Pas assez de données pour afficher le graphique.',
|
||||
'Previous' => 'Précédent',
|
||||
'The id must be an integer' => 'L\'id doit être un entier',
|
||||
'The project id must be an integer' => 'L\'id du projet doit être un entier',
|
||||
'The status must be an integer' => 'Le status doit être un entier',
|
||||
'The subtask id is required' => 'L\'id de la sous-tâche est obligatoire',
|
||||
'The subtask id must be an integer' => 'L\'id de la sous-tâche doit être en entier',
|
||||
'The task id is required' => 'L\'id de la tâche est obligatoire',
|
||||
'The task id must be an integer' => 'L\'id de la tâche doit être en entier',
|
||||
'The user id must be an integer' => 'L\'id de l\'utilisateur doit être en entier',
|
||||
'This value is required' => 'Cette valeur est obligatoire',
|
||||
'This value must be numeric' => 'Cette valeur doit être numérique',
|
||||
'Unable to create this task.' => 'Impossible de créer cette tâche',
|
||||
'Cumulative flow diagram' => 'Diagramme de flux cumulé',
|
||||
'Cumulative flow diagram for "%s"' => 'Diagramme de flux cumulé pour « %s »',
|
||||
'Daily project summary' => 'Résumé journalier du projet',
|
||||
'Daily project summary export' => 'Export du résumé journalier du projet',
|
||||
'Daily project summary export for "%s"' => 'Export du résumé quotidien du projet pour « %s »',
|
||||
'Exports' => 'Exports',
|
||||
'This export contains the number of tasks per column grouped per day.' => 'Cet export contient le nombre de tâches par colonne groupé par jour.',
|
||||
'Nothing to preview...' => 'Rien à prévisualiser...',
|
||||
'Preview' => 'Prévisualiser',
|
||||
'Write' => 'Écrire',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
// 'Complexity' => '',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Numero massimo di compiti',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'questo valore deve essere maggiore di %d',
|
||||
'Edit project access list' => 'Modificare i permessi del progetto',
|
||||
'Edit users access' => 'Modificare i permessi degli utenti',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => '複雑さ',
|
||||
'limit' => '制限',
|
||||
'Task limit' => 'タスク数制限',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => '%d より大きな値を入力してください',
|
||||
'Edit project access list' => 'プロジェクトのアクセス許可を変更',
|
||||
'Edit users access' => 'ユーザのアクセス許可を変更',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Poziom trudności',
|
||||
'limit' => 'limit',
|
||||
'Task limit' => 'Limit zadań',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Wartość musi być większa niż %d',
|
||||
'Edit project access list' => 'Edycja list dostępu dla projektu',
|
||||
'Edit users access' => 'Edytuj dostęp',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -108,8 +108,8 @@ return array(
|
|||
'There is nobody assigned' => 'Não há ninguém designado',
|
||||
'Column on the board:' => 'Coluna no quadro:',
|
||||
'Status is open' => 'Status está aberto',
|
||||
'Status is closed' => 'Status está fechado',
|
||||
'Close this task' => 'Fechar esta tarefa',
|
||||
'Status is closed' => 'Status está encerrado',
|
||||
'Close this task' => 'Encerrar esta tarefa',
|
||||
'Open this task' => 'Abrir esta tarefa',
|
||||
'There is no description.' => 'Não há descrição.',
|
||||
'Add a new task' => 'Adicionar uma nova tarefa',
|
||||
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Complexidade',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Limite da tarefa',
|
||||
'Task count' => 'Número de tarefas',
|
||||
'This value must be greater than %d' => 'Este valor deve ser maior que %d',
|
||||
'Edit project access list' => 'Editar lista de acesso ao projeto',
|
||||
'Edit users access' => 'Editar acesso de usuários',
|
||||
|
@ -196,7 +197,7 @@ return array(
|
|||
'revoke' => 'revogar',
|
||||
'List of authorized users' => 'Lista de usuários autorizados',
|
||||
'User' => 'Usuário',
|
||||
// 'Nobody have access to this project.' => '',
|
||||
'Nobody have access to this project.' => 'Ninguém tem acesso a este projeto.',
|
||||
'You are not allowed to access to this project.' => 'Você não está autorizado a acessar este projeto.',
|
||||
'Comments' => 'Comentários',
|
||||
'Post comment' => 'Postar comentário',
|
||||
|
@ -216,7 +217,7 @@ return array(
|
|||
'Your automatic action have been created successfully.' => 'Sua ação automética foi criada com sucesso.',
|
||||
'Unable to create your automatic action.' => 'Impossível criar sua ação automática.',
|
||||
'Remove an action' => 'Remover uma ação',
|
||||
'Unable to remove this action.' => 'Impossível remover esta ação',
|
||||
'Unable to remove this action.' => 'Impossível remover esta ação.',
|
||||
'Action removed successfully.' => 'Ação removida com sucesso.',
|
||||
'Automatic actions for the project "%s"' => 'Ações automáticas para o projeto "%s"',
|
||||
'Defined actions' => 'Ações definidas',
|
||||
|
@ -226,13 +227,13 @@ return array(
|
|||
'Action parameters' => 'Parâmetros da ação',
|
||||
'Action' => 'Ação',
|
||||
'Event' => 'Evento',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Quando o evento selecionado ocorrer, execute a ação correspondente',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Quando o evento selecionado ocorrer, execute a ação correspondente.',
|
||||
'Next step' => 'Próximo passo',
|
||||
'Define action parameters' => 'Definir parêmetros da ação',
|
||||
'Save this action' => 'Salvar esta ação',
|
||||
'Do you really want to remove this action: "%s"?' => 'Você quer realmente remover esta ação: "%s"?',
|
||||
'Remove an automatic action' => 'Remove uma ação automática',
|
||||
'Close the task' => 'Fechar tarefa',
|
||||
'Close the task' => 'Encerrar tarefa',
|
||||
'Assign the task to a specific user' => 'Designar a tarefa para um usuário específico',
|
||||
'Assign the task to the person who does the action' => 'Designar a tarefa para a pessoa que executa a ação',
|
||||
'Duplicate the task to another project' => 'Duplicar a tarefa para um outro projeto',
|
||||
|
@ -240,8 +241,8 @@ return array(
|
|||
'Move a task to another position in the same column' => 'Mover a tarefa para outra posição, na mesma coluna',
|
||||
'Task modification' => 'Modificação de tarefa',
|
||||
'Task creation' => 'Criação de tarefa',
|
||||
'Open a closed task' => 'Reabrir uma tarefa fechada',
|
||||
'Closing a task' => 'Fechando uma tarefa',
|
||||
'Open a closed task' => 'Reabrir uma tarefa encerrada',
|
||||
'Closing a task' => 'Encerrando uma tarefa',
|
||||
'Assign a color to a specific user' => 'Designar uma cor para um usuário específico',
|
||||
'Column title' => 'Título da coluna',
|
||||
'Position' => 'Posição',
|
||||
|
@ -253,9 +254,9 @@ return array(
|
|||
'Update this comment' => 'Atualizar este comentário',
|
||||
'Comment updated successfully.' => 'Comentário atualizado com sucesso.',
|
||||
'Unable to update your comment.' => 'Impossível atualizar seu comentário.',
|
||||
'Remove a comment' => 'Remover um comentário.',
|
||||
'Remove a comment' => 'Remover um comentário',
|
||||
'Comment removed successfully.' => 'Comentário removido com sucesso.',
|
||||
'Unable to remove this comment.' => 'Impossível remover este comentário',
|
||||
'Unable to remove this comment.' => 'Impossível remover este comentário.',
|
||||
'Do you really want to remove this comment?' => 'Você tem certeza de que quer remover este comentário?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Somente administradores ou o criator deste comentário tem acesso a esta página.',
|
||||
'Details' => 'Detalhes',
|
||||
|
@ -263,7 +264,7 @@ return array(
|
|||
'The current password is required' => 'A senha atual é obrigatória',
|
||||
'Wrong password' => 'Senha errada',
|
||||
'Reset all tokens' => 'Reiniciar todos os tokens',
|
||||
'All tokens have been regenerated.' => 'Todos os tokens foram gerados novamente',
|
||||
'All tokens have been regenerated.' => 'Todos os tokens foram gerados novamente.',
|
||||
'Unknown' => 'Desconhecido',
|
||||
'Last logins' => 'Últimos logins',
|
||||
'Login date' => 'Data de login',
|
||||
|
@ -279,7 +280,7 @@ return array(
|
|||
'Filter by due date' => 'Filtrar por data de vencimento',
|
||||
'Everybody' => 'Todos',
|
||||
'Open' => 'Abrir',
|
||||
'Closed' => 'Fechado',
|
||||
'Closed' => 'Encerrado',
|
||||
'Search' => 'Pesquisar',
|
||||
'Nothing found.' => 'Não encontrado.',
|
||||
'Search in the project "%s"' => 'Procure no projeto "%s"',
|
||||
|
@ -391,21 +392,21 @@ return array(
|
|||
'Clone Project' => 'Clonar Projeto',
|
||||
'Project cloned successfully.' => 'Projeto clonado com sucesso.',
|
||||
'Unable to clone this project.' => 'Impossível clonar este projeto.',
|
||||
// 'Email notifications' => '',
|
||||
// 'Enable email notifications' => '',
|
||||
// 'Task position:' => '',
|
||||
// 'The task #%d have been opened.' => '',
|
||||
// 'The task #%d have been closed.' => '',
|
||||
// 'Sub-task updated' => '',
|
||||
// 'Title:' => '',
|
||||
// 'Status:' => '',
|
||||
// 'Assignee:' => '',
|
||||
// 'Time tracking:' => '',
|
||||
// 'New sub-task' => '',
|
||||
// 'New attachment added "%s"' => '',
|
||||
// 'Comment updated' => '',
|
||||
// 'New comment posted by %s' => '',
|
||||
// 'List of due tasks for the project "%s"' => '',
|
||||
'Email notifications' => 'Notificações por email',
|
||||
'Enable email notifications' => 'Habilitar notificações por email',
|
||||
'Task position:' => 'Posição da tarefa:',
|
||||
'The task #%d have been opened.' => 'A tarefa #%d foi aberta.',
|
||||
'The task #%d have been closed.' => 'A tarefa #%d foi encerrada.',
|
||||
'Sub-task updated' => 'Subtarefa atualizada',
|
||||
'Title:' => 'Título:',
|
||||
'Status:' => 'Status:',
|
||||
'Assignee:' => 'Designado:',
|
||||
'Time tracking:' => 'Controle de tempo:',
|
||||
'New sub-task' => 'Nova subtarefa',
|
||||
'New attachment added "%s"' => 'Novo anexo adicionado "%s"',
|
||||
'Comment updated' => 'Comentário atualizado',
|
||||
'New comment posted by %s' => 'Novo comentário postado por %s',
|
||||
'List of due tasks for the project "%s"' => 'Lista de tarefas pendentes para o projeto "%s"',
|
||||
// '[%s][New attachment] %s (#%d)' => '',
|
||||
// '[%s][New comment] %s (#%d)' => '',
|
||||
// '[%s][Comment updated] %s (#%d)' => '',
|
||||
|
@ -416,107 +417,107 @@ return array(
|
|||
// '[%s][Task closed] %s (#%d)' => '',
|
||||
// '[%s][Task opened] %s (#%d)' => '',
|
||||
// '[%s][Due tasks]' => '',
|
||||
// '[Kanboard] Notification' => '',
|
||||
// 'I want to receive notifications only for those projects:' => '',
|
||||
// 'view the task on Kanboard' => '',
|
||||
// 'Public access' => '',
|
||||
// 'Category management' => '',
|
||||
// 'User management' => '',
|
||||
// 'Active tasks' => '',
|
||||
// 'Disable public access' => '',
|
||||
// 'Enable public access' => '',
|
||||
// 'Active projects' => '',
|
||||
// 'Inactive projects' => '',
|
||||
// 'Public access disabled' => '',
|
||||
// 'Do you really want to disable this project: "%s"?' => '',
|
||||
// 'Do you really want to duplicate this project: "%s"?' => '',
|
||||
// 'Do you really want to enable this project: "%s"?' => '',
|
||||
// 'Project activation' => '',
|
||||
// 'Move the task to another project' => '',
|
||||
// 'Move to another project' => '',
|
||||
// 'Do you really want to duplicate this task?' => '',
|
||||
// 'Duplicate a task' => '',
|
||||
// 'External accounts' => '',
|
||||
// 'Account type' => '',
|
||||
'[Kanboard] Notification' => '[Kanboard] Notificação',
|
||||
'I want to receive notifications only for those projects:' => 'Quero receber notificações somente para estes projetos:',
|
||||
'view the task on Kanboard' => 'ver a tarefa no Kanboard',
|
||||
'Public access' => 'Acesso público',
|
||||
'Category management' => 'Gerenciamento de categorias',
|
||||
'User management' => 'Gerenciamento de usuários',
|
||||
'Active tasks' => 'Tarefas ativas',
|
||||
'Disable public access' => 'Desabilitar o acesso público',
|
||||
'Enable public access' => 'Habilitar o acesso público',
|
||||
'Active projects' => 'Projetos ativos',
|
||||
'Inactive projects' => 'Projetos inativos',
|
||||
'Public access disabled' => 'Acesso público desabilitado',
|
||||
'Do you really want to disable this project: "%s"?' => 'Deseja ralmente desabilitar este projeto: "%s"?',
|
||||
'Do you really want to duplicate this project: "%s"?' => 'Deseja realmente duplicar este projeto: "%s"?',
|
||||
'Do you really want to enable this project: "%s"?' => 'Deseja realmente habilitar este projeto: "%s"?',
|
||||
'Project activation' => 'Avaliação do projeto',
|
||||
'Move the task to another project' => 'Mover a tarefa para outro projeto',
|
||||
'Move to another project' => 'Mover para outro projeto',
|
||||
'Do you really want to duplicate this task?' => 'Deseja realmente duplicar esta tarefa?',
|
||||
'Duplicate a task' => 'Duplicar tarefa',
|
||||
'External accounts' => 'Contas externas',
|
||||
'Account type' => 'Tipo de conta',
|
||||
// 'Local' => '',
|
||||
// 'Remote' => '',
|
||||
// 'Enabled' => '',
|
||||
// 'Disabled' => '',
|
||||
// 'Google account linked' => '',
|
||||
// 'Github account linked' => '',
|
||||
// 'Username:' => '',
|
||||
// 'Name:' => '',
|
||||
'Remote' => 'Remoto',
|
||||
'Enabled' => 'Habilitado',
|
||||
'Disabled' => 'Desabilitado',
|
||||
'Google account linked' => 'Conta do Google associada',
|
||||
'Github account linked' => 'Conta do Github associada',
|
||||
'Username:' => 'Usuário:',
|
||||
'Name:' => 'Nome:',
|
||||
// 'Email:' => '',
|
||||
// 'Default project:' => '',
|
||||
// 'Notifications:' => '',
|
||||
// 'Notifications' => '',
|
||||
// 'Group:' => '',
|
||||
// 'Regular user' => '',
|
||||
// 'Account type:' => '',
|
||||
// 'Edit profile' => '',
|
||||
// 'Change password' => '',
|
||||
// 'Password modification' => '',
|
||||
// 'External authentications' => '',
|
||||
// 'Google Account' => '',
|
||||
// 'Github Account' => '',
|
||||
// 'Never connected.' => '',
|
||||
// 'No account linked.' => '',
|
||||
// 'Account linked.' => '',
|
||||
// 'No external authentication enabled.' => '',
|
||||
// 'Password modified successfully.' => '',
|
||||
// 'Unable to change the password.' => '',
|
||||
// 'Change category for the task "%s"' => '',
|
||||
// 'Change category' => '',
|
||||
'Default project:' => 'Projeto padrão:',
|
||||
'Notifications:' => 'Notificações:',
|
||||
'Notifications' => 'Notificações',
|
||||
'Group:' => 'Groupo:',
|
||||
'Regular user' => 'Usuário habitual',
|
||||
'Account type:' => 'Tipo de conta:',
|
||||
'Edit profile' => 'Editar perfil',
|
||||
'Change password' => 'Alterar senha',
|
||||
'Password modification' => 'Alteração de senha',
|
||||
'External authentications' => 'Autenticação externa',
|
||||
'Google Account' => 'Conta do Google',
|
||||
'Github Account' => 'Conta do Github',
|
||||
'Never connected.' => 'Nunca conectado.',
|
||||
'No account linked.' => 'Nenhuma conta associada.',
|
||||
'Account linked.' => 'Conta associada.',
|
||||
'No external authentication enabled.' => 'Nenhuma autenticação externa permitida.',
|
||||
'Password modified successfully.' => 'Senha alterada com sucesso.',
|
||||
'Unable to change the password.' => 'Não foi possível alterar a senha.',
|
||||
'Change category for the task "%s"' => 'Mudar categoria para a tarefa "%s"',
|
||||
'Change category' => 'Mudar categoria',
|
||||
// '%s updated the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s open the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the position #%d in the column "%s"' => '',
|
||||
// '%s moved the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to the column "%s"' => '',
|
||||
// '%s created the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s closed the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// 'Assigned to %s with an estimate of %s/%sh' => '',
|
||||
// 'Not assigned, estimate of %sh' => '',
|
||||
// '%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '',
|
||||
// '%s\'s activity' => '',
|
||||
// 'No activity.' => '',
|
||||
'%s created a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s criou uma sub-tarefa para a tarefa <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s updated a subtask for the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s atualizou uma sub-tarefa da tarefa <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'Assigned to %s with an estimate of %s/%sh' => 'Designado para %s com tempo estimado de %s/%sh',
|
||||
'Not assigned, estimate of %sh' => 'Não designado, estimado em %sh',
|
||||
'%s updated a comment on the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s atualizou o comentário na tarefa <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s commented the task <a href="?controller=task&action=show&task_id=%d">#%d</a>' => '%s comentou a tarefa <a href="?controller=task&action=show&task_id=%d">#%d</a>',
|
||||
'%s\'s activity' => 'Atividades de%s',
|
||||
'No activity.' => 'Sem atividade.',
|
||||
// 'RSS feed' => '',
|
||||
// '%s updated a comment on the task #%d' => '',
|
||||
// '%s commented on the task #%d' => '',
|
||||
// '%s updated a subtask for the task #%d' => '',
|
||||
// '%s created a subtask for the task #%d' => '',
|
||||
// '%s updated the task #%d' => '',
|
||||
// '%s created the task #%d' => '',
|
||||
// '%s closed the task #%d' => '',
|
||||
// '%s open the task #%d' => '',
|
||||
// '%s moved the task #%d to the column "%s"' => '',
|
||||
// '%s moved the task #%d to the position %d in the column "%s"' => '',
|
||||
// 'Activity' => '',
|
||||
// 'Default values are "%s"' => '',
|
||||
// 'Default columns for new projects (Comma-separated)' => '',
|
||||
// 'Task assignee change' => '',
|
||||
// '%s change the assignee of the task #%d to %s' => '',
|
||||
// '%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to %s' => '',
|
||||
'%s updated a comment on the task #%d' => '%s atualizou um comentário na tarefa #%d',
|
||||
'%s commented on the task #%d' => '%s comentou na tarefa #%d',
|
||||
'%s updated a subtask for the task #%d' => '%s atualizou uma sub-tarefa para a tarefa #%d',
|
||||
'%s created a subtask for the task #%d' => '%s criou uma sub-tarefa para a tarefa #%d',
|
||||
'%s updated the task #%d' => '%s atualizou a tarefa #%d',
|
||||
'%s created the task #%d' => '%s criou a tarefa #%d',
|
||||
'%s closed the task #%d' => '%s encerrou a tarefa #%d',
|
||||
'%s open the task #%d' => '%s abriu a tarefa #%d',
|
||||
'%s moved the task #%d to the column "%s"' => '%s moveu a tarefa #%d para a coluna "%s"',
|
||||
'%s moved the task #%d to the position %d in the column "%s"' => '%s moveu a tarefa #%d para a posição %d na coluna "%s"',
|
||||
'Activity' => 'Atividade',
|
||||
'Default values are "%s"' => 'Os valores padrão são "%s"',
|
||||
'Default columns for new projects (Comma-separated)' => 'Colunas padrão para novos projetos (Separado por vírgula)',
|
||||
'Task assignee change' => 'Mudar designação da tarefa',
|
||||
'%s change the assignee of the task #%d to %s' => '%s mudou a designação da tarefa #%d para %s',
|
||||
'%s change the assignee of the task <a href="?controller=task&action=show&task_id=%d">#%d</a> to %s' => '%s mudou a designação da tarefa <a href="?controller=task&action=show&task_id=%d">#%d</a> para %s',
|
||||
// '[%s][Column Change] %s (#%d)' => '',
|
||||
// '[%s][Position Change] %s (#%d)' => '',
|
||||
// '[%s][Assignee Change] %s (#%d)' => '',
|
||||
// 'New password for the user "%s"' => '',
|
||||
// 'Choose an event' => '',
|
||||
'New password for the user "%s"' => 'Novo password para o usuário "%s"',
|
||||
'Choose an event' => 'Escolher um evento',
|
||||
// 'Github commit received' => '',
|
||||
// 'Github issue opened' => '',
|
||||
// 'Github issue closed' => '',
|
||||
// 'Github issue reopened' => '',
|
||||
// 'Github issue assignee change' => '',
|
||||
// 'Github issue label change' => '',
|
||||
// 'Create a task from an external provider' => '',
|
||||
// 'Change the assignee based on an external username' => '',
|
||||
// 'Change the category based on an external label' => '',
|
||||
// 'Reference' => '',
|
||||
// 'Reference: %s' => '',
|
||||
// 'Label' => '',
|
||||
// 'Database' => '',
|
||||
// 'About' => '',
|
||||
'Create a task from an external provider' => 'Criar uma tarefa a partir de um provedor externo',
|
||||
'Change the assignee based on an external username' => 'Alterar designação com vase em um usuário externo!',
|
||||
'Change the category based on an external label' => 'Alterar categoria com base em um rótulo externo',
|
||||
'Reference' => 'Referencia',
|
||||
'Reference: %s' => 'Referencia: %s',
|
||||
'Label' => 'Rótulo',
|
||||
'Database' => 'Banco de dados',
|
||||
'About' => 'Sobre',
|
||||
// 'Database driver:' => '',
|
||||
// 'Board settings' => '',
|
||||
// 'URL and token' => '',
|
||||
|
@ -530,32 +531,75 @@ return array(
|
|||
// 'Period (in second) to consider a task was modified recently (0 to disable, 2 days by default)' => '',
|
||||
// 'Frequency in second (60 seconds by default)' => '',
|
||||
// 'Frequency in second (0 to disable this feature, 10 seconds by default)' => '',
|
||||
// 'Application URL' => '',
|
||||
'Application URL' => 'URL da Aplicação',
|
||||
// 'Example: http://example.kanboard.net/ (used by email notifications)' => '',
|
||||
// 'Token regenerated.' => '',
|
||||
// 'Date format' => '',
|
||||
// 'ISO format is always accepted, example: "%s" and "%s"' => '',
|
||||
// 'New private project' => '',
|
||||
// 'This project is private' => '',
|
||||
// 'Type here to create a new sub-task' => '',
|
||||
// 'Add' => '',
|
||||
// 'Estimated time: %s hours' => '',
|
||||
// 'Time spent: %s hours' => '',
|
||||
// 'Started on %B %e, %Y' => '',
|
||||
// 'Start date' => '',
|
||||
// 'Time estimated' => '',
|
||||
// 'There is nothing assigned to you.' => '',
|
||||
// 'My tasks' => '',
|
||||
'Date format' => 'Formato de data',
|
||||
'ISO format is always accepted, example: "%s" and "%s"' => 'O formato ISO é sempre aceito, exemplo: "%s" e "%s"',
|
||||
'New private project' => 'Novo projeto privado',
|
||||
'This project is private' => 'Este projeto é privado',
|
||||
'Type here to create a new sub-task' => 'Digite aqui para criar uma nova sub-tarefa',
|
||||
'Add' => 'Adicionar',
|
||||
'Estimated time: %s hours' => 'Tempo estimado: %s horas',
|
||||
'Time spent: %s hours' => 'Tempo gasto: %s horas',
|
||||
'Started on %B %e, %Y' => 'Iniciado em %B %e, %Y',
|
||||
'Start date' => 'Data de início',
|
||||
'Time estimated' => 'Tempo estimado',
|
||||
'There is nothing assigned to you.' => 'Não há nada designado para você.',
|
||||
'My tasks' => 'Minhas tarefas',
|
||||
// 'Activity stream' => '',
|
||||
// 'Dashboard' => '',
|
||||
// 'Confirmation' => '',
|
||||
// 'Allow everybody to access to this project' => '',
|
||||
// 'Everybody have access to this project.' => '',
|
||||
'Confirmation' => 'Confirmação',
|
||||
'Allow everybody to access to this project' => 'Permitir que todos acessem este projeto',
|
||||
'Everybody have access to this project.' => 'Todos possuem acesso a este projeto.',
|
||||
// 'Webhooks' => '',
|
||||
// 'API' => '',
|
||||
// 'Integration' => '',
|
||||
'Integration' => 'Integração',
|
||||
// 'Github webhook' => '',
|
||||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
'Help on Github webhook' => 'Ajuda para o Github webhook',
|
||||
'Create a comment from an external provider' => 'Criar um comentário de um provedor externo',
|
||||
// 'Github issue comment created' => '',
|
||||
'Configure' => 'Configurar',
|
||||
'Project management' => 'Gerenciamento de projetos',
|
||||
'My projects' => 'Meus projetos',
|
||||
'Columns' => 'Colunas',
|
||||
'Task' => 'Tarefas',
|
||||
'Your are not member of any project.' => 'Você não é menmbro de nenhum projeto.',
|
||||
'Percentage' => 'Porcentagem',
|
||||
'Number of tasks' => 'Número de tarefas',
|
||||
'Task distribution' => 'Distribuição de tarefas',
|
||||
'Reportings' => 'Relatórios',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
'Analytics' => 'Estatísticas',
|
||||
'Subtask' => 'Sub-tarefa',
|
||||
'My subtasks' => 'Minhas sub-tarefas',
|
||||
'User repartition' => 'Repartição de usuário',
|
||||
'User repartition for "%s"' => 'Repartição de usuário para "%s"',
|
||||
'Clone this project' => 'Clonar o projeto',
|
||||
'Column removed successfully.' => 'Coluna removida com sucesso.',
|
||||
'Edit Project' => 'Editar projeto',
|
||||
// 'Github Issue' => '',
|
||||
'Not enough data to show the graph.' => 'Dados insuficientes para exibir o gráfico.',
|
||||
'Previous' => 'Anterior',
|
||||
'The id must be an integer' => 'A ID deve ser um inteiro',
|
||||
'The project id must be an integer' => 'A ID do projeto deve ser um inteiro',
|
||||
'The status must be an integer' => 'O status deve ser um inteiro',
|
||||
'The subtask id is required' => 'A ID da sub-tarefa é requerida',
|
||||
'The subtask id must be an integer' => 'A ID da sub-tarefa deve ser um inteiro',
|
||||
'The task id is required' => 'A ID da tarefa é requerida',
|
||||
'The task id must be an integer' => 'A ID da tarefa deve ser um inteiro',
|
||||
'The user id must be an integer' => 'A ID de usuário deve ser um inteiro',
|
||||
'This value is required' => 'Este valor é requerido',
|
||||
'This value must be numeric' => 'Este valor deve ser numérico',
|
||||
'Unable to create this task.' => 'Não foi possível criar esta tarefa.',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
'Nothing to preview...' => 'Nada para pré-visualizar...',
|
||||
'Preview' => 'Pré-visualizar',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Сложность',
|
||||
'limit' => 'лимит',
|
||||
'Task limit' => 'Лимит задач',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Это значение должно быть больше %d',
|
||||
'Edit project access list' => 'Изменить доступ к проекту',
|
||||
'Edit users access' => 'Изменить доступ пользователей',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'Ungefärligt antal timmar',
|
||||
'limit' => 'max',
|
||||
'Task limit' => 'Uppgiftsbegränsning',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'Värdet måste vara större än %d',
|
||||
'Edit project access list' => 'Ändra projektåtkomst lista',
|
||||
'Edit users access' => 'Användaråtkomst',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => 'ความซับซ้อน',
|
||||
'limit' => 'จำกัด',
|
||||
'Task limit' => 'จำกัดงาน',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => 'ค่าต้องมากกว่า %d',
|
||||
'Edit project access list' => 'แก้ไขการเข้าถึงรายชื่อโปรเจค',
|
||||
'Edit users access' => 'แก้ไขการเข้าถึงผู้ใช้',
|
||||
|
@ -558,4 +559,47 @@ return array(
|
|||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
// 'Configure' => '',
|
||||
// 'Project management' => '',
|
||||
// 'My projects' => '',
|
||||
// 'Columns' => '',
|
||||
// 'Task' => '',
|
||||
// 'Your are not member of any project.' => '',
|
||||
// 'Percentage' => '',
|
||||
// 'Number of tasks' => '',
|
||||
// 'Task distribution' => '',
|
||||
// 'Reportings' => '',
|
||||
// 'Task repartition for "%s"' => '',
|
||||
// 'Analytics' => '',
|
||||
// 'Subtask' => '',
|
||||
// 'My subtasks' => '',
|
||||
// 'User repartition' => '',
|
||||
// 'User repartition for "%s"' => '',
|
||||
// 'Clone this project' => '',
|
||||
// 'Column removed successfully.' => '',
|
||||
// 'Edit Project' => '',
|
||||
// 'Github Issue' => '',
|
||||
// 'Not enough data to show the graph.' => '',
|
||||
// 'Previous' => '',
|
||||
// 'The id must be an integer' => '',
|
||||
// 'The project id must be an integer' => '',
|
||||
// 'The status must be an integer' => '',
|
||||
// 'The subtask id is required' => '',
|
||||
// 'The subtask id must be an integer' => '',
|
||||
// 'The task id is required' => '',
|
||||
// 'The task id must be an integer' => '',
|
||||
// 'The user id must be an integer' => '',
|
||||
// 'This value is required' => '',
|
||||
// 'This value must be numeric' => '',
|
||||
// 'Unable to create this task.' => '',
|
||||
// 'Cumulative flow diagram' => '',
|
||||
// 'Cumulative flow diagram for "%s"' => '',
|
||||
// 'Daily project summary' => '',
|
||||
// 'Daily project summary export' => '',
|
||||
// 'Daily project summary export for "%s"' => '',
|
||||
// 'Exports' => '',
|
||||
// 'This export contains the number of tasks per column grouped per day.' => '',
|
||||
// 'Nothing to preview...' => '',
|
||||
// 'Preview' => '',
|
||||
// 'Write' => '',
|
||||
);
|
||||
|
|
|
@ -187,6 +187,7 @@ return array(
|
|||
'Complexity' => '复杂度',
|
||||
'limit' => '限制',
|
||||
'Task limit' => '任务限制',
|
||||
// 'Task count' => '',
|
||||
'This value must be greater than %d' => '该数值必须大于%d',
|
||||
'Edit project access list' => '编辑项目存取列表',
|
||||
'Edit users access' => '编辑用户存取权限',
|
||||
|
@ -288,13 +289,13 @@ return array(
|
|||
'Description' => '描述',
|
||||
'%d comments' => '%d个评论',
|
||||
'%d comment' => '%d个评论',
|
||||
'Email address invalid' => 'Email地址无效',
|
||||
'Email address invalid' => '电子邮件地址无效',
|
||||
'Your Google Account is not linked anymore to your profile.' => '您的google帐号不再与您的账户配置关联。',
|
||||
'Unable to unlink your Google Account.' => '无法去除您google帐号的关联',
|
||||
'Google authentication failed' => 'google验证失败',
|
||||
'Unable to link your Google Account.' => '无法关联您的google帐号。',
|
||||
'Your Google Account is linked to your profile successfully.' => '您的google帐号已成功与账户配置关联。',
|
||||
'Email' => 'Email',
|
||||
'Email' => '电子邮件',
|
||||
'Link my Google Account' => '关联我的google帐号',
|
||||
'Unlink my Google Account' => '去除我的google帐号关联',
|
||||
'Login with my Google Account' => '用我的google帐号登录',
|
||||
|
@ -549,13 +550,56 @@ return array(
|
|||
'Activity stream' => '活动流',
|
||||
'Dashboard' => '面板',
|
||||
'Confirmation' => '确认',
|
||||
// 'Allow everybody to access to this project' => '',
|
||||
// 'Everybody have access to this project.' => '',
|
||||
// 'Webhooks' => '',
|
||||
// 'API' => '',
|
||||
// 'Integration' => '',
|
||||
// 'Github webhook' => '',
|
||||
// 'Help on Github webhook' => '',
|
||||
// 'Create a comment from an external provider' => '',
|
||||
// 'Github issue comment created' => '',
|
||||
'Allow everybody to access to this project' => '允许所有人访问此项目',
|
||||
'Everybody have access to this project.' => '所有人都可以访问此项目',
|
||||
'Webhooks' => '网络钩子',
|
||||
'API' => '应用程序接口',
|
||||
'Integration' => '整合',
|
||||
'Github webhook' => 'Github 网络钩子',
|
||||
'Help on Github webhook' => 'Github 网络钩子帮助',
|
||||
'Create a comment from an external provider' => '从外部创建一个评论',
|
||||
'Github issue comment created' => '已经创建了Github问题评论',
|
||||
'Configure' => '配置',
|
||||
'Project management' => '项目管理',
|
||||
'My projects' => '我的项目',
|
||||
'Columns' => '栏目',
|
||||
'Task' => '任务',
|
||||
'Your are not member of any project.' => '您尚未加入任何项目',
|
||||
'Percentage' => '百分比',
|
||||
'Number of tasks' => '任务数',
|
||||
'Task distribution' => '任务分布',
|
||||
'Reportings' => '报告',
|
||||
'Task repartition for "%s"' => '"%s"的任务分析',
|
||||
'Analytics' => '分析',
|
||||
'Subtask' => '子任务',
|
||||
'My subtasks' => '我的子任务',
|
||||
'User repartition' => '用户分析',
|
||||
'User repartition for "%s"' => '"%s"的用户分析',
|
||||
'Clone this project' => '复制此项目',
|
||||
'Column removed successfully.' => '成功删除了栏目。',
|
||||
'Edit Project' => '编辑项目',
|
||||
'Github Issue' => 'Github 任务报告',
|
||||
'Not enough data to show the graph.' => '数据不足,无法绘图。',
|
||||
'Previous' => '后退',
|
||||
'The id must be an integer' => '编号必须为整数',
|
||||
'The project id must be an integer' => '项目编号必须为整数',
|
||||
'The status must be an integer' => '状态必须为整数',
|
||||
'The subtask id is required' => '必须提供子任务编号',
|
||||
'The subtask id must be an integer' => '子任务编号必须为整数',
|
||||
'The task id is required' => '需要任务编号',
|
||||
'The task id must be an integer' => '任务编号必须为整数',
|
||||
'The user id must be an integer' => '用户编号必须为整数',
|
||||
'This value is required' => '必须给出这个值',
|
||||
'This value must be numeric' => '这个值必须为数字',
|
||||
'Unable to create this task.' => '无法创建此任务。',
|
||||
'Cumulative flow diagram' => '累积流图表',
|
||||
'Cumulative flow diagram for "%s"' => '"%s"的累积流图表',
|
||||
'Daily project summary' => '每日项目汇总',
|
||||
'Daily project summary export' => '导出每日项目汇总',
|
||||
'Daily project summary export for "%s"' => '导出项目"%s"的每日汇总',
|
||||
'Exports' => '导出',
|
||||
'This export contains the number of tasks per column grouped per day.' => '此导出包含每列的任务数,按天分组',
|
||||
'Nothing to preview...' => '没有需要预览的内容',
|
||||
'Preview' => '预览',
|
||||
'Write' => '书写',
|
||||
);
|
||||
|
|
|
@ -31,9 +31,9 @@ class Acl extends Base
|
|||
* @var array
|
||||
*/
|
||||
private $user_actions = array(
|
||||
'app' => array('index'),
|
||||
'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory', 'movecolumn', 'edit', 'update', 'add', 'confirm', 'remove'),
|
||||
'project' => array('index', 'show', 'export', 'share', 'edit', 'update', 'users', 'remove', 'duplicate', 'disable', 'enable', 'activity', 'search', 'tasks', 'create', 'save'),
|
||||
'app' => array('index', 'preview', 'status'),
|
||||
'project' => array('index', 'show', 'exporttasks', 'exportdaily', 'share', 'edit', 'update', 'users', 'remove', 'duplicate', 'disable', 'enable', 'activity', 'search', 'tasks', 'create', 'save'),
|
||||
'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory', 'movecolumn', 'edit', 'update', 'add', 'confirm', 'remove', 'subtasks', 'togglesubtask', 'attachments', 'comments', 'description'),
|
||||
'user' => array('edit', 'forbidden', 'logout', 'show', 'external', 'unlinkgoogle', 'unlinkgithub', 'sessions', 'removesession', 'last', 'notifications', 'password'),
|
||||
'comment' => array('create', 'save', 'confirm', 'remove', 'update', 'edit', 'forbidden'),
|
||||
'file' => array('create', 'save', 'download', 'confirm', 'remove', 'open', 'image'),
|
||||
|
@ -41,6 +41,7 @@ class Acl extends Base
|
|||
'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy', 'time'),
|
||||
'category' => array('index', 'save', 'edit', 'update', 'confirm', 'remove'),
|
||||
'action' => array('index', 'event', 'params', 'create', 'confirm', 'remove'),
|
||||
'analytic' => array('tasks', 'users', 'cfd'),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
@ -264,7 +264,7 @@ class Action extends Base
|
|||
public function load($name, $project_id, $event)
|
||||
{
|
||||
$className = '\Action\\'.$name;
|
||||
return new $className($this->registry, $project_id, $event);
|
||||
return new $className($this->container, $project_id, $event);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,12 +24,12 @@ class Authentication extends Base
|
|||
*/
|
||||
public function backend($name)
|
||||
{
|
||||
if (! isset($this->registry->$name)) {
|
||||
if (! isset($this->container[$name])) {
|
||||
$class = '\Auth\\'.ucfirst($name);
|
||||
$this->registry->$name = new $class($this->registry);
|
||||
$this->container[$name] = new $class($this->container);
|
||||
}
|
||||
|
||||
return $this->registry->shared($name);
|
||||
return $this->container[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,7 +4,7 @@ namespace Model;
|
|||
|
||||
use Core\Event;
|
||||
use Core\Tool;
|
||||
use Core\Registry;
|
||||
use Pimple\Container;
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
|
@ -31,9 +31,11 @@ use PicoDb\Database;
|
|||
* @property \Model\SubTask $subTask
|
||||
* @property \Model\SubtaskHistory $subtaskHistory
|
||||
* @property \Model\Task $task
|
||||
* @property \Model\TaskCreation $taskCreation
|
||||
* @property \Model\TaskExport $taskExport
|
||||
* @property \Model\TaskFinder $taskFinder
|
||||
* @property \Model\TaskHistory $taskHistory
|
||||
* @property \Model\TaskPosition $taskPosition
|
||||
* @property \Model\TaskValidator $taskValidator
|
||||
* @property \Model\TimeTracking $timeTracking
|
||||
* @property \Model\User $user
|
||||
|
@ -58,24 +60,24 @@ abstract class Base
|
|||
public $event;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
* Container instance
|
||||
*
|
||||
* @access protected
|
||||
* @var \Core\Registry
|
||||
* @var \Pimple\Container
|
||||
*/
|
||||
protected $registry;
|
||||
protected $container;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
* @param \Pimple\Container $container
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
public function __construct(Container $container)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->db = $this->registry->shared('db');
|
||||
$this->event = $this->registry->shared('event');
|
||||
$this->container = $container;
|
||||
$this->db = $this->container['db'];
|
||||
$this->event = $this->container['event'];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -87,7 +89,27 @@ abstract class Base
|
|||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
return Tool::loadModel($this->registry, $name);
|
||||
return Tool::loadModel($this->container, $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a record in the database
|
||||
*
|
||||
* @access public
|
||||
* @param string $table Table name
|
||||
* @param array $values Form values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function persist($table, array $values)
|
||||
{
|
||||
return $this->db->transaction(function($db) use ($table, $values) {
|
||||
|
||||
if (! $db->table($table)->save($values)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (int) $db->getConnection()->getLastId();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -109,16 +109,18 @@ class Board extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @param string $title Column title
|
||||
* @param integer $task_limit Task limit
|
||||
* @return boolean
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function addColumn($project_id, $title, $task_limit = 0)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->save(array(
|
||||
$values = array(
|
||||
'project_id' => $project_id,
|
||||
'title' => $title,
|
||||
'task_limit' => $task_limit,
|
||||
'position' => $this->getLastColumnPosition($project_id) + 1,
|
||||
));
|
||||
);
|
||||
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -229,10 +231,9 @@ class Board extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $filters
|
||||
* @return array
|
||||
*/
|
||||
public function get($project_id, array $filters = array())
|
||||
public function get($project_id)
|
||||
{
|
||||
$columns = $this->getColumns($project_id);
|
||||
$tasks = $this->taskFinder->getTasksOnBoard($project_id);
|
||||
|
|
|
@ -45,6 +45,34 @@ class Category extends Base
|
|||
return $this->db->table(self::TABLE)->eq('id', $category_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category name by the id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $category_id Category id
|
||||
* @return string
|
||||
*/
|
||||
public function getNameById($category_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a category id by the project and the name
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $category_name Category name
|
||||
* @return integer
|
||||
*/
|
||||
public function getIdByName($project_id, $category_name)
|
||||
{
|
||||
return (int) $this->db->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('name', $category_name)
|
||||
->findOneColumn('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all categories
|
||||
*
|
||||
|
@ -94,11 +122,11 @@ class Category extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
* @return bool|integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -137,26 +165,26 @@ class Category extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Duplicate categories from a project to another one
|
||||
* Duplicate categories from a project to another one, must be executed inside a transaction
|
||||
*
|
||||
* @author Antonio Rabelo
|
||||
* @param integer $project_from Project Template
|
||||
* @return integer $project_to Project that receives the copy
|
||||
* @param integer $src_project_id Source project id
|
||||
* @return integer $dst_project_id Destination project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($project_from, $project_to)
|
||||
public function duplicate($src_project_id, $dst_project_id)
|
||||
{
|
||||
$categories = $this->db->table(self::TABLE)
|
||||
->columns('name')
|
||||
->eq('project_id', $project_from)
|
||||
->eq('project_id', $src_project_id)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
|
||||
foreach ($categories as $category) {
|
||||
|
||||
$category['project_id'] = $project_to;
|
||||
$category['project_id'] = $dst_project_id;
|
||||
|
||||
if (! $this->category->create($category)) {
|
||||
if (! $this->db->table(self::TABLE)->save($category)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,4 +28,15 @@ class Color extends Base
|
|||
'grey' => t('Grey'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default color
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getDefaultColor()
|
||||
{
|
||||
return 'yellow'; // TODO: make this parameter configurable
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,24 +95,22 @@ class Comment extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Save a comment in the database
|
||||
* Create a new comment
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$values['date'] = time();
|
||||
$comment_id = $this->persist(self::TABLE, $values);
|
||||
|
||||
if ($this->db->table(self::TABLE)->save($values)) {
|
||||
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
return true;
|
||||
if ($comment_id) {
|
||||
$this->event->trigger(self::EVENT_CREATE, array('id' => $comment_id) + $values);
|
||||
}
|
||||
|
||||
return false;
|
||||
return $comment_id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -47,13 +47,11 @@ class GithubWebhook extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param string $type Github event type
|
||||
* @param string $payload Raw Github event (JSON)
|
||||
* @param array $payload Github event
|
||||
* @return boolean
|
||||
*/
|
||||
public function parsePayload($type, $payload)
|
||||
public function parsePayload($type, array $payload)
|
||||
{
|
||||
$payload = json_decode($payload, true);
|
||||
|
||||
switch ($type) {
|
||||
case 'push':
|
||||
return $this->parsePushEvent($payload);
|
||||
|
|
|
@ -101,23 +101,23 @@ class Notification extends Base
|
|||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_CREATE => 'notification_task_creation',
|
||||
Task::EVENT_UPDATE => 'notification_task_update',
|
||||
Task::EVENT_CLOSE => 'notification_task_close',
|
||||
Task::EVENT_OPEN => 'notification_task_open',
|
||||
Task::EVENT_MOVE_COLUMN => 'notification_task_move_column',
|
||||
Task::EVENT_MOVE_POSITION => 'notification_task_move_position',
|
||||
Task::EVENT_ASSIGNEE_CHANGE => 'notification_task_assignee_change',
|
||||
SubTask::EVENT_CREATE => 'notification_subtask_creation',
|
||||
SubTask::EVENT_UPDATE => 'notification_subtask_update',
|
||||
Comment::EVENT_CREATE => 'notification_comment_creation',
|
||||
Comment::EVENT_UPDATE => 'notification_comment_update',
|
||||
File::EVENT_CREATE => 'notification_file_creation',
|
||||
Task::EVENT_CREATE => 'task_creation',
|
||||
Task::EVENT_UPDATE => 'task_update',
|
||||
Task::EVENT_CLOSE => 'task_close',
|
||||
Task::EVENT_OPEN => 'task_open',
|
||||
Task::EVENT_MOVE_COLUMN => 'task_move_column',
|
||||
Task::EVENT_MOVE_POSITION => 'task_move_position',
|
||||
Task::EVENT_ASSIGNEE_CHANGE => 'task_assignee_change',
|
||||
SubTask::EVENT_CREATE => 'subtask_creation',
|
||||
SubTask::EVENT_UPDATE => 'subtask_update',
|
||||
Comment::EVENT_CREATE => 'comment_creation',
|
||||
Comment::EVENT_UPDATE => 'comment_update',
|
||||
File::EVENT_CREATE => 'file_creation',
|
||||
);
|
||||
|
||||
foreach ($events as $event_name => $template_name) {
|
||||
|
||||
$listener = new NotificationListener($this->registry);
|
||||
$listener = new NotificationListener($this->container);
|
||||
$listener->setTemplate($template_name);
|
||||
|
||||
$this->event->attach($event_name, $listener);
|
||||
|
@ -135,8 +135,7 @@ class Notification extends Base
|
|||
public function sendEmails($template, array $users, array $data)
|
||||
{
|
||||
try {
|
||||
$transport = $this->registry->shared('mailer');
|
||||
$mailer = Swift_Mailer::newInstance($transport);
|
||||
$mailer = Swift_Mailer::newInstance($this->container['mailer']);
|
||||
|
||||
$message = Swift_Message::newInstance()
|
||||
->setSubject($this->getMailSubject($template, $data))
|
||||
|
@ -149,7 +148,7 @@ class Notification extends Base
|
|||
}
|
||||
}
|
||||
catch (Swift_TransportException $e) {
|
||||
debug($e->getMessage());
|
||||
$this->container['logger']->addError($e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -163,43 +162,43 @@ class Notification extends Base
|
|||
public function getMailSubject($template, array $data)
|
||||
{
|
||||
switch ($template) {
|
||||
case 'notification_file_creation':
|
||||
case 'file_creation':
|
||||
$subject = e('[%s][New attachment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_comment_creation':
|
||||
case 'comment_creation':
|
||||
$subject = e('[%s][New comment] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_comment_update':
|
||||
case 'comment_update':
|
||||
$subject = e('[%s][Comment updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_subtask_creation':
|
||||
case 'subtask_creation':
|
||||
$subject = e('[%s][New subtask] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_subtask_update':
|
||||
case 'subtask_update':
|
||||
$subject = e('[%s][Subtask updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_creation':
|
||||
case 'task_creation':
|
||||
$subject = e('[%s][New task] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_update':
|
||||
case 'task_update':
|
||||
$subject = e('[%s][Task updated] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_close':
|
||||
case 'task_close':
|
||||
$subject = e('[%s][Task closed] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_open':
|
||||
case 'task_open':
|
||||
$subject = e('[%s][Task opened] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_move_column':
|
||||
case 'task_move_column':
|
||||
$subject = e('[%s][Column Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_move_position':
|
||||
case 'task_move_position':
|
||||
$subject = e('[%s][Position Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_assignee_change':
|
||||
case 'task_assignee_change':
|
||||
$subject = e('[%s][Assignee Change] %s (#%d)', $data['task']['project_name'], $data['task']['title'], $data['task']['id']);
|
||||
break;
|
||||
case 'notification_task_due':
|
||||
case 'task_due':
|
||||
$subject = e('[%s][Due tasks]', $data['project']);
|
||||
break;
|
||||
default:
|
||||
|
@ -219,7 +218,7 @@ class Notification extends Base
|
|||
public function getMailContent($template, array $data)
|
||||
{
|
||||
$tpl = new Template;
|
||||
return $tpl->load($template, $data + array('application_url' => $this->config->get('application_url')));
|
||||
return $tpl->load('notification/'.$template, $data + array('application_url' => $this->config->get('application_url')));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -192,7 +192,7 @@ class Project extends Base
|
|||
public function getStats($project_id)
|
||||
{
|
||||
$stats = array();
|
||||
$columns = $this->board->getcolumns($project_id);
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$stats['nb_active_tasks'] = 0;
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
|
@ -270,11 +270,12 @@ class Project extends Base
|
|||
* Create a project
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @param integer $user_id User who create the project
|
||||
* @return integer Project id
|
||||
* @param array $values Form values
|
||||
* @param integer $user_id User who create the project
|
||||
* @param bool $add_user Automatically add the user
|
||||
* @return integer Project id
|
||||
*/
|
||||
public function create(array $values, $user_id = 0)
|
||||
public function create(array $values, $user_id = 0, $add_user = false)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
|
@ -294,7 +295,7 @@ class Project extends Base
|
|||
return false;
|
||||
}
|
||||
|
||||
if ($values['is_private'] && $user_id) {
|
||||
if ($add_user && $user_id) {
|
||||
$this->projectPermission->allowUser($project_id, $user_id);
|
||||
}
|
||||
|
||||
|
@ -512,7 +513,7 @@ class Project extends Base
|
|||
GithubWebhook::EVENT_COMMIT,
|
||||
);
|
||||
|
||||
$listener = new ProjectModificationDateListener($this->registry);
|
||||
$listener = new ProjectModificationDateListener($this->container);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
|
|
|
@ -147,7 +147,7 @@ class ProjectActivity extends Base
|
|||
SubTask::EVENT_CREATE,
|
||||
);
|
||||
|
||||
$listener = new ProjectActivityListener($this->registry);
|
||||
$listener = new ProjectActivityListener($this->container);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
|
@ -164,7 +164,7 @@ class ProjectActivity extends Base
|
|||
public function getContent(array $params)
|
||||
{
|
||||
$tpl = new Template;
|
||||
return $tpl->load('event_'.str_replace('.', '_', $params['event_name']), $params);
|
||||
return $tpl->load('event/'.str_replace('.', '_', $params['event_name']), $params);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
88
sources/app/Model/ProjectAnalytic.php
Normal file
88
sources/app/Model/ProjectAnalytic.php
Normal file
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Project analytic model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectAnalytic extends Base
|
||||
{
|
||||
/**
|
||||
* Get tasks repartition
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getTaskRepartition($project_id)
|
||||
{
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
|
||||
foreach ($columns as $column) {
|
||||
|
||||
$nb_tasks = $this->taskFinder->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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get users repartition
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getUserRepartition($project_id)
|
||||
{
|
||||
$metrics = array();
|
||||
$total = 0;
|
||||
$tasks = $this->taskFinder->getAll($project_id);
|
||||
$users = $this->projectPermission->getMemberList($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);
|
||||
}
|
||||
|
||||
return array_values($metrics);
|
||||
}
|
||||
}
|
181
sources/app/Model/ProjectDailySummary.php
Normal file
181
sources/app/Model/ProjectDailySummary.php
Normal file
|
@ -0,0 +1,181 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use Core\Template;
|
||||
use Event\ProjectDailySummaryListener;
|
||||
|
||||
/**
|
||||
* Project daily summary
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectDailySummary extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'project_daily_summaries';
|
||||
|
||||
/**
|
||||
* Update daily totals for the project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $date Record date (YYYY-MM-DD)
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateTotals($project_id, $date)
|
||||
{
|
||||
return $this->db->transaction(function($db) use ($project_id, $date) {
|
||||
|
||||
$column_ids = $db->table(Board::TABLE)->eq('project_id', $project_id)->findAllByColumn('id');
|
||||
|
||||
foreach ($column_ids as $column_id) {
|
||||
|
||||
// This call will fail if the record already exists
|
||||
// (cross database driver hack for INSERT..ON DUPLICATE KEY UPDATE)
|
||||
$db->table(ProjectDailySummary::TABLE)->insert(array(
|
||||
'day' => $date,
|
||||
'project_id' => $project_id,
|
||||
'column_id' => $column_id,
|
||||
'total' => 0,
|
||||
));
|
||||
|
||||
$db->table(ProjectDailySummary::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
->eq('day', $date)
|
||||
->update(array(
|
||||
'total' => $db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
->eq('is_active', Task::STATUS_OPEN)
|
||||
->count()
|
||||
));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of recorded days for the data range
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $from Start date (ISO format YYYY-MM-DD)
|
||||
* @param string $to End date
|
||||
* @return integer
|
||||
*/
|
||||
public function countDays($project_id, $from, $to)
|
||||
{
|
||||
$rq = $this->db->execute(
|
||||
'SELECT COUNT(DISTINCT day) FROM '.self::TABLE.' WHERE day >= ? AND day <= ? AND project_id=?',
|
||||
array($from, $to, $project_id)
|
||||
);
|
||||
|
||||
return $rq !== false ? $rq->fetchColumn(0) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw metrics for the project within a data range
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $from Start date (ISO format YYYY-MM-DD)
|
||||
* @param string $to End date
|
||||
* @return array
|
||||
*/
|
||||
public function getRawMetrics($project_id, $from, $to)
|
||||
{
|
||||
return $this->db->table(ProjectDailySummary::TABLE)
|
||||
->columns(
|
||||
ProjectDailySummary::TABLE.'.column_id',
|
||||
ProjectDailySummary::TABLE.'.day',
|
||||
ProjectDailySummary::TABLE.'.total',
|
||||
Board::TABLE.'.title AS column_title'
|
||||
)
|
||||
->join(Board::TABLE, 'id', 'column_id')
|
||||
->eq(ProjectDailySummary::TABLE.'.project_id', $project_id)
|
||||
->gte('day', $from)
|
||||
->lte('day', $to)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aggregated metrics for the project within a data range
|
||||
*
|
||||
* [
|
||||
* ['Date', 'Column1', 'Column2'],
|
||||
* ['2014-11-16', 2, 5],
|
||||
* ['2014-11-17', 20, 15],
|
||||
* ]
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $from Start date (ISO format YYYY-MM-DD)
|
||||
* @param string $to End date
|
||||
* @return array
|
||||
*/
|
||||
public function getAggregatedMetrics($project_id, $from, $to)
|
||||
{
|
||||
$columns = $this->board->getColumnsList($project_id);
|
||||
$column_ids = array_keys($columns);
|
||||
$metrics = array(array(e('Date')) + $columns);
|
||||
$aggregates = array();
|
||||
|
||||
// Fetch metrics for the project
|
||||
$records = $this->db->table(ProjectDailySummary::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->gte('day', $from)
|
||||
->lte('day', $to)
|
||||
->findAll();
|
||||
|
||||
// Aggregate by day
|
||||
foreach ($records as $record) {
|
||||
|
||||
if (! isset($aggregates[$record['day']])) {
|
||||
$aggregates[$record['day']] = array($record['day']);
|
||||
}
|
||||
|
||||
$aggregates[$record['day']][$record['column_id']] = $record['total'];
|
||||
}
|
||||
|
||||
// Aggregate by row
|
||||
foreach ($aggregates as $aggregate) {
|
||||
|
||||
$row = array($aggregate[0]);
|
||||
|
||||
foreach ($column_ids as $column_id) {
|
||||
$row[] = (int) $aggregate[$column_id];
|
||||
}
|
||||
|
||||
$metrics[] = $row;
|
||||
}
|
||||
|
||||
return $metrics;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events to be able to record the metrics
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_CREATE,
|
||||
Task::EVENT_CLOSE,
|
||||
Task::EVENT_OPEN,
|
||||
Task::EVENT_MOVE_COLUMN,
|
||||
);
|
||||
|
||||
$listener = new ProjectDailySummaryListener($this->container);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
}
|
49
sources/app/Model/ProjectPaginator.php
Normal file
49
sources/app/Model/ProjectPaginator.php
Normal file
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Project Paginator
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class ProjectPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Get project summary for a list of project (number of tasks for each column)
|
||||
*
|
||||
* @access public
|
||||
* @param array $project_ids List of project id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function projectSummaries(array $project_ids, $offset = 0, $limit = 25, $column = 'name', $direction = 'asc')
|
||||
{
|
||||
if (empty($project_ids)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
$projects = $this->db
|
||||
->table(Project::TABLE)
|
||||
->in('id', $project_ids)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
|
||||
foreach ($projects as &$project) {
|
||||
|
||||
$project['columns'] = $this->board->getColumns($project['id']);
|
||||
|
||||
foreach ($project['columns'] as &$column) {
|
||||
$column['nb_tasks'] = $this->taskFinder->countByColumnId($project['id'], $column['id']);
|
||||
}
|
||||
}
|
||||
|
||||
return $projects;
|
||||
}
|
||||
}
|
|
@ -27,11 +27,16 @@ class ProjectPermission extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @param bool $prepend_unassigned Prepend the 'Unassigned' value
|
||||
* @param bool $prepend_everybody Prepend the 'Everbody' value
|
||||
* @param bool $allow_single_user If there is only one user return only this user
|
||||
* @return array
|
||||
*/
|
||||
public function getUsersList($project_id, $prepend_unassigned = true, $prepend_everybody = false)
|
||||
public function getMemberList($project_id, $prepend_unassigned = true, $prepend_everybody = false, $allow_single_user = false)
|
||||
{
|
||||
$allowed_users = $this->getAllowedUsers($project_id);
|
||||
$allowed_users = $this->getMembers($project_id);
|
||||
|
||||
if ($allow_single_user && count($allowed_users) === 1) {
|
||||
return $allowed_users;
|
||||
}
|
||||
|
||||
if ($prepend_unassigned) {
|
||||
$allowed_users = array(t('Unassigned')) + $allowed_users;
|
||||
|
@ -51,7 +56,7 @@ class ProjectPermission extends Base
|
|||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedUsers($project_id)
|
||||
public function getMembers($project_id)
|
||||
{
|
||||
if ($this->isEverybodyAllowed($project_id)) {
|
||||
return $this->user->getList();
|
||||
|
@ -96,7 +101,7 @@ class ProjectPermission extends Base
|
|||
|
||||
$all_users = $this->user->getList();
|
||||
|
||||
$users['allowed'] = $this->getAllowedUsers($project_id);
|
||||
$users['allowed'] = $this->getMembers($project_id);
|
||||
|
||||
foreach ($all_users as $user_id => $username) {
|
||||
|
||||
|
@ -141,19 +146,15 @@ class ProjectPermission extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Check if a specific user is allowed to access to a given project
|
||||
* Check if a specific user is member of a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function isUserAllowed($project_id, $user_id)
|
||||
public function isMember($project_id, $user_id)
|
||||
{
|
||||
if ($this->user->isAdmin($user_id)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->isEverybodyAllowed($project_id)) {
|
||||
return true;
|
||||
}
|
||||
|
@ -165,6 +166,19 @@ class ProjectPermission extends Base
|
|||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a specific user is allowed to access to a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function isUserAllowed($project_id, $user_id)
|
||||
{
|
||||
return $project_id === 0 || $this->user->isAdmin($user_id) || $this->isMember($project_id, $user_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if everybody is allowed for the project
|
||||
*
|
||||
|
@ -204,12 +218,13 @@ class ProjectPermission extends Base
|
|||
* @access public
|
||||
* @param array $projects Project list: ['project_id' => 'project_name']
|
||||
* @param integer $user_id User id
|
||||
* @param string $filter Method name to apply
|
||||
* @return array
|
||||
*/
|
||||
public function filterProjects(array $projects, $user_id)
|
||||
public function filterProjects(array $projects, $user_id, $filter = 'isUserAllowed')
|
||||
{
|
||||
foreach ($projects as $project_id => $project_name) {
|
||||
if (! $this->isUserAllowed($project_id, $user_id)) {
|
||||
if (! $this->$filter($project_id, $user_id)) {
|
||||
unset($projects[$project_id]);
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +233,7 @@ class ProjectPermission extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Return a list of projects for a given user
|
||||
* Return a list of allowed projects for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
|
@ -226,7 +241,19 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function getAllowedProjects($user_id)
|
||||
{
|
||||
return $this->filterProjects($this->project->getListByStatus(Project::ACTIVE), $user_id);
|
||||
return $this->filterProjects($this->project->getListByStatus(Project::ACTIVE), $user_id, 'isUserAllowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of projects where the user is member
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getMemberProjects($user_id)
|
||||
{
|
||||
return $this->filterProjects($this->project->getListByStatus(Project::ACTIVE), $user_id, 'isMember');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -239,7 +266,7 @@ class ProjectPermission extends Base
|
|||
*/
|
||||
public function duplicate($project_from, $project_to)
|
||||
{
|
||||
$users = $this->getAllowedUsers($project_from);
|
||||
$users = $this->getMembers($project_from);
|
||||
|
||||
foreach ($users as $user_id => $name) {
|
||||
if (! $this->allowUser($project_to, $user_id)) {
|
||||
|
|
|
@ -134,23 +134,22 @@ class SubTask extends Base
|
|||
}
|
||||
|
||||
/**
|
||||
* Create
|
||||
* Create a new subtask
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
* @return bool|integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(self::TABLE)->save($values);
|
||||
$subtask_id = $this->persist(self::TABLE, $values);
|
||||
|
||||
if ($result) {
|
||||
$values['id'] = $this->db->getConnection()->getLastId();
|
||||
$this->event->trigger(self::EVENT_CREATE, $values);
|
||||
if ($subtask_id) {
|
||||
$this->event->trigger(self::EVENT_CREATE, array('id' => $subtask_id) + $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $subtask_id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -172,6 +171,28 @@ class SubTask extends Base
|
|||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the status of subtask
|
||||
*
|
||||
* Todo -> In progress -> Done -> Todo -> etc...
|
||||
*
|
||||
* @access public
|
||||
* @param integer $subtask_id
|
||||
* @return bool
|
||||
*/
|
||||
public function toggleStatus($subtask_id)
|
||||
{
|
||||
$subtask = $this->getById($subtask_id);
|
||||
|
||||
$values = array(
|
||||
'id' => $subtask['id'],
|
||||
'status' => ($subtask['status'] + 1) % 3,
|
||||
'task_id' => $subtask['task_id'],
|
||||
);
|
||||
|
||||
return $this->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove
|
||||
*
|
||||
|
@ -194,22 +215,22 @@ class SubTask extends Base
|
|||
*/
|
||||
public function duplicate($src_task_id, $dst_task_id)
|
||||
{
|
||||
$subtasks = $this->db->table(self::TABLE)
|
||||
->columns('title', 'time_estimated')
|
||||
->eq('task_id', $src_task_id)
|
||||
->findAll();
|
||||
return $this->db->transaction(function ($db) use ($src_task_id, $dst_task_id) {
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtasks = $db->table(SubTask::TABLE)
|
||||
->columns('title', 'time_estimated')
|
||||
->eq('task_id', $src_task_id)
|
||||
->findAll();
|
||||
|
||||
$subtask['task_id'] = $dst_task_id;
|
||||
$subtask['time_spent'] = 0;
|
||||
foreach ($subtasks as &$subtask) {
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($subtask)) {
|
||||
return false;
|
||||
$subtask['task_id'] = $dst_task_id;
|
||||
|
||||
if (! $db->table(SubTask::TABLE)->save($subtask)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -242,6 +263,29 @@ class SubTask extends Base
|
|||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The subtask id is required')),
|
||||
new Validators\Required('task_id', t('The task id is required')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate API modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateApiModification(array $values)
|
||||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The subtask id is required')),
|
||||
|
|
68
sources/app/Model/SubtaskPaginator.php
Normal file
68
sources/app/Model/SubtaskPaginator.php
Normal file
|
@ -0,0 +1,68 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Subtask Paginator
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class SubtaskPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Get all subtasks assigned to a user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $status List of status
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function userSubtasks($user_id, array $status, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'asc')
|
||||
{
|
||||
$status_list = $this->subTask->getStatusList();
|
||||
|
||||
$subtasks = $this->db->table(SubTask::TABLE)
|
||||
->columns(
|
||||
SubTask::TABLE.'.*',
|
||||
Task::TABLE.'.project_id',
|
||||
Task::TABLE.'.color_id',
|
||||
Project::TABLE.'.name AS project_name'
|
||||
)
|
||||
->eq('user_id', $user_id)
|
||||
->in(SubTask::TABLE.'.status', $status)
|
||||
->join(Task::TABLE, 'id', 'task_id')
|
||||
->join(Project::TABLE, 'id', 'project_id', Task::TABLE)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
|
||||
foreach ($subtasks as &$subtask) {
|
||||
$subtask['status_name'] = $status_list[$subtask['status']];
|
||||
}
|
||||
|
||||
return $subtasks;
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all subtasks assigned to the user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $status List of status
|
||||
* @return integer
|
||||
*/
|
||||
public function countUserSubtasks($user_id, array $status)
|
||||
{
|
||||
return $this->db
|
||||
->table(SubTask::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->in('status', $status)
|
||||
->count();
|
||||
}
|
||||
}
|
|
@ -39,204 +39,6 @@ class Task extends Base
|
|||
const EVENT_CREATE_UPDATE = 'task.create_update';
|
||||
const EVENT_ASSIGNEE_CHANGE = 'task.assignee_change';
|
||||
|
||||
/**
|
||||
* Prepare data before task creation or modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
$this->dateParser->convert($values, array('date_due', 'date_started'));
|
||||
$this->removeFields($values, array('another_task', 'id'));
|
||||
$this->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent'));
|
||||
$this->convertIntegerFields($values, array('is_active'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before task creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepareCreation(array &$values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
|
||||
if (empty($values['column_id'])) {
|
||||
$values['column_id'] = $this->board->getFirstColumn($values['project_id']);
|
||||
}
|
||||
|
||||
if (empty($values['color_id'])) {
|
||||
$colors = $this->color->getList();
|
||||
$values['color_id'] = key($colors);
|
||||
}
|
||||
|
||||
$values['date_creation'] = time();
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['position'] = $this->taskFinder->countByColumnId($values['project_id'], $values['column_id']) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepareModification(array &$values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
$values['date_modification'] = time();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$this->prepareCreation($values);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$task_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
// Trigger events
|
||||
$this->event->trigger(self::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $values);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $values);
|
||||
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @param boolean $trigger_Events Trigger events
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values, $trigger_events = true)
|
||||
{
|
||||
// Fetch original task
|
||||
$original_task = $this->taskFinder->getById($values['id']);
|
||||
|
||||
if (! $original_task) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare data
|
||||
$updated_task = $values;
|
||||
$this->prepareModification($updated_task);
|
||||
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updated_task);
|
||||
|
||||
if ($result && $trigger_events) {
|
||||
$this->triggerUpdateEvents($original_task, $updated_task);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger events for task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $original_task Original task data
|
||||
* @param array $updated_task Updated task data
|
||||
*/
|
||||
public function triggerUpdateEvents(array $original_task, array $updated_task)
|
||||
{
|
||||
$events = array();
|
||||
|
||||
if (isset($updated_task['owner_id']) && $original_task['owner_id'] != $updated_task['owner_id']) {
|
||||
$events[] = self::EVENT_ASSIGNEE_CHANGE;
|
||||
}
|
||||
else if (isset($updated_task['column_id']) && $original_task['column_id'] != $updated_task['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($updated_task['position']) && $original_task['position'] != $updated_task['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
else {
|
||||
$events[] = self::EVENT_CREATE_UPDATE;
|
||||
$events[] = self::EVENT_UPDATE;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $updated_task);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task closed
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function close($task_id)
|
||||
{
|
||||
if (! $this->taskFinder->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'is_active' => 0,
|
||||
'date_completed' => time()
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(self::EVENT_CLOSE, array('task_id' => $task_id) + $this->taskFinder->getById($task_id));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task open
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function open($task_id)
|
||||
{
|
||||
if (! $this->taskFinder->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'is_active' => 1,
|
||||
'date_completed' => 0
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(self::EVENT_OPEN, array('task_id' => $task_id) + $this->taskFinder->getById($task_id));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a task
|
||||
*
|
||||
|
@ -255,228 +57,6 @@ class Task extends Base
|
|||
return $this->db->table(self::TABLE)->eq('id', $task_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a task to another column or to another position
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be >= 1)
|
||||
* @return boolean
|
||||
*/
|
||||
public function movePosition($project_id, $task_id, $column_id, $position)
|
||||
{
|
||||
// The position can't be lower than 1
|
||||
if ($position < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$board = $this->db->table(Board::TABLE)->eq('project_id', $project_id)->asc('position')->findAllByColumn('id');
|
||||
$columns = array();
|
||||
|
||||
// Prepare the columns
|
||||
foreach ($board as $board_column_id) {
|
||||
|
||||
$columns[$board_column_id] = $this->db->table(self::TABLE)
|
||||
->eq('is_active', 1)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $board_column_id)
|
||||
->neq('id', $task_id)
|
||||
->asc('position')
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
|
||||
// The column must exists
|
||||
if (! isset($columns[$column_id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We put our task to the new position
|
||||
array_splice($columns[$column_id], $position - 1, 0, $task_id); // print_r($columns);
|
||||
|
||||
// We save the new positions for all tasks
|
||||
return $this->savePositions($task_id, $columns);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save task positions
|
||||
*
|
||||
* @access private
|
||||
* @param integer $moved_task_id Id of the moved task
|
||||
* @param array $columns Sorted tasks
|
||||
* @return boolean
|
||||
*/
|
||||
private function savePositions($moved_task_id, array $columns)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($columns as $column_id => $column) {
|
||||
|
||||
$position = 1;
|
||||
|
||||
foreach ($column as $task_id) {
|
||||
|
||||
if ($task_id == $moved_task_id) {
|
||||
|
||||
// Events will be triggered only for that task
|
||||
$result = $this->update(array(
|
||||
'id' => $task_id,
|
||||
'position' => $position,
|
||||
'column_id' => $column_id
|
||||
));
|
||||
}
|
||||
else {
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $task_id)->update(array(
|
||||
'position' => $position,
|
||||
'column_id' => $column_id
|
||||
));
|
||||
}
|
||||
|
||||
$position++;
|
||||
|
||||
if (! $result) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a task to another project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $task Task data
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveToAnotherProject($project_id, array $task)
|
||||
{
|
||||
$values = array();
|
||||
|
||||
// Clear values (categories are different for each project)
|
||||
$values['category_id'] = 0;
|
||||
$values['owner_id'] = 0;
|
||||
|
||||
// Check if the assigned user is allowed for the new project
|
||||
if ($task['owner_id'] && $this->projectPermission->isUserAllowed($project_id, $task['owner_id'])) {
|
||||
$values['owner_id'] = $task['owner_id'];
|
||||
}
|
||||
|
||||
// We use the first column of the new project
|
||||
$values['column_id'] = $this->board->getFirstColumn($project_id);
|
||||
$values['position'] = $this->taskFinder->countByColumnId($project_id, $values['column_id']) + 1;
|
||||
$values['project_id'] = $project_id;
|
||||
|
||||
// The task will be open (close event binding)
|
||||
$values['is_active'] = 1;
|
||||
|
||||
if ($this->db->table(self::TABLE)->eq('id', $task['id'])->update($values)) {
|
||||
return $task['id'];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic method to duplicate a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $task Task data
|
||||
* @param array $override Task properties to override
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function copy(array $task, array $override = array())
|
||||
{
|
||||
// Values to override
|
||||
if (! empty($override)) {
|
||||
$task = $override + $task;
|
||||
}
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Assign new values
|
||||
$values = array();
|
||||
$values['title'] = $task['title'];
|
||||
$values['description'] = $task['description'];
|
||||
$values['date_creation'] = time();
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['date_due'] = $task['date_due'];
|
||||
$values['color_id'] = $task['color_id'];
|
||||
$values['project_id'] = $task['project_id'];
|
||||
$values['column_id'] = $task['column_id'];
|
||||
$values['owner_id'] = 0;
|
||||
$values['creator_id'] = $task['creator_id'];
|
||||
$values['position'] = $this->taskFinder->countByColumnId($values['project_id'], $values['column_id']) + 1;
|
||||
$values['score'] = $task['score'];
|
||||
$values['category_id'] = 0;
|
||||
|
||||
// Check if the assigned user is allowed for the new project
|
||||
if ($task['owner_id'] && $this->projectPermission->isUserAllowed($values['project_id'], $task['owner_id'])) {
|
||||
$values['owner_id'] = $task['owner_id'];
|
||||
}
|
||||
|
||||
// Check if the category exists
|
||||
if ($task['category_id'] && $this->category->exists($task['category_id'], $task['project_id'])) {
|
||||
$values['category_id'] = $task['category_id'];
|
||||
}
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(Task::TABLE)->save($values)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$task_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
// Duplicate subtasks
|
||||
if (! $this->subTask->duplicate($task['id'], $task_id)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
// Trigger events
|
||||
$this->event->trigger(Task::EVENT_CREATE_UPDATE, array('task_id' => $task_id) + $values);
|
||||
$this->event->trigger(Task::EVENT_CREATE, array('task_id' => $task_id) + $values);
|
||||
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to the same project
|
||||
*
|
||||
* @access public
|
||||
* @param array $task Task data
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function duplicateToSameProject($task)
|
||||
{
|
||||
return $this->copy($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project (always copy to the first column)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Destination project id
|
||||
* @param array $task Task data
|
||||
* @return integer|boolean
|
||||
*/
|
||||
public function duplicateToAnotherProject($project_id, array $task)
|
||||
{
|
||||
return $this->copy($task, array(
|
||||
'project_id' => $project_id,
|
||||
'column_id' => $this->board->getFirstColumn($project_id),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a the task id from a text
|
||||
*
|
||||
|
|
70
sources/app/Model/TaskCreation.php
Normal file
70
sources/app/Model/TaskCreation.php
Normal file
|
@ -0,0 +1,70 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Creation
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskCreation extends Base
|
||||
{
|
||||
/**
|
||||
* Create a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
$task_id = $this->persist(Task::TABLE, $values);
|
||||
|
||||
if ($task_id) {
|
||||
$this->fireEvents($task_id, $values);
|
||||
}
|
||||
|
||||
return (int) $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
$this->dateParser->convert($values, array('date_due', 'date_started'));
|
||||
$this->removeFields($values, array('another_task'));
|
||||
$this->resetFields($values, array('owner_id', 'owner_id', 'date_due', 'score', 'category_id', 'time_estimated'));
|
||||
|
||||
if (empty($values['column_id'])) {
|
||||
$values['column_id'] = $this->board->getFirstColumn($values['project_id']);
|
||||
}
|
||||
|
||||
if (empty($values['color_id'])) {
|
||||
$values['color_id'] = $this->color->getDefaultColor();
|
||||
}
|
||||
|
||||
$values['date_creation'] = time();
|
||||
$values['date_modification'] = $values['date_creation'];
|
||||
$values['position'] = $this->taskFinder->countByColumnId($values['project_id'], $values['column_id']) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire events
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id Task id
|
||||
* @param array $values Form values
|
||||
*/
|
||||
private function fireEvents($task_id, array $values)
|
||||
{
|
||||
$values['task_id'] = $task_id;
|
||||
$this->event->trigger(Task::EVENT_CREATE_UPDATE, $values);
|
||||
$this->event->trigger(Task::EVENT_CREATE, $values);
|
||||
}
|
||||
}
|
145
sources/app/Model/TaskDuplication.php
Normal file
145
sources/app/Model/TaskDuplication.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Duplication
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskDuplication extends Base
|
||||
{
|
||||
/**
|
||||
* Fields to copy when duplicating a task
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $fields_to_duplicate = array(
|
||||
'title',
|
||||
'description',
|
||||
'date_due',
|
||||
'color_id',
|
||||
'project_id',
|
||||
'column_id',
|
||||
'owner_id',
|
||||
'score',
|
||||
'category_id',
|
||||
'time_estimated',
|
||||
);
|
||||
|
||||
/**
|
||||
* Duplicate a task to the same project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean|integer Duplicated task id
|
||||
*/
|
||||
public function duplicate($task_id)
|
||||
{
|
||||
return $this->save($task_id, $this->copyFields($task_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $project_id Project id
|
||||
* @return boolean|integer Duplicated task id
|
||||
*/
|
||||
public function duplicateToProject($task_id, $project_id)
|
||||
{
|
||||
$values = $this->copyFields($task_id);
|
||||
$values['project_id'] = $project_id;
|
||||
$values['column_id'] = $this->board->getFirstColumn($project_id);
|
||||
|
||||
$this->checkDestinationProjectValues($values);
|
||||
|
||||
return $this->save($task_id, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a task to another project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $project_id Project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveToProject($task_id, $project_id)
|
||||
{
|
||||
$task = $this->taskFinder->getById($task_id);
|
||||
|
||||
$values = array();
|
||||
$values['is_active'] = 1;
|
||||
$values['project_id'] = $project_id;
|
||||
$values['column_id'] = $this->board->getFirstColumn($project_id);
|
||||
$values['position'] = $this->taskFinder->countByColumnId($project_id, $values['column_id']) + 1;
|
||||
$values['owner_id'] = $task['owner_id'];
|
||||
$values['category_id'] = $task['category_id'];
|
||||
|
||||
$this->checkDestinationProjectValues($values);
|
||||
|
||||
return $this->db->table(Task::TABLE)->eq('id', $task['id'])->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the assignee and the category are available in the destination project
|
||||
*
|
||||
* @access private
|
||||
* @param array $values
|
||||
*/
|
||||
private function checkDestinationProjectValues(&$values)
|
||||
{
|
||||
// Check if the assigned user is allowed for the destination project
|
||||
if ($values['owner_id'] > 0 && ! $this->projectPermission->isUserAllowed($values['project_id'], $values['owner_id'])) {
|
||||
$values['owner_id'] = 0;
|
||||
}
|
||||
|
||||
// Check if the category exists for the destination project
|
||||
if ($values['category_id'] > 0) {
|
||||
$category_name = $this->category->getNameById($values['category_id']);
|
||||
$values['category_id'] = $this->category->getIdByName($values['project_id'], $category_name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate fields for the new task
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
private function copyFields($task_id)
|
||||
{
|
||||
$task = $this->taskFinder->getById($task_id);
|
||||
$values = array();
|
||||
|
||||
foreach ($this->fields_to_duplicate as $field) {
|
||||
$values[$field] = $task[$field];
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the new task and duplicate subtasks
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id Task id
|
||||
* @param array $values Form values
|
||||
* @return boolean|integer
|
||||
*/
|
||||
private function save($task_id, array $values)
|
||||
{
|
||||
$new_task_id = $this->taskCreation->create($values);
|
||||
|
||||
if ($new_task_id) {
|
||||
$this->subTask->duplicate($task_id, $new_task_id);
|
||||
}
|
||||
|
||||
return $new_task_id;
|
||||
}
|
||||
}
|
|
@ -15,10 +15,10 @@ class TaskFinder extends Base
|
|||
/**
|
||||
* Common request to fetch a list of tasks
|
||||
*
|
||||
* @access private
|
||||
* @access public
|
||||
* @return \PicoDb\Table
|
||||
*/
|
||||
private function prepareRequestList()
|
||||
public function getQuery()
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
|
@ -50,51 +50,6 @@ class TaskFinder extends Base
|
|||
->join(User::TABLE, 'id', 'owner_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Task search with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function search($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC')
|
||||
{
|
||||
return $this->prepareRequestList()
|
||||
->eq('project_id', $project_id)
|
||||
->like('title', '%'.$search.'%')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all completed tasks with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function getClosedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC')
|
||||
{
|
||||
return $this->prepareRequestList()
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_CLOSED)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tasks shown on the board (sorted by position)
|
||||
*
|
||||
|
@ -104,40 +59,13 @@ class TaskFinder extends Base
|
|||
*/
|
||||
public function getTasksOnBoard($project_id)
|
||||
{
|
||||
return $this->prepareRequestList()
|
||||
return $this->getQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_OPEN)
|
||||
->asc('tasks.position')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all open tasks for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllTasksByUser($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->columns(
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.date_due',
|
||||
'tasks.date_creation',
|
||||
'tasks.project_id',
|
||||
'tasks.color_id',
|
||||
'projects.name AS project_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->eq('tasks.owner_id', $user_id)
|
||||
->eq('tasks.is_active', Task::STATUS_OPEN)
|
||||
->asc('tasks.id')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tasks for a given project and status
|
||||
*
|
||||
|
@ -295,22 +223,6 @@ class TaskFinder extends Base
|
|||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of tasks for a custom search
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @return integer
|
||||
*/
|
||||
public function countSearch($project_id, $search)
|
||||
{
|
||||
return $this->db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->like('title', '%'.$search.'%')
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the task exists
|
||||
*
|
||||
|
|
73
sources/app/Model/TaskModification.php
Normal file
73
sources/app/Model/TaskModification.php
Normal file
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Modification
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskModification extends Base
|
||||
{
|
||||
/**
|
||||
* Update a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values
|
||||
* @param boolean $fire_events
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values, $fire_events = true)
|
||||
{
|
||||
$original_task = $this->taskFinder->getById($values['id']);
|
||||
|
||||
$this->prepare($values);
|
||||
$result = $this->db->table(Task::TABLE)->eq('id', $original_task['id'])->update($values);
|
||||
|
||||
if ($result && $fire_events) {
|
||||
$this->fireEvents($original_task, $values);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire events
|
||||
*
|
||||
* @access public
|
||||
* @param array $task
|
||||
* @param array $new_values
|
||||
*/
|
||||
public function fireEvents(array $task, array $new_values)
|
||||
{
|
||||
$event_data = array_merge($task, $new_values, array('task_id' => $task['id']));
|
||||
|
||||
if (isset($new_values['owner_id']) && $task['owner_id'] != $new_values['owner_id']) {
|
||||
$events = array(Task::EVENT_ASSIGNEE_CHANGE);
|
||||
}
|
||||
else {
|
||||
$events = array(Task::EVENT_CREATE_UPDATE, Task::EVENT_UPDATE);
|
||||
}
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare data before task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
*/
|
||||
public function prepare(array &$values)
|
||||
{
|
||||
$this->dateParser->convert($values, array('date_due', 'date_started'));
|
||||
$this->removeFields($values, array('another_task', 'id'));
|
||||
$this->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent'));
|
||||
$this->convertIntegerFields($values, array('is_active'));
|
||||
|
||||
$values['date_modification'] = time();
|
||||
}
|
||||
}
|
139
sources/app/Model/TaskPaginator.php
Normal file
139
sources/app/Model/TaskPaginator.php
Normal file
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Paginator model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskPaginator extends Base
|
||||
{
|
||||
/**
|
||||
* Task search with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function searchTasks($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC')
|
||||
{
|
||||
return $this->taskFinder->getQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->like('title', '%'.$search.'%')
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of tasks for a custom search
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param string $search Search terms
|
||||
* @return integer
|
||||
*/
|
||||
public function countSearchTasks($project_id, $search)
|
||||
{
|
||||
return $this->db->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->like('title', '%'.$search.'%')
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all completed tasks with pagination
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function closedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC')
|
||||
{
|
||||
return $this->taskFinder->getQuery()
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_CLOSED)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all closed tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $status List of status id
|
||||
* @return integer
|
||||
*/
|
||||
public function countClosedTasks($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('is_active', Task::STATUS_CLOSED)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all open tasks for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param integer $offset Offset
|
||||
* @param integer $limit Limit
|
||||
* @param string $column Sorting column
|
||||
* @param string $direction Sorting direction
|
||||
* @return array
|
||||
*/
|
||||
public function userTasks($user_id, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'ASC')
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->columns(
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.date_due',
|
||||
'tasks.date_creation',
|
||||
'tasks.project_id',
|
||||
'tasks.color_id',
|
||||
'projects.name AS project_name'
|
||||
)
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->eq('tasks.owner_id', $user_id)
|
||||
->eq('tasks.is_active', Task::STATUS_OPEN)
|
||||
->offset($offset)
|
||||
->limit($limit)
|
||||
->orderBy($column, $direction)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all tasks assigned to the user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return integer
|
||||
*/
|
||||
public function countUserTasks($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('owner_id', $user_id)
|
||||
->eq('is_active', Task::STATUS_OPEN)
|
||||
->count();
|
||||
}
|
||||
}
|
136
sources/app/Model/TaskPosition.php
Normal file
136
sources/app/Model/TaskPosition.php
Normal file
|
@ -0,0 +1,136 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Position
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskPosition extends Base
|
||||
{
|
||||
/**
|
||||
* Move a task to another column or to another position
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be >= 1)
|
||||
* @return boolean
|
||||
*/
|
||||
public function movePosition($project_id, $task_id, $column_id, $position)
|
||||
{
|
||||
$original_task = $this->taskFinder->getById($task_id);
|
||||
$positions = $this->calculatePositions($project_id, $task_id, $column_id, $position);
|
||||
|
||||
if ($positions === false || ! $this->savePositions($positions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->fireEvents($original_task, $column_id, $position);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the new position of all tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be >= 1)
|
||||
* @return array|boolean
|
||||
*/
|
||||
public function calculatePositions($project_id, $task_id, $column_id, $position)
|
||||
{
|
||||
// The position can't be lower than 1
|
||||
if ($position < 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$board = $this->db->table(Board::TABLE)->eq('project_id', $project_id)->asc('position')->findAllByColumn('id');
|
||||
$columns = array();
|
||||
|
||||
// For each column fetch all tasks ordered by position
|
||||
foreach ($board as $board_column_id) {
|
||||
|
||||
$columns[$board_column_id] = $this->db->table(Task::TABLE)
|
||||
->eq('is_active', 1)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $board_column_id)
|
||||
->neq('id', $task_id)
|
||||
->asc('position')
|
||||
->findAllByColumn('id');
|
||||
}
|
||||
|
||||
// The column must exists
|
||||
if (! isset($columns[$column_id])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We put our task to the new position
|
||||
array_splice($columns[$column_id], $position - 1, 0, $task_id);
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save task positions
|
||||
*
|
||||
* @access private
|
||||
* @param array $columns Sorted tasks
|
||||
* @return boolean
|
||||
*/
|
||||
private function savePositions(array $columns)
|
||||
{
|
||||
return $this->db->transaction(function ($db) use ($columns) {
|
||||
|
||||
foreach ($columns as $column_id => $column) {
|
||||
|
||||
$position = 1;
|
||||
|
||||
foreach ($column as $task_id) {
|
||||
|
||||
$result = $db->table(Task::TABLE)->eq('id', $task_id)->update(array(
|
||||
'position' => $position,
|
||||
'column_id' => $column_id
|
||||
));
|
||||
|
||||
if (! $result) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$position++;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Fire events
|
||||
*
|
||||
* @access public
|
||||
* @param array $task
|
||||
* @param integer $new_column_id
|
||||
* @param integer $new_position
|
||||
*/
|
||||
public function fireEvents(array $task, $new_column_id, $new_position)
|
||||
{
|
||||
$event_data = array(
|
||||
'task_id' => $task['id'],
|
||||
'project_id' => $task['project_id'],
|
||||
'position' => $new_position,
|
||||
'column_id' => $new_column_id,
|
||||
);
|
||||
|
||||
if ($task['column_id'] != $new_column_id) {
|
||||
$this->event->trigger(Task::EVENT_MOVE_COLUMN, $event_data);
|
||||
}
|
||||
else if ($task['position'] != $new_position) {
|
||||
$this->event->trigger(Task::EVENT_MOVE_POSITION, $event_data);
|
||||
}
|
||||
}
|
||||
}
|
112
sources/app/Model/TaskStatus.php
Normal file
112
sources/app/Model/TaskStatus.php
Normal file
|
@ -0,0 +1,112 @@
|
|||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Task Status
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskStatus extends Base
|
||||
{
|
||||
/**
|
||||
* Return true if the task is closed
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function isClosed($task_id)
|
||||
{
|
||||
return $this->checkStatus($task_id, Task::STATUS_CLOSED);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the task is open
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function isOpen($task_id)
|
||||
{
|
||||
return $this->checkStatus($task_id, Task::STATUS_OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task closed
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function close($task_id)
|
||||
{
|
||||
return $this->changeStatus($task_id, Task::STATUS_CLOSED, time(), Task::EVENT_CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task open
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function open($task_id)
|
||||
{
|
||||
return $this->changeStatus($task_id, Task::STATUS_OPEN, 0, Task::EVENT_OPEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Common method to change the status of task
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $status Task status
|
||||
* @param integer $date_completed Timestamp
|
||||
* @param string $event Event name
|
||||
* @return boolean
|
||||
*/
|
||||
private function changeStatus($task_id, $status, $date_completed, $event)
|
||||
{
|
||||
if (! $this->taskFinder->exists($task_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$result = $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'is_active' => $status,
|
||||
'date_completed' => $date_completed,
|
||||
'date_modification' => time(),
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(
|
||||
$event,
|
||||
array('task_id' => $task_id) + $this->taskFinder->getById($task_id)
|
||||
);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the status of task
|
||||
*
|
||||
* @access private
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $status Task status
|
||||
* @return boolean
|
||||
*/
|
||||
private function checkStatus($task_id, $status)
|
||||
{
|
||||
return $this->db
|
||||
->table(Task::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->eq('is_active', $status)
|
||||
->count() === 1;
|
||||
}
|
||||
}
|
|
@ -70,7 +70,6 @@ class TaskValidator extends Base
|
|||
{
|
||||
$rules = array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Required('description', t('The description is required')),
|
||||
);
|
||||
|
||||
$v = new Validator($values, array_merge($rules, $this->commonValidationRules()));
|
||||
|
|
|
@ -255,12 +255,12 @@ class User extends Base
|
|||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
* @return boolean|integer
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->prepare($values);
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
return $this->persist(self::TABLE, $values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -292,15 +292,29 @@ class User extends Base
|
|||
*/
|
||||
public function remove($user_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
return $this->db->transaction(function ($db) use ($user_id) {
|
||||
|
||||
// All tasks assigned to this user will be unassigned
|
||||
$this->db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0));
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $user_id)->remove();
|
||||
// All assigned tasks are now unassigned
|
||||
if (! $db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
// All private projects are removed
|
||||
$project_ids = $db->table(Project::TABLE)
|
||||
->eq('is_private', 1)
|
||||
->eq(ProjectPermission::TABLE.'.user_id', $user_id)
|
||||
->join(ProjectPermission::TABLE, 'project_id', 'id')
|
||||
->findAllByColumn(Project::TABLE.'.id');
|
||||
|
||||
return $result;
|
||||
if (! empty($project_ids)) {
|
||||
$db->table(Project::TABLE)->in('id', $project_ids)->remove();
|
||||
}
|
||||
|
||||
// Finally remove the user
|
||||
if (! $db->table(User::TABLE)->eq('id', $user_id)->remove()) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,7 +93,7 @@ class Webhook extends Base
|
|||
Task::EVENT_ASSIGNEE_CHANGE,
|
||||
);
|
||||
|
||||
$listener = new WebhookListener($this->registry);
|
||||
$listener = new WebhookListener($this->container);
|
||||
$listener->setUrl($this->url_task_modification);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
|
@ -108,7 +108,7 @@ class Webhook extends Base
|
|||
*/
|
||||
public function attachCreateEvents()
|
||||
{
|
||||
$listener = new WebhookListener($this->registry);
|
||||
$listener = new WebhookListener($this->container);
|
||||
$listener->setUrl($this->url_task_creation);
|
||||
|
||||
$this->event->attach(Task::EVENT_CREATE, $listener);
|
||||
|
|
|
@ -5,7 +5,30 @@ namespace Schema;
|
|||
use PDO;
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 34;
|
||||
const VERSION = 36;
|
||||
|
||||
function version_36($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks MODIFY title VARCHAR(255) NOT NULL');
|
||||
}
|
||||
|
||||
function version_35($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_daily_summaries (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
day CHAR(10) NOT NULL,
|
||||
project_id INT NOT NULL,
|
||||
column_id INT NOT NULL,
|
||||
total INT NOT NULL DEFAULT 0,
|
||||
PRIMARY KEY(id),
|
||||
FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)');
|
||||
}
|
||||
|
||||
function version_34($pdo)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,29 @@ namespace Schema;
|
|||
use PDO;
|
||||
use Core\Security;
|
||||
|
||||
const VERSION = 15;
|
||||
const VERSION = 17;
|
||||
|
||||
function version_17($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks ALTER COLUMN title SET NOT NULL');
|
||||
}
|
||||
|
||||
function version_16($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_daily_summaries (
|
||||
id SERIAL PRIMARY KEY,
|
||||
day CHAR(10) NOT NULL,
|
||||
project_id INTEGER NOT NULL,
|
||||
column_id INTEGER NOT NULL,
|
||||
total INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)');
|
||||
}
|
||||
|
||||
function version_15($pdo)
|
||||
{
|
||||
|
|
|
@ -5,7 +5,24 @@ namespace Schema;
|
|||
use Core\Security;
|
||||
use PDO;
|
||||
|
||||
const VERSION = 34;
|
||||
const VERSION = 35;
|
||||
|
||||
function version_35($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_daily_summaries (
|
||||
id INTEGER PRIMARY KEY,
|
||||
day TEXT NOT NULL,
|
||||
project_id INTEGER NOT NULL,
|
||||
column_id INTEGER NOT NULL,
|
||||
total INTEGER NOT NULL DEFAULT 0,
|
||||
FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec('CREATE UNIQUE INDEX project_daily_column_stats_idx ON project_daily_summaries(day, project_id, column_id)');
|
||||
}
|
||||
|
||||
function version_34($pdo)
|
||||
{
|
||||
|
@ -440,7 +457,7 @@ function version_1($pdo)
|
|||
$pdo->exec("
|
||||
CREATE TABLE tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT,
|
||||
title TEXT NOT NULL,
|
||||
description TEXT,
|
||||
date_creation INTEGER,
|
||||
color_id TEXT,
|
||||
|
|
100
sources/app/ServiceProvider/Database.php
Normal file
100
sources/app/ServiceProvider/Database.php
Normal file
|
@ -0,0 +1,100 @@
|
|||
<?php
|
||||
|
||||
namespace ServiceProvider;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use PicoDb\Database as Dbal;
|
||||
|
||||
class Database implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['db'] = $this->getInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the database driver and execute schema migration
|
||||
*
|
||||
* @return PicoDb\Database
|
||||
*/
|
||||
public function getInstance()
|
||||
{
|
||||
switch (DB_DRIVER) {
|
||||
case 'sqlite':
|
||||
$db = $this->getSqliteInstance();
|
||||
break;
|
||||
|
||||
case 'mysql':
|
||||
$db = $this->getMysqlInstance();
|
||||
break;
|
||||
|
||||
case 'postgres':
|
||||
$db = $this->getPostgresInstance();
|
||||
break;
|
||||
|
||||
default:
|
||||
die('Database driver not supported');
|
||||
}
|
||||
|
||||
if ($db->schema()->check(\Schema\VERSION)) {
|
||||
return $db;
|
||||
}
|
||||
else {
|
||||
$errors = $db->getLogMessages();
|
||||
die('Unable to migrate database schema: <br/><br/><strong>'.(isset($errors[0]) ? $errors[0] : 'Unknown error').'</strong>');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the Sqlite database driver
|
||||
*
|
||||
* @return PicoDb\Database
|
||||
*/
|
||||
function getSqliteInstance()
|
||||
{
|
||||
require_once __DIR__.'/../Schema/Sqlite.php';
|
||||
|
||||
return new Dbal(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => DB_FILENAME
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the Mysql database driver
|
||||
*
|
||||
* @return PicoDb\Database
|
||||
*/
|
||||
function getMysqlInstance()
|
||||
{
|
||||
require_once __DIR__.'/../Schema/Mysql.php';
|
||||
|
||||
return new Dbal(array(
|
||||
'driver' => 'mysql',
|
||||
'hostname' => DB_HOSTNAME,
|
||||
'username' => DB_USERNAME,
|
||||
'password' => DB_PASSWORD,
|
||||
'database' => DB_NAME,
|
||||
'charset' => 'utf8',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup the Postgres database driver
|
||||
*
|
||||
* @return PicoDb\Database
|
||||
*/
|
||||
public function getPostgresInstance()
|
||||
{
|
||||
require_once __DIR__.'/../Schema/Postgres.php';
|
||||
|
||||
return new Dbal(array(
|
||||
'driver' => 'postgres',
|
||||
'hostname' => DB_HOSTNAME,
|
||||
'username' => DB_USERNAME,
|
||||
'password' => DB_PASSWORD,
|
||||
'database' => DB_NAME,
|
||||
));
|
||||
}
|
||||
}
|
15
sources/app/ServiceProvider/Event.php
Normal file
15
sources/app/ServiceProvider/Event.php
Normal file
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
namespace ServiceProvider;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Core\Event as EventDispatcher;
|
||||
|
||||
class Event implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['event'] = new EventDispatcher;
|
||||
}
|
||||
}
|
21
sources/app/ServiceProvider/Logging.php
Normal file
21
sources/app/ServiceProvider/Logging.php
Normal file
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
namespace ServiceProvider;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Handler\SyslogHandler;
|
||||
|
||||
class Logging implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$logger = new Logger('app');
|
||||
$logger->pushHandler(new StreamHandler(__DIR__.'/../../data/debug.log', Logger::DEBUG));
|
||||
$logger->pushHandler(new SyslogHandler('kanboard', LOG_USER, Logger::DEBUG));
|
||||
|
||||
$container['logger'] = $logger;
|
||||
}
|
||||
}
|
36
sources/app/ServiceProvider/Mailer.php
Normal file
36
sources/app/ServiceProvider/Mailer.php
Normal file
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace ServiceProvider;
|
||||
|
||||
use Pimple\Container;
|
||||
use Pimple\ServiceProviderInterface;
|
||||
use Swift_SmtpTransport;
|
||||
use Swift_SendmailTransport;
|
||||
use Swift_MailTransport;
|
||||
|
||||
class Mailer implements ServiceProviderInterface
|
||||
{
|
||||
public function register(Container $container)
|
||||
{
|
||||
$container['mailer'] = $this->getInstance();
|
||||
}
|
||||
|
||||
public function getInstance()
|
||||
{
|
||||
switch (MAIL_TRANSPORT) {
|
||||
case 'smtp':
|
||||
$transport = Swift_SmtpTransport::newInstance(MAIL_SMTP_HOSTNAME, MAIL_SMTP_PORT);
|
||||
$transport->setUsername(MAIL_SMTP_USERNAME);
|
||||
$transport->setPassword(MAIL_SMTP_PASSWORD);
|
||||
$transport->setEncryption(MAIL_SMTP_ENCRYPTION);
|
||||
break;
|
||||
case 'sendmail':
|
||||
$transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND);
|
||||
break;
|
||||
default:
|
||||
$transport = Swift_MailTransport::newInstance();
|
||||
}
|
||||
|
||||
return $transport;
|
||||
}
|
||||
}
|
|
@ -3,8 +3,10 @@
|
|||
</div>
|
||||
|
||||
<h3><?= t('Choose an event') ?></h3>
|
||||
<form method="post" action="?controller=action&action=params&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
<form method="post" action="<?= Helper\u('action', 'params', array('project_id' => $project['id'])) ?>">
|
||||
|
||||
<?= Helper\form_csrf() ?>
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_hidden('action_name', $values) ?>
|
||||
|
||||
|
@ -17,6 +19,7 @@
|
|||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
|
||||
<?= t('or') ?>
|
||||
<?= Helper\a(t('cancel'), 'action', 'index', array('project_id' => $project['id'])) ?>
|
||||
</div>
|
||||
</form>
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue