Code refactoring (add autoloader and change files organization)
This commit is contained in:
1
app/.htaccess
Normal file
1
app/.htaccess
Normal file
@@ -0,0 +1 @@
|
||||
Deny from all
|
||||
142
app/Action/Base.php
Normal file
142
app/Action/Base.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Core\Listener;
|
||||
|
||||
/**
|
||||
* Base class for automatic actions
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Base implements Listener
|
||||
{
|
||||
/**
|
||||
* Project id
|
||||
*
|
||||
* @access private
|
||||
* @var integer
|
||||
*/
|
||||
private $project_id = 0;
|
||||
|
||||
/**
|
||||
* User parameters
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $params = array();
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
abstract public function doAction(array $data);
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getActionRequiredParameters();
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event (check if for the event data)
|
||||
*
|
||||
* @abstract
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
abstract public function getEventRequiredParameters();
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
*/
|
||||
public function __construct($project_id)
|
||||
{
|
||||
$this->project_id = $project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set an user defined parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param mixed $value Value
|
||||
*/
|
||||
public function setParam($name, $value)
|
||||
{
|
||||
$this->params[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an user defined parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param mixed $default_value Default value
|
||||
* @return mixed
|
||||
*/
|
||||
public function getParam($name, $default_value = null)
|
||||
{
|
||||
return isset($this->params[$name]) ? $this->params[$name] : $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action is executable (right project and required parameters)
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action is executable
|
||||
*/
|
||||
public function isExecutable(array $data)
|
||||
{
|
||||
if (isset($data['project_id']) && $data['project_id'] == $this->project_id && $this->hasRequiredParameters($data)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the event data has required parameters to execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if all keys are there
|
||||
*/
|
||||
public function hasRequiredParameters(array $data)
|
||||
{
|
||||
foreach ($this->getEventRequiredParameters() as $parameter) {
|
||||
if (! isset($data[$parameter])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ($this->isExecutable($data)) {
|
||||
return $this->doAction($data);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
85
app/Action/TaskAssignColorCategory.php
Normal file
85
app/Action/TaskAssignColorCategory.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Assign a color to a specific category
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorCategory extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'category_id' => t('Category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'category_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['category_id'] == $this->getParam('category_id')) {
|
||||
|
||||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
85
app/Action/TaskAssignColorUser.php
Normal file
85
app/Action/TaskAssignColorUser.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Assign a color to a specific user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignColorUser extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'color_id' => t('Color'),
|
||||
'user_id' => t('Assignee'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'owner_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['owner_id'] == $this->getParam('user_id')) {
|
||||
|
||||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'color_id' => $this->getParam('color_id'),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
95
app/Action/TaskAssignCurrentUser.php
Normal file
95
app/Action/TaskAssignCurrentUser.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
use Model\Acl;
|
||||
|
||||
/**
|
||||
* Assign a task to the logged user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignCurrentUser extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Acl model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Acl
|
||||
*/
|
||||
private $acl;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
* @param \Model\Acl $acl Acl model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task, Acl $acl)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
$this->acl = $acl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id')) {
|
||||
|
||||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->acl->getUserId(),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
85
app/Action/TaskAssignSpecificUser.php
Normal file
85
app/Action/TaskAssignSpecificUser.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Assign a task to a specific user
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskAssignSpecificUser extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'user_id' => t('Assignee'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id')) {
|
||||
|
||||
$this->task->update(array(
|
||||
'id' => $data['task_id'],
|
||||
'owner_id' => $this->getParam('user_id'),
|
||||
));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
79
app/Action/TaskClose.php
Normal file
79
app/Action/TaskClose.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Close automatically a task
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskClose extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id')) {
|
||||
$this->task->close($data['task_id']);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
83
app/Action/TaskDuplicateAnotherProject.php
Normal file
83
app/Action/TaskDuplicateAnotherProject.php
Normal file
@@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
namespace Action;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project
|
||||
*
|
||||
* @package action
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskDuplicateAnotherProject extends Base
|
||||
{
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Task
|
||||
*/
|
||||
private $task;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param \Model\Task $task Task model instance
|
||||
*/
|
||||
public function __construct($project_id, Task $task)
|
||||
{
|
||||
parent::__construct($project_id);
|
||||
$this->task = $task;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the action (defined by the user)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getActionRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'column_id' => t('Column'),
|
||||
'project_id' => t('Project'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the required parameter for the event
|
||||
*
|
||||
* @access public
|
||||
* @return string[]
|
||||
*/
|
||||
public function getEventRequiredParameters()
|
||||
{
|
||||
return array(
|
||||
'task_id',
|
||||
'column_id',
|
||||
'project_id',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param array $data Event data dictionary
|
||||
* @return bool True if the action was executed or false when not executed
|
||||
*/
|
||||
public function doAction(array $data)
|
||||
{
|
||||
if ($data['column_id'] == $this->getParam('column_id') && $data['project_id'] != $this->getParam('project_id')) {
|
||||
|
||||
$this->task->duplicateToAnotherProject($data['task_id'], $this->getParam('project_id'));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
142
app/Controller/Action.php
Normal file
142
app/Controller/Action.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Automatic actions management
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Action extends Base
|
||||
{
|
||||
/**
|
||||
* List of automatic actions for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('action_index', array(
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'project' => $project,
|
||||
'actions' => $this->action->getAllByProject($project['id']),
|
||||
'available_actions' => $this->action->getAvailableActions(),
|
||||
'available_events' => $this->action->getAvailableEvents(),
|
||||
'available_params' => $this->action->getAllActionParameters(),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'users_list' => $this->project->getUsersList($project['id'], false),
|
||||
'projects_list' => $this->project->getList(false),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($project['id'], false),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Define action parameters (step 2)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function params()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$values = $this->request->getValues();
|
||||
$action = $this->action->load($values['action_name'], $values['project_id']);
|
||||
|
||||
$this->response->html($this->template->layout('action_params', array(
|
||||
'values' => $values,
|
||||
'action_params' => $action->getActionRequiredParameters(),
|
||||
'columns_list' => $this->board->getColumnsList($project['id']),
|
||||
'users_list' => $this->project->getUsersList($project['id'], false),
|
||||
'projects_list' => $this->project->getList(false),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($project['id'], false),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Automatic actions')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new action (last step)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$values = $this->request->getValues();
|
||||
|
||||
list($valid, $errors) = $this->action->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->action->create($values)) {
|
||||
$this->session->flash(t('Your automatic action have been created successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your automatic action.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=action&action=index&project_id='.$project['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing an action
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$this->response->html($this->template->layout('action_remove', array(
|
||||
'action' => $this->action->getById($this->request->getIntegerParam('action_id')),
|
||||
'available_events' => $this->action->getAvailableEvents(),
|
||||
'available_actions' => $this->action->getAvailableActions(),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove an action')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an action
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$action = $this->action->getById($this->request->getIntegerParam('action_id'));
|
||||
|
||||
if ($action && $this->action->remove($action['id'])) {
|
||||
$this->session->flash(t('Action removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this action.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=action&action=index&project_id='.$action['project_id']);
|
||||
}
|
||||
}
|
||||
29
app/Controller/App.php
Normal file
29
app/Controller/App.php
Normal file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Project;
|
||||
|
||||
/**
|
||||
* Application controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class App extends Base
|
||||
{
|
||||
/**
|
||||
* Redirect to the project creation page or the board controller
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
if ($this->project->countByStatus(Project::ACTIVE)) {
|
||||
$this->response->redirect('?controller=board');
|
||||
}
|
||||
else {
|
||||
$this->redirectNoProject();
|
||||
}
|
||||
}
|
||||
}
|
||||
238
app/Controller/Base.php
Normal file
238
app/Controller/Base.php
Normal file
@@ -0,0 +1,238 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Core\Registry;
|
||||
use Core\Translator;
|
||||
use Model\LastLogin;
|
||||
|
||||
/**
|
||||
* Base controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
/**
|
||||
* Request instance
|
||||
*
|
||||
* @accesss public
|
||||
* @var \Core\Request
|
||||
*/
|
||||
public $request;
|
||||
|
||||
/**
|
||||
* Response instance
|
||||
*
|
||||
* @accesss public
|
||||
* @var \Core\Response
|
||||
*/
|
||||
public $response;
|
||||
|
||||
/**
|
||||
* Template instance
|
||||
*
|
||||
* @accesss public
|
||||
* @var \Core\Template
|
||||
*/
|
||||
public $template;
|
||||
|
||||
/**
|
||||
* Session instance
|
||||
*
|
||||
* @accesss public
|
||||
* @var \Core\Session
|
||||
*/
|
||||
public $session;
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
*
|
||||
* @access private
|
||||
* @var Core\Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Core\Registry $registry Registry instance
|
||||
*/
|
||||
public function __construct(Registry $registry)
|
||||
{
|
||||
$this->registry = $registry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load automatically models
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Model name
|
||||
*/
|
||||
public function __get($name)
|
||||
{
|
||||
$class = '\Model\\'.ucfirst($name);
|
||||
$this->registry->$name = new $class($this->registry->shared('db'), $this->registry->shared('event'));
|
||||
return $this->registry->shared($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method executed before each action
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function beforeAction($controller, $action)
|
||||
{
|
||||
// Start the session
|
||||
$this->session->open(BASE_URL_DIRECTORY, SESSION_SAVE_PATH);
|
||||
|
||||
// HTTP secure headers
|
||||
$this->response->csp();
|
||||
$this->response->nosniff();
|
||||
$this->response->xss();
|
||||
$this->response->hsts();
|
||||
$this->response->xframe();
|
||||
|
||||
// Load translations
|
||||
$language = $this->config->get('language', 'en_US');
|
||||
if ($language !== 'en_US') Translator::load($language);
|
||||
|
||||
// Set timezone
|
||||
date_default_timezone_set($this->config->get('timezone', 'UTC'));
|
||||
|
||||
// Authentication
|
||||
if (! $this->acl->isLogged() && ! $this->acl->isPublicAction($controller, $action)) {
|
||||
|
||||
// Try the remember me authentication first
|
||||
if (! $this->rememberMe->authenticate()) {
|
||||
|
||||
// Redirect to the login form if not authenticated
|
||||
$this->response->redirect('?controller=user&action=login');
|
||||
}
|
||||
else {
|
||||
|
||||
$this->lastLogin->create(
|
||||
LastLogin::AUTH_REMEMBER_ME,
|
||||
$this->acl->getUserId(),
|
||||
$this->user->getIpAddress(),
|
||||
$this->user->getUserAgent()
|
||||
);
|
||||
}
|
||||
}
|
||||
else if ($this->rememberMe->hasCookie()) {
|
||||
$this->rememberMe->refresh();
|
||||
}
|
||||
|
||||
// Check if the user is allowed to see this page
|
||||
if (! $this->acl->isPageAccessAllowed($controller, $action)) {
|
||||
$this->response->redirect('?controller=user&action=forbidden');
|
||||
}
|
||||
|
||||
// Attach events
|
||||
$this->action->attachEvents();
|
||||
$this->project->attachEvents();
|
||||
}
|
||||
|
||||
/**
|
||||
* Application not found page (404 error)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function notfound()
|
||||
{
|
||||
$this->response->html($this->template->layout('app_notfound', array('title' => t('Page not found'))));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the current user have access to the given project
|
||||
*
|
||||
* @access protected
|
||||
* @param integer $project_id Project id
|
||||
*/
|
||||
protected function checkProjectPermissions($project_id)
|
||||
{
|
||||
if ($this->acl->isRegularUser()) {
|
||||
|
||||
if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->redirect('?controller=project&action=forbidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirection when there is no project in the database
|
||||
*
|
||||
* @access protected
|
||||
*/
|
||||
protected function redirectNoProject()
|
||||
{
|
||||
$this->session->flash(t('There is no active project, the first step is to create a new project.'));
|
||||
$this->response->redirect('?controller=project&action=create');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the template show task (common between different actions)
|
||||
*
|
||||
* @access protected
|
||||
* @param array $task Task data
|
||||
* @param array $comment_form Comment form data
|
||||
* @param array $description_form Description form data
|
||||
* @param array $comment_edit_form Comment edit form data
|
||||
*/
|
||||
protected function showTask(array $task, array $comment_form = array(), array $description_form = array(), array $comment_edit_form = array())
|
||||
{
|
||||
if (empty($comment_form)) {
|
||||
$comment_form = array(
|
||||
'values' => array('task_id' => $task['id'], 'user_id' => $this->acl->getUserId()),
|
||||
'errors' => array()
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($description_form)) {
|
||||
$description_form = array(
|
||||
'values' => array('id' => $task['id']),
|
||||
'errors' => array()
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($comment_edit_form)) {
|
||||
$comment_edit_form = array(
|
||||
'values' => array('id' => 0),
|
||||
'errors' => array()
|
||||
);
|
||||
}
|
||||
else {
|
||||
$hide_comment_form = true;
|
||||
}
|
||||
|
||||
$this->response->html($this->taskLayout('task_show', array(
|
||||
'hide_comment_form' => isset($hide_comment_form),
|
||||
'comment_edit_form' => $comment_edit_form,
|
||||
'comment_form' => $comment_form,
|
||||
'description_form' => $description_form,
|
||||
'comments' => $this->comment->getAll($task['id']),
|
||||
'task' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'menu' => 'tasks',
|
||||
'title' => $task['title'],
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Common layout for task views
|
||||
*
|
||||
* @access protected
|
||||
* @param string $template Template name
|
||||
* @param array $params Template parameters
|
||||
*/
|
||||
protected function taskLayout($template, array $params)
|
||||
{
|
||||
$content = $this->template->load($template, $params);
|
||||
$params['task_content_for_layout'] = $content;
|
||||
|
||||
return $this->template->layout('task_layout', $params);
|
||||
}
|
||||
}
|
||||
411
app/Controller/Board.php
Normal file
411
app/Controller/Board.php
Normal file
@@ -0,0 +1,411 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Project;
|
||||
use Model\User;
|
||||
|
||||
/**
|
||||
* Board controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Board extends Base
|
||||
{
|
||||
/**
|
||||
* Move a column up
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function moveUp()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$column_id = $this->request->getIntegerParam('column_id');
|
||||
|
||||
$this->board->moveUp($project_id, $column_id);
|
||||
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a column down
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function moveDown()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$column_id = $this->request->getIntegerParam('column_id');
|
||||
|
||||
$this->board->moveDown($project_id, $column_id);
|
||||
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change a task assignee directly from the board
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function assign()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
$project = $this->project->getById($task['project_id']);
|
||||
$projects = $this->project->getListByStatus(Project::ACTIVE);
|
||||
|
||||
if ($this->acl->isRegularUser()) {
|
||||
$projects = $this->project->filterListByAccess($projects, $this->acl->getUserId());
|
||||
}
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
if ($this->request->isAjax()) {
|
||||
|
||||
$this->response->html($this->template->load('board_assign', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'users_list' => $this->project->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
)));
|
||||
}
|
||||
else {
|
||||
|
||||
$this->response->html($this->template->layout('board_assign', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'users_list' => $this->project->getUsersList($project['id']),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project['id'],
|
||||
'current_project_name' => $project['name'],
|
||||
'menu' => 'boards',
|
||||
'title' => t('Change assignee').' - '.$task['title'],
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an assignee modification
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function assignTask()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
|
||||
list($valid,) = $this->task->validateAssigneeModification($values);
|
||||
|
||||
if ($valid && $this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$values['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the public version of a board
|
||||
* Access checked by a simple token, no user login, read only, auto-refresh
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function readonly()
|
||||
{
|
||||
$token = $this->request->getStringParam('token');
|
||||
$project = $this->project->getByToken($token);
|
||||
|
||||
// Token verification
|
||||
if (! $project) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
// Display the board with a specific layout
|
||||
$this->response->html($this->template->layout('board_public', array(
|
||||
'project' => $project,
|
||||
'columns' => $this->board->get($project['id']),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'title' => $project['name'],
|
||||
'no_layout' => true,
|
||||
'auto_refresh' => true,
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Redirect the user to the default project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = $this->project->getListByStatus(Project::ACTIVE);
|
||||
|
||||
if ($this->acl->isRegularUser()) {
|
||||
$projects = $this->project->filterListByAccess($projects, $this->acl->getUserId());
|
||||
}
|
||||
|
||||
if (empty($projects)) {
|
||||
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$this->redirectNoProject();
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=project&action=forbidden');
|
||||
}
|
||||
}
|
||||
else if (! empty($_SESSION['user']['default_project_id']) && isset($projects[$_SESSION['user']['default_project_id']])) {
|
||||
$project_id = $_SESSION['user']['default_project_id'];
|
||||
$project_name = $projects[$_SESSION['user']['default_project_id']];
|
||||
}
|
||||
else {
|
||||
list($project_id, $project_name) = each($projects);
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$project_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a board for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$user_id = $this->request->getIntegerParam('user_id', User::EVERYBODY_ID);
|
||||
|
||||
$this->checkProjectPermissions($project_id);
|
||||
$projects = $this->project->getListByStatus(Project::ACTIVE);
|
||||
|
||||
if ($this->acl->isRegularUser()) {
|
||||
$projects = $this->project->filterListByAccess($projects, $this->acl->getUserId());
|
||||
}
|
||||
|
||||
if (! isset($projects[$project_id])) {
|
||||
$this->notfound();
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_index', array(
|
||||
'users' => $this->project->getUsersList($project_id, true, true),
|
||||
'filters' => array('user_id' => $user_id),
|
||||
'projects' => $projects,
|
||||
'current_project_id' => $project_id,
|
||||
'current_project_name' => $projects[$project_id],
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, true, true),
|
||||
'menu' => 'boards',
|
||||
'title' => $projects[$project_id]
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a board
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$values = array();
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$values['title['.$column['id'].']'] = $column['title'];
|
||||
$values['task_limit['.$column['id'].']'] = $column['task_limit'] ?: null;
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $values + array('project_id' => $project_id),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a board
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumns($project_id);
|
||||
$data = $this->request->getValues();
|
||||
$values = $columns_list = array();
|
||||
|
||||
foreach ($columns as $column) {
|
||||
$columns_list[$column['id']] = $column['title'];
|
||||
$values['title['.$column['id'].']'] = isset($data['title'][$column['id']]) ? $data['title'][$column['id']] : '';
|
||||
$values['task_limit['.$column['id'].']'] = isset($data['task_limit'][$column['id']]) ? $data['task_limit'][$column['id']] : 0;
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->board->validateModification($columns_list, $values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->board->update($data)) {
|
||||
$this->session->flash(t('Board updated successfully.'));
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this board.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + array('project_id' => $project_id),
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and add a new column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) $this->notfound();
|
||||
|
||||
$columns = $this->board->getColumnsList($project_id);
|
||||
$data = $this->request->getValues();
|
||||
$values = array();
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$values['title['.$column_id.']'] = $column_title;
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->board->validateCreation($data);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->board->add($data)) {
|
||||
$this->session->flash(t('Board updated successfully.'));
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$project['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this board.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('board_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values + $data,
|
||||
'columns' => $columns,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit board')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$this->response->html($this->template->layout('board_remove', array(
|
||||
'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove a column from a board')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a column
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$column = $this->board->getColumn($this->request->getIntegerParam('column_id'));
|
||||
|
||||
if ($column && $this->board->removeColumn($column['id'])) {
|
||||
$this->session->flash(t('Column removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this column.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=edit&project_id='.$column['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the board (Ajax request made by the drag and drop)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
if (isset($values['positions'])) {
|
||||
$this->board->saveTasksPosition($values['positions']);
|
||||
}
|
||||
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
)),
|
||||
201
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the board have been changed
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$timestamp = $this->request->getIntegerParam('timestamp');
|
||||
|
||||
if ($project_id > 0 && ! $this->project->isUserAllowed($project_id, $this->acl->getUserId())) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
if ($this->project->isModifiedSince($project_id, $timestamp)) {
|
||||
$this->response->html(
|
||||
$this->template->load('board_show', array(
|
||||
'current_project_id' => $project_id,
|
||||
'board' => $this->board->get($project_id),
|
||||
'categories' => $this->category->getList($project_id, false),
|
||||
))
|
||||
);
|
||||
}
|
||||
else {
|
||||
$this->response->status(304);
|
||||
}
|
||||
}
|
||||
}
|
||||
189
app/Controller/Category.php
Normal file
189
app/Controller/Category.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Categories management
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Category extends Base
|
||||
{
|
||||
/**
|
||||
* Get the current project (common method between actions)
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getProject()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
return $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the category (common method between actions)
|
||||
*
|
||||
* @access private
|
||||
* @return array
|
||||
*/
|
||||
private function getCategory($project_id)
|
||||
{
|
||||
$category = $this->category->getById($this->request->getIntegerParam('category_id'));
|
||||
|
||||
if (! $category) {
|
||||
$this->session->flashError(t('Category not found.'));
|
||||
$this->response->redirect('?controller=category&action=index&project_id='.$project_id);
|
||||
}
|
||||
|
||||
return $category;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of categories for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$this->response->html($this->template->layout('category_index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => array('project_id' => $project['id']),
|
||||
'errors' => array(),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->category->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->category->create($values)) {
|
||||
$this->session->flash(t('Your category have been created successfully.'));
|
||||
$this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your category.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('category_index', array(
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a category (display the form)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('category_edit', array(
|
||||
'values' => $category,
|
||||
'errors' => array(),
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a category (validate the form and update the database)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->category->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->category->update($values)) {
|
||||
$this->session->flash(t('Your category have been updated successfully.'));
|
||||
$this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your category.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('category_edit', array(
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Categories')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a category
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
$this->response->html($this->template->layout('category_remove', array(
|
||||
'project' => $project,
|
||||
'category' => $category,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove a category')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a category
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$project = $this->getProject();
|
||||
$category = $this->getCategory($project['id']);
|
||||
|
||||
if ($this->category->remove($category['id'])) {
|
||||
$this->session->flash(t('Category removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this category.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=category&action=index&project_id='.$project['id']);
|
||||
}
|
||||
}
|
||||
189
app/Controller/Comment.php
Normal file
189
app/Controller/Comment.php
Normal file
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Comment controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Comment extends Base
|
||||
{
|
||||
/**
|
||||
* Forbidden page for comments
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function forbidden()
|
||||
{
|
||||
$this->response->html($this->template->layout('comment_forbidden', array(
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a comment
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
list($valid, $errors) = $this->comment->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->comment->create($values)) {
|
||||
$this->session->flash(t('Comment added successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your comment.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->showTask(
|
||||
$task,
|
||||
array('values' => $values, 'errors' => $errors)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Edit a comment
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
$comment_id = $this->request->getIntegerParam('comment_id');
|
||||
|
||||
$task = $this->task->getById($task_id, true);
|
||||
$comment = $this->comment->getById($comment_id);
|
||||
|
||||
if (! $task || ! $comment) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if ($this->acl->isAdminUser() || $comment['user_id'] == $this->acl->getUserId()) {
|
||||
|
||||
$this->showTask(
|
||||
$task,
|
||||
array(),
|
||||
array(),
|
||||
array('values' => array('id' => $comment['id']), 'errors' => array())
|
||||
);
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update and validate a comment
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$task_id = $this->request->getIntegerParam('task_id');
|
||||
$comment_id = $this->request->getIntegerParam('comment_id');
|
||||
|
||||
$task = $this->task->getById($task_id, true);
|
||||
$comment = $this->comment->getById($comment_id);
|
||||
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (! $task || ! $comment) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if ($this->acl->isAdminUser() || $comment['user_id'] == $this->acl->getUserId()) {
|
||||
|
||||
list($valid, $errors) = $this->comment->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->comment->update($values)) {
|
||||
$this->session->flash(t('Comment updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your comment.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id'].'#comment-'.$comment_id);
|
||||
}
|
||||
|
||||
$this->showTask(
|
||||
$task,
|
||||
array(),
|
||||
array(),
|
||||
array('values' => $values, 'errors' => $errors)
|
||||
);
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a comment
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$comment_id = $this->request->getIntegerParam('comment_id');
|
||||
|
||||
$this->checkProjectPermissions($project_id);
|
||||
|
||||
$comment = $this->comment->getById($comment_id);
|
||||
if (! $comment) $this->notfound();
|
||||
|
||||
if ($this->acl->isAdminUser() || $comment['user_id'] == $this->acl->getUserId()) {
|
||||
|
||||
$this->response->html($this->template->layout('comment_remove', array(
|
||||
'comment' => $comment,
|
||||
'project_id' => $project_id,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a comment')
|
||||
)));
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a comment
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$comment_id = $this->request->getIntegerParam('comment_id');
|
||||
|
||||
$this->checkProjectPermissions($project_id);
|
||||
|
||||
$comment = $this->comment->getById($comment_id);
|
||||
if (! $comment) $this->notfound();
|
||||
|
||||
if ($this->acl->isAdminUser() || $comment['user_id'] == $this->acl->getUserId()) {
|
||||
|
||||
if ($this->comment->remove($comment['id'])) {
|
||||
$this->session->flash(t('Comment removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this comment.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$comment['task_id']);
|
||||
}
|
||||
|
||||
$this->forbidden();
|
||||
}
|
||||
}
|
||||
117
app/Controller/Config.php
Normal file
117
app/Controller/Config.php
Normal file
@@ -0,0 +1,117 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* Config controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
/**
|
||||
* Display the settings page
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $this->config->getAll(),
|
||||
'errors' => array(),
|
||||
'menu' => 'config',
|
||||
'title' => t('Settings'),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'remember_me_sessions' => $this->rememberMe->getAll($this->acl->getUserId()),
|
||||
'last_logins' => $this->lastLogin->getAll($this->acl->getUserId()),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save settings
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->config->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->config->save($values)) {
|
||||
$this->config->reload();
|
||||
$this->session->flash(t('Settings saved successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to save your settings.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=config');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('config_index', array(
|
||||
'db_size' => $this->config->getDatabaseSize(),
|
||||
'user' => $_SESSION['user'],
|
||||
'projects' => $this->project->getList(),
|
||||
'languages' => $this->config->getLanguages(),
|
||||
'values' => $values,
|
||||
'errors' => $errors,
|
||||
'menu' => 'config',
|
||||
'title' => t('Settings'),
|
||||
'timezones' => $this->config->getTimezones(),
|
||||
'remember_me_sessions' => $this->rememberMe->getAll($this->acl->getUserId()),
|
||||
'last_logins' => $this->lastLogin->getAll($this->acl->getUserId()),
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Download the Sqlite database
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function downloadDb()
|
||||
{
|
||||
$this->response->forceDownload('db.sqlite.gz');
|
||||
$this->response->binary($this->config->downloadDatabase());
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize the Sqlite database
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function optimizeDb()
|
||||
{
|
||||
$this->config->optimizeDatabase();
|
||||
$this->session->flash(t('Database optimization done.'));
|
||||
$this->response->redirect('?controller=config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate all application tokens
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tokens()
|
||||
{
|
||||
$this->config->regenerateTokens();
|
||||
$this->session->flash(t('All tokens have been regenerated.'));
|
||||
$this->response->redirect('?controller=config');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a "RememberMe" token
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function removeRememberMeToken()
|
||||
{
|
||||
$this->rememberMe->remove($this->request->getIntegerParam('id'));
|
||||
$this->response->redirect('?controller=config&action=index#remember-me');
|
||||
}
|
||||
}
|
||||
375
app/Controller/Project.php
Normal file
375
app/Controller/Project.php
Normal file
@@ -0,0 +1,375 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Task;
|
||||
|
||||
/**
|
||||
* Project controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Project extends Base
|
||||
{
|
||||
/**
|
||||
* Display access forbidden page
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function forbidden()
|
||||
{
|
||||
$this->response->html($this->template->layout('project_forbidden', array(
|
||||
'menu' => 'projects',
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Task search for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function search()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$search = $this->request->getStringParam('search');
|
||||
|
||||
$project = $this->project->getById($project_id);
|
||||
$tasks = array();
|
||||
$nb_tasks = 0;
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
if ($search !== '') {
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id),
|
||||
'or' => array(
|
||||
array('column' => 'title', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
//array('column' => 'description', 'operator' => 'like', 'value' => '%'.$search.'%'),
|
||||
)
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_search', array(
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'values' => array(
|
||||
'search' => $search,
|
||||
'controller' => 'project',
|
||||
'action' => 'search',
|
||||
'project_id' => $project['id'],
|
||||
),
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project_id),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'title' => $project['name'].($nb_tasks > 0 ? ' ('.$nb_tasks.')' : '')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of completed tasks for a given project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function tasks()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$project = $this->project->getById($project_id);
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->checkProjectPermissions($project['id']);
|
||||
|
||||
$filters = array(
|
||||
array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id),
|
||||
array('column' => 'is_active', 'operator' => 'eq', 'value' => Task::STATUS_CLOSED),
|
||||
);
|
||||
|
||||
$tasks = $this->task->find($filters);
|
||||
$nb_tasks = count($tasks);
|
||||
|
||||
$this->response->html($this->template->layout('project_tasks', array(
|
||||
'menu' => 'projects',
|
||||
'project' => $project,
|
||||
'columns' => $this->board->getColumnsList($project_id),
|
||||
'categories' => $this->category->getList($project['id'], false),
|
||||
'tasks' => $tasks,
|
||||
'nb_tasks' => $nb_tasks,
|
||||
'title' => $project['name'].' ('.$nb_tasks.')'
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List of projects
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$projects = $this->project->getAll(true, $this->acl->isRegularUser());
|
||||
$nb_projects = count($projects);
|
||||
|
||||
$this->response->html($this->template->layout('project_index', array(
|
||||
'projects' => $projects,
|
||||
'nb_projects' => $nb_projects,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Projects').' ('.$nb_projects.')'
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
'menu' => 'projects',
|
||||
'title' => t('New project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->project->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->create($values)) {
|
||||
$this->session->flash(t('Your project have been created successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'projects',
|
||||
'title' => t('New Project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$values = $this->request->getValues() + array('is_active' => 0);
|
||||
list($valid, $errors) = $this->project->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->update($values)) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit Project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_remove', array(
|
||||
'project' => $project,
|
||||
'menu' => 'projects',
|
||||
'title' => t('Remove project')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->remove($project_id)) {
|
||||
$this->session->flash(t('Project removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function enable()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->enable($project_id)) {
|
||||
$this->session->flash(t('Project activated successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to activate this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function disable()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
|
||||
if ($project_id && $this->project->disable($project_id)) {
|
||||
$this->session->flash(t('Project disabled successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to disable this project.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
/**
|
||||
* Users list for the selected project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
$project = $this->project->getById($this->request->getIntegerParam('project_id'));
|
||||
|
||||
if (! $project) {
|
||||
$this->session->flashError(t('Project not found.'));
|
||||
$this->response->redirect('?controller=project');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('project_users', array(
|
||||
'project' => $project,
|
||||
'users' => $this->project->getAllUsers($project['id']),
|
||||
'menu' => 'projects',
|
||||
'title' => t('Edit project access list')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow a specific user for the selected project
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function allow()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid,) = $this->project->validateUserAccess($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->allowUser($values['project_id'], $values['user_id'])) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke user access
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function revoke()
|
||||
{
|
||||
$values = array(
|
||||
'project_id' => $this->request->getIntegerParam('project_id'),
|
||||
'user_id' => $this->request->getIntegerParam('user_id'),
|
||||
);
|
||||
|
||||
list($valid,) = $this->project->validateUserAccess($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->project->revokeUser($values['project_id'], $values['user_id'])) {
|
||||
$this->session->flash(t('Project updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update this project.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']);
|
||||
}
|
||||
}
|
||||
397
app/Controller/Task.php
Normal file
397
app/Controller/Task.php
Normal file
@@ -0,0 +1,397 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
use Model\Project;
|
||||
|
||||
/**
|
||||
* Task controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Task extends Base
|
||||
{
|
||||
/**
|
||||
* Webhook to create a task (useful for external software)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function add()
|
||||
{
|
||||
$token = $this->request->getStringParam('token');
|
||||
|
||||
if ($this->config->get('webhooks_token') !== $token) {
|
||||
$this->response->text('Not Authorized', 401);
|
||||
}
|
||||
|
||||
$defaultProject = $this->project->getFirst();
|
||||
|
||||
$values = array(
|
||||
'title' => $this->request->getStringParam('title'),
|
||||
'description' => $this->request->getStringParam('description'),
|
||||
'color_id' => $this->request->getStringParam('color_id', 'blue'),
|
||||
'project_id' => $this->request->getIntegerParam('project_id', $defaultProject['id']),
|
||||
'owner_id' => $this->request->getIntegerParam('owner_id'),
|
||||
'column_id' => $this->request->getIntegerParam('column_id'),
|
||||
'category_id' => $this->request->getIntegerParam('category_id'),
|
||||
);
|
||||
|
||||
if ($values['column_id'] == 0) {
|
||||
$values['column_id'] = $this->board->getFirstColumn($values['project_id']);
|
||||
}
|
||||
|
||||
list($valid,) = $this->task->validateCreation($values);
|
||||
|
||||
if ($valid && $this->task->create($values)) {
|
||||
$this->response->text('OK');
|
||||
}
|
||||
|
||||
$this->response->text('FAILED');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function show()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
$this->showTask($task);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a description from the show task page
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function description()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
list($valid, $errors) = $this->task->validateDescriptionCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
$this->showTask(
|
||||
$task,
|
||||
array(),
|
||||
array('values' => $values, 'errors' => $errors)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$project_id = $this->request->getIntegerParam('project_id');
|
||||
$this->checkProjectPermissions($project_id);
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => array(),
|
||||
'values' => array(
|
||||
'project_id' => $project_id,
|
||||
'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'),
|
||||
),
|
||||
'projects_list' => $this->project->getListByStatus(\Model\Project::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($project_id),
|
||||
'users_list' => $this->project->getUsersList($project_id),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($project_id),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
|
||||
list($valid, $errors) = $this->task->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->create($values)) {
|
||||
$this->session->flash(t('Task created successfully.'));
|
||||
|
||||
if (isset($values['another_task']) && $values['another_task'] == 1) {
|
||||
unset($values['title']);
|
||||
unset($values['description']);
|
||||
$this->response->redirect('?controller=task&action=create&'.http_build_query($values));
|
||||
}
|
||||
else {
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$values['project_id']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your task.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'projects_list' => $this->project->getListByStatus(Project::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($values['project_id']),
|
||||
'users_list' => $this->project->getUsersList($values['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($values['project_id']),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if (! empty($task['date_due'])) {
|
||||
$task['date_due'] = date(t('m/d/Y'), $task['date_due']);
|
||||
}
|
||||
else {
|
||||
$task['date_due'] = '';
|
||||
}
|
||||
|
||||
$task['score'] = $task['score'] ?: '';
|
||||
|
||||
$this->response->html($this->template->layout('task_edit', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'users_list' => $this->project->getUsersList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
$this->checkProjectPermissions($values['project_id']);
|
||||
|
||||
list($valid, $errors) = $this->task->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->task->update($values)) {
|
||||
$this->session->flash(t('Task updated successfully.'));
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$values['id']);
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your task.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('task_edit', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'columns_list' => $this->board->getColumnsList($values['project_id']),
|
||||
'users_list' => $this->project->getUsersList($values['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($values['project_id']),
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Edit a task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function close()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if ($this->task->close($task['id'])) {
|
||||
$this->session->flash(t('Task closed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to close this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to close a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmClose()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
$this->response->html($this->taskLayout('task_close', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Close a task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function open()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if ($this->task->open($task['id'])) {
|
||||
$this->session->flash(t('Task opened successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to open this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=task&action=show&task_id='.$task['id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to open a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmOpen()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
$this->response->html($this->taskLayout('task_open', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Open a task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if ($this->task->remove($task['id'])) {
|
||||
$this->session->flash(t('Task removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this task.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=board&action=show&project_id='.$task['project_id']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before removing a task
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirmRemove()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'), true);
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
$this->response->html($this->taskLayout('task_remove', array(
|
||||
'task' => $task,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('Remove a task')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task (fill the form for a new task)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function duplicate()
|
||||
{
|
||||
$task = $this->task->getById($this->request->getIntegerParam('task_id'));
|
||||
|
||||
if (! $task) $this->notfound();
|
||||
$this->checkProjectPermissions($task['project_id']);
|
||||
|
||||
if (! empty($task['date_due'])) {
|
||||
$task['date_due'] = date(t('m/d/Y'), $task['date_due']);
|
||||
}
|
||||
else {
|
||||
$task['date_due'] = '';
|
||||
}
|
||||
|
||||
$task['score'] = $task['score'] ?: '';
|
||||
|
||||
$this->response->html($this->template->layout('task_new', array(
|
||||
'errors' => array(),
|
||||
'values' => $task,
|
||||
'projects_list' => $this->project->getListByStatus(Project::ACTIVE),
|
||||
'columns_list' => $this->board->getColumnsList($task['project_id']),
|
||||
'users_list' => $this->project->getUsersList($task['project_id']),
|
||||
'colors_list' => $this->task->getColors(),
|
||||
'categories_list' => $this->category->getList($task['project_id']),
|
||||
'duplicate' => true,
|
||||
'menu' => 'tasks',
|
||||
'title' => t('New task')
|
||||
)));
|
||||
}
|
||||
}
|
||||
310
app/Controller/User.php
Normal file
310
app/Controller/User.php
Normal file
@@ -0,0 +1,310 @@
|
||||
<?php
|
||||
|
||||
namespace Controller;
|
||||
|
||||
/**
|
||||
* User controller
|
||||
*
|
||||
* @package controller
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class User extends Base
|
||||
{
|
||||
/**
|
||||
* Display access forbidden page
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function forbidden()
|
||||
{
|
||||
$this->response->html($this->template->layout('user_forbidden', array(
|
||||
'menu' => 'users',
|
||||
'title' => t('Access Forbidden')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout and destroy session
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function logout()
|
||||
{
|
||||
$this->rememberMe->destroy($this->acl->getUserId());
|
||||
$this->session->close();
|
||||
$this->response->redirect('?controller=user&action=login');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the form login
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
if (isset($_SESSION['user'])) $this->response->redirect('?controller=app');
|
||||
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
'no_layout' => true,
|
||||
'title' => t('Login')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check credentials
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function check()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->user->validateLogin($values);
|
||||
|
||||
if ($valid) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'no_layout' => true,
|
||||
'title' => t('Login')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* List all users
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function index()
|
||||
{
|
||||
$users = $this->user->getAll();
|
||||
$nb_users = count($users);
|
||||
|
||||
$this->response->html(
|
||||
$this->template->layout('user_index', array(
|
||||
'projects' => $this->project->getList(),
|
||||
'users' => $users,
|
||||
'nb_users' => $nb_users,
|
||||
'menu' => 'users',
|
||||
'title' => t('Users').' ('.$nb_users.')'
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to create a new user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
$this->response->html($this->template->layout('user_new', array(
|
||||
'projects' => $this->project->getList(),
|
||||
'errors' => array(),
|
||||
'values' => array(),
|
||||
'menu' => 'users',
|
||||
'title' => t('New user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and save a new user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function save()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
list($valid, $errors) = $this->user->validateCreation($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->user->create($values)) {
|
||||
$this->session->flash(t('User created successfully.'));
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to create your user.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_new', array(
|
||||
'projects' => $this->project->getList(),
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'users',
|
||||
'title' => t('New user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a form to edit a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function edit()
|
||||
{
|
||||
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
|
||||
|
||||
if (! $user) $this->notfound();
|
||||
|
||||
if ($this->acl->isRegularUser() && $this->acl->getUserId() != $user['id']) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
unset($user['password']);
|
||||
|
||||
$this->response->html($this->template->layout('user_edit', array(
|
||||
'projects' => $this->project->filterListByAccess($this->project->getList(), $user['id']),
|
||||
'errors' => array(),
|
||||
'values' => $user,
|
||||
'menu' => 'users',
|
||||
'title' => t('Edit user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate and update a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function update()
|
||||
{
|
||||
$values = $this->request->getValues();
|
||||
|
||||
if ($this->acl->isAdminUser()) {
|
||||
$values += array('is_admin' => 0);
|
||||
}
|
||||
else {
|
||||
|
||||
if ($this->acl->getUserId() != $values['id']) {
|
||||
$this->forbidden();
|
||||
}
|
||||
|
||||
if (isset($values['is_admin'])) {
|
||||
unset($values['is_admin']); // Regular users can't be admin
|
||||
}
|
||||
}
|
||||
|
||||
list($valid, $errors) = $this->user->validateModification($values);
|
||||
|
||||
if ($valid) {
|
||||
|
||||
if ($this->user->update($values)) {
|
||||
$this->session->flash(t('User updated successfully.'));
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to update your user.'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->html($this->template->layout('user_edit', array(
|
||||
'projects' => $this->project->filterListByAccess($this->project->getList(), $values['id']),
|
||||
'errors' => $errors,
|
||||
'values' => $values,
|
||||
'menu' => 'users',
|
||||
'title' => t('Edit user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirmation dialog before to remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function confirm()
|
||||
{
|
||||
$user = $this->user->getById($this->request->getIntegerParam('user_id'));
|
||||
|
||||
if (! $user) $this->notfound();
|
||||
|
||||
$this->response->html($this->template->layout('user_remove', array(
|
||||
'user' => $user,
|
||||
'menu' => 'users',
|
||||
'title' => t('Remove user')
|
||||
)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a user
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function remove()
|
||||
{
|
||||
$user_id = $this->request->getIntegerParam('user_id');
|
||||
|
||||
if ($user_id && $this->user->remove($user_id)) {
|
||||
$this->session->flash(t('User removed successfully.'));
|
||||
} else {
|
||||
$this->session->flashError(t('Unable to remove this user.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
|
||||
/**
|
||||
* Google authentication
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function google()
|
||||
{
|
||||
$code = $this->request->getStringParam('code');
|
||||
|
||||
if ($code) {
|
||||
|
||||
$profile = $this->google->getGoogleProfile($code);
|
||||
|
||||
if (is_array($profile)) {
|
||||
|
||||
// If the user is already logged, link the account otherwise authenticate
|
||||
if ($this->acl->isLogged()) {
|
||||
|
||||
if ($this->google->updateUser($this->acl->getUserId(), $profile)) {
|
||||
$this->session->flash(t('Your Google Account is linked to your profile successfully.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to link your Google Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
else if ($this->google->authenticate($profile['id'])) {
|
||||
$this->response->redirect('?controller=app');
|
||||
}
|
||||
else {
|
||||
$this->response->html($this->template->layout('user_login', array(
|
||||
'errors' => array('login' => t('Google authentication failed')),
|
||||
'values' => array(),
|
||||
'no_layout' => true,
|
||||
'title' => t('Login')
|
||||
)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->response->redirect($this->google->getAuthorizationUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink a Google account
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function unlinkGoogle()
|
||||
{
|
||||
if ($this->google->unlink($this->acl->getUserId())) {
|
||||
$this->session->flash(t('Your Google Account is not linked anymore to your profile.'));
|
||||
}
|
||||
else {
|
||||
$this->session->flashError(t('Unable to unlink your Google Account.'));
|
||||
}
|
||||
|
||||
$this->response->redirect('?controller=user');
|
||||
}
|
||||
}
|
||||
135
app/Core/Event.php
Normal file
135
app/Core/Event.php
Normal file
@@ -0,0 +1,135 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Event dispatcher class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Event
|
||||
{
|
||||
/**
|
||||
* Contains all listeners
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $listeners = array();
|
||||
|
||||
/**
|
||||
* The last listener executed
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $lastListener = '';
|
||||
|
||||
/**
|
||||
* The last triggered event
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $lastEvent = '';
|
||||
|
||||
/**
|
||||
* Triggered events list
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $events = array();
|
||||
|
||||
/**
|
||||
* Attach a listener object to an event
|
||||
*
|
||||
* @access public
|
||||
* @param string $eventName Event name
|
||||
* @param Listener $listener Object that implements the Listener interface
|
||||
*/
|
||||
public function attach($eventName, Listener $listener)
|
||||
{
|
||||
if (! isset($this->listeners[$eventName])) {
|
||||
$this->listeners[$eventName] = array();
|
||||
}
|
||||
|
||||
$this->listeners[$eventName][] = $listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trigger an event
|
||||
*
|
||||
* @access public
|
||||
* @param string $eventName Event name
|
||||
* @param array $data Event data
|
||||
*/
|
||||
public function trigger($eventName, array $data)
|
||||
{
|
||||
$this->lastEvent = $eventName;
|
||||
$this->events[] = $eventName;
|
||||
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
foreach ($this->listeners[$eventName] as $listener) {
|
||||
if ($listener->execute($data)) {
|
||||
$this->lastListener = get_class($listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last listener executed
|
||||
*
|
||||
* @access public
|
||||
* @return string Event name
|
||||
*/
|
||||
public function getLastListenerExecuted()
|
||||
{
|
||||
return $this->lastListener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last fired event
|
||||
*
|
||||
* @access public
|
||||
* @return string Event name
|
||||
*/
|
||||
public function getLastTriggeredEvent()
|
||||
{
|
||||
return $this->lastEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of triggered events
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getTriggeredEvents()
|
||||
{
|
||||
return $this->events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a listener bind to an event
|
||||
*
|
||||
* @access public
|
||||
* @param string $eventName Event name
|
||||
* @param mixed $instance Instance name or object itself
|
||||
* @return bool Yes or no
|
||||
*/
|
||||
public function hasListener($eventName, $instance)
|
||||
{
|
||||
if (isset($this->listeners[$eventName])) {
|
||||
foreach ($this->listeners[$eventName] as $listener) {
|
||||
if ($listener instanceof $instance) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
17
app/Core/Listener.php
Normal file
17
app/Core/Listener.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Event listener interface
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
interface Listener {
|
||||
|
||||
/**
|
||||
* @return boolean
|
||||
*/
|
||||
public function execute(array $data);
|
||||
}
|
||||
37
app/Core/Loader.php
Normal file
37
app/Core/Loader.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Loader class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Loader
|
||||
{
|
||||
/**
|
||||
* Load the missing class
|
||||
*
|
||||
* @access public
|
||||
* @param string $class Class name
|
||||
*/
|
||||
public function load($class)
|
||||
{
|
||||
$filename = __DIR__.DIRECTORY_SEPARATOR.'..'.DIRECTORY_SEPARATOR.str_replace('\\', DIRECTORY_SEPARATOR, $class).'.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
require $filename;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the autoloader
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
spl_autoload_register(array($this, 'load'));
|
||||
}
|
||||
}
|
||||
79
app/Core/Registry.php
Normal file
79
app/Core/Registry.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* The registry class is a dependency injection container
|
||||
*
|
||||
* @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];
|
||||
}
|
||||
}
|
||||
56
app/Core/Request.php
Normal file
56
app/Core/Request.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
class Request
|
||||
{
|
||||
public function getStringParam($name, $default_value = '')
|
||||
{
|
||||
return isset($_GET[$name]) ? $_GET[$name] : $default_value;
|
||||
}
|
||||
|
||||
public function getIntegerParam($name, $default_value = 0)
|
||||
{
|
||||
return isset($_GET[$name]) && ctype_digit($_GET[$name]) ? (int) $_GET[$name] : $default_value;
|
||||
}
|
||||
|
||||
public function getValue($name)
|
||||
{
|
||||
$values = $this->getValues();
|
||||
return isset($values[$name]) ? $values[$name] : null;
|
||||
}
|
||||
|
||||
public function getValues()
|
||||
{
|
||||
if (! empty($_POST)) return $_POST;
|
||||
|
||||
$result = json_decode($this->getBody(), true);
|
||||
if ($result) return $result;
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
public function getBody()
|
||||
{
|
||||
return file_get_contents('php://input');
|
||||
}
|
||||
|
||||
public function getFileContent($name)
|
||||
{
|
||||
if (isset($_FILES[$name])) {
|
||||
return file_get_contents($_FILES[$name]['tmp_name']);
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
public function isPost()
|
||||
{
|
||||
return isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
|
||||
}
|
||||
|
||||
public function isAjax()
|
||||
{
|
||||
return isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest';
|
||||
}
|
||||
}
|
||||
138
app/Core/Response.php
Normal file
138
app/Core/Response.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
class Response
|
||||
{
|
||||
public function forceDownload($filename)
|
||||
{
|
||||
header('Content-Disposition: attachment; filename="'.$filename.'"');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param integer $status_code
|
||||
*/
|
||||
public function status($status_code)
|
||||
{
|
||||
header('Status: '.$status_code);
|
||||
header($_SERVER['SERVER_PROTOCOL'].' '.$status_code);
|
||||
}
|
||||
|
||||
public function redirect($url)
|
||||
{
|
||||
header('Location: '.$url);
|
||||
exit;
|
||||
}
|
||||
|
||||
public function json(array $data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function text($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function html($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function xml($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/xml; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function js($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Type: text/javascript; charset=utf-8');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function binary($data, $status_code = 200)
|
||||
{
|
||||
$this->status($status_code);
|
||||
|
||||
header('Content-Transfer-Encoding: binary');
|
||||
header('Content-Type: application/octet-stream');
|
||||
echo $data;
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function csp(array $policies = array())
|
||||
{
|
||||
$policies['default-src'] = "'self'";
|
||||
$values = '';
|
||||
|
||||
foreach ($policies as $policy => $hosts) {
|
||||
|
||||
if (is_array($hosts)) {
|
||||
|
||||
$acl = '';
|
||||
|
||||
foreach ($hosts as &$host) {
|
||||
|
||||
if ($host === '*' || $host === 'self' || strpos($host, 'http') === 0) {
|
||||
$acl .= $host.' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
$acl = $hosts;
|
||||
}
|
||||
|
||||
$values .= $policy.' '.trim($acl).'; ';
|
||||
}
|
||||
|
||||
header('Content-Security-Policy: '.$values);
|
||||
}
|
||||
|
||||
public function nosniff()
|
||||
{
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
}
|
||||
|
||||
public function xss()
|
||||
{
|
||||
header('X-XSS-Protection: 1; mode=block');
|
||||
}
|
||||
|
||||
public function hsts()
|
||||
{
|
||||
if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off') {
|
||||
header('Strict-Transport-Security: max-age=31536000');
|
||||
}
|
||||
}
|
||||
|
||||
public function xframe($mode = 'DENY', array $urls = array())
|
||||
{
|
||||
header('X-Frame-Options: '.$mode.' '.implode(' ', $urls));
|
||||
}
|
||||
}
|
||||
111
app/Core/Router.php
Normal file
111
app/Core/Router.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Router class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Router
|
||||
{
|
||||
/**
|
||||
* Controller name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $controller = '';
|
||||
|
||||
/**
|
||||
* Action name
|
||||
*
|
||||
* @access private
|
||||
* @var string
|
||||
*/
|
||||
private $action = '';
|
||||
|
||||
/**
|
||||
* Registry instance
|
||||
*
|
||||
* @access private
|
||||
* @var Core\Registry
|
||||
*/
|
||||
private $registry;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param Core\Registry $registry Registry instance
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
*/
|
||||
public function __construct(Registry $registry, $controller = '', $action = '')
|
||||
{
|
||||
$this->registry = $registry;
|
||||
$this->controller = empty($_GET['controller']) ? $controller : $_GET['controller'];
|
||||
$this->action = empty($_GET['action']) ? $controller : $_GET['action'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check controller and action parameter
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Controller or action name
|
||||
* @param string $default_value Default value if validation fail
|
||||
*/
|
||||
public function sanitize($value, $default_value)
|
||||
{
|
||||
return ! ctype_alpha($value) || empty($value) ? $default_value : strtolower($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a controller and execute the action
|
||||
*
|
||||
* @access public
|
||||
* @param string $filename Controller filename
|
||||
* @param string $class Class name
|
||||
* @param string $method Method name
|
||||
*/
|
||||
public function load($filename, $class, $method)
|
||||
{
|
||||
if (file_exists($filename)) {
|
||||
|
||||
require $filename;
|
||||
|
||||
if (! method_exists($class, $method)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$instance = new $class($this->registry);
|
||||
$instance->request = new Request;
|
||||
$instance->response = new Response;
|
||||
$instance->session = new Session;
|
||||
$instance->template = new Template;
|
||||
$instance->beforeAction($this->controller, $this->action);
|
||||
$instance->$method();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find a route
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function execute()
|
||||
{
|
||||
$this->controller = $this->sanitize($this->controller, 'app');
|
||||
$this->action = $this->sanitize($this->action, 'index');
|
||||
$filename = __DIR__.'/../Controller/'.ucfirst($this->controller).'.php';
|
||||
|
||||
if (! $this->load($filename, '\Controller\\'.$this->controller, $this->action)) {
|
||||
die('Page not found!');
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Core/Session.php
Normal file
56
app/Core/Session.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
class Session
|
||||
{
|
||||
const SESSION_LIFETIME = 86400; // 1 day
|
||||
|
||||
public function open($base_path = '/', $save_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,
|
||||
isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on',
|
||||
true
|
||||
);
|
||||
|
||||
// Avoid session id in the URL
|
||||
ini_set('session.use_only_cookies', '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);
|
||||
|
||||
// Custom session name
|
||||
session_name('__S');
|
||||
|
||||
session_start();
|
||||
|
||||
// Regenerate the session id to avoid session fixation issue
|
||||
if (empty($_SESSION['__validated'])) {
|
||||
session_regenerate_id(true);
|
||||
$_SESSION['__validated'] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public function close()
|
||||
{
|
||||
session_destroy();
|
||||
}
|
||||
|
||||
public function flash($message)
|
||||
{
|
||||
$_SESSION['flash_message'] = $message;
|
||||
}
|
||||
|
||||
public function flashError($message)
|
||||
{
|
||||
$_SESSION['flash_error_message'] = $message;
|
||||
}
|
||||
}
|
||||
72
app/Core/Template.php
Normal file
72
app/Core/Template.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Template class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Template
|
||||
{
|
||||
/**
|
||||
* Template path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PATH = 'app/Templates/';
|
||||
|
||||
/**
|
||||
* Load a template
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* $template->load('template_name', ['bla' => 'value']);
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function load()
|
||||
{
|
||||
if (func_num_args() < 1 || func_num_args() > 2) {
|
||||
die('Invalid template arguments');
|
||||
}
|
||||
|
||||
if (! file_exists(self::PATH.func_get_arg(0).'.php')) {
|
||||
die('Unable to load the template: "'.func_get_arg(0).'"');
|
||||
}
|
||||
|
||||
if (func_num_args() === 2) {
|
||||
|
||||
if (! is_array(func_get_arg(1))) {
|
||||
die('Template variables must be an array');
|
||||
}
|
||||
|
||||
extract(func_get_arg(1));
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
include self::PATH.func_get_arg(0).'.php';
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Render a page layout
|
||||
*
|
||||
* @access public
|
||||
* @param string $template_name Template name
|
||||
* @param array $template_args Key/value map
|
||||
* @param string $layout_name Layout name
|
||||
* @return string
|
||||
*/
|
||||
public function layout($template_name, array $template_args = array(), $layout_name = 'layout')
|
||||
{
|
||||
return $this->load(
|
||||
$layout_name,
|
||||
$template_args + array('content_for_layout' => $this->load($template_name, $template_args))
|
||||
);
|
||||
}
|
||||
}
|
||||
155
app/Core/Translator.php
Normal file
155
app/Core/Translator.php
Normal file
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
|
||||
namespace Core;
|
||||
|
||||
/**
|
||||
* Translator class
|
||||
*
|
||||
* @package core
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Translator
|
||||
{
|
||||
/**
|
||||
* Locales path
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const PATH = 'app/Locales/';
|
||||
|
||||
/**
|
||||
* Locales
|
||||
*
|
||||
* @static
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private static $locales = array();
|
||||
|
||||
/**
|
||||
* Get a translation
|
||||
*
|
||||
* $translator->translate('I have %d kids', 5);
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function translate($identifier)
|
||||
{
|
||||
$args = func_get_args();
|
||||
|
||||
array_shift($args);
|
||||
array_unshift($args, $this->get($identifier, $identifier));
|
||||
|
||||
foreach ($args as &$arg) {
|
||||
$arg = htmlspecialchars($arg, ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
|
||||
return call_user_func_array(
|
||||
'sprintf',
|
||||
$args
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted number
|
||||
*
|
||||
* $translator->number(1234.56);
|
||||
*
|
||||
* @access public
|
||||
* @param float $number Number to format
|
||||
* @return string
|
||||
*/
|
||||
public function number($number)
|
||||
{
|
||||
return number_format(
|
||||
$number,
|
||||
$this->get('number.decimals', 2),
|
||||
$this->get('number.decimals_separator', '.'),
|
||||
$this->get('number.thousands_separator', ',')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted currency number
|
||||
*
|
||||
* $translator->currency(1234.56);
|
||||
*
|
||||
* @access public
|
||||
* @param float $amount Number to format
|
||||
* @return string
|
||||
*/
|
||||
public function currency($amount)
|
||||
{
|
||||
$position = $this->get('currency.position', 'before');
|
||||
$symbol = $this->get('currency.symbol', '$');
|
||||
$str = '';
|
||||
|
||||
if ($position === 'before') {
|
||||
$str .= $symbol;
|
||||
}
|
||||
|
||||
$str .= $this->number($amount);
|
||||
|
||||
if ($position === 'after') {
|
||||
$str .= ' '.$symbol;
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a formatted datetime
|
||||
*
|
||||
* $translator->datetime('%Y-%m-%d', time());
|
||||
*
|
||||
* @access public
|
||||
* @param string $format Format defined by the strftime function
|
||||
* @param integer $timestamp Unix timestamp
|
||||
* @return string
|
||||
*/
|
||||
public function datetime($format, $timestamp)
|
||||
{
|
||||
if (! $timestamp) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return strftime($this->get($format, $format), (int) $timestamp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an identifier from the translations or return the default
|
||||
*
|
||||
* @access public
|
||||
* @param string $idendifier Locale identifier
|
||||
* @param string $default Default value
|
||||
* @return string
|
||||
*/
|
||||
public function get($identifier, $default = '')
|
||||
{
|
||||
if (isset(self::$locales[$identifier])) {
|
||||
return self::$locales[$identifier];
|
||||
}
|
||||
else {
|
||||
return $default;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load translations
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @param string $language Locale code: fr_FR
|
||||
*/
|
||||
public static function load($language)
|
||||
{
|
||||
setlocale(LC_TIME, $language.'.UTF-8', $language);
|
||||
|
||||
$filename = self::PATH.$language.DIRECTORY_SEPARATOR.'translations.php';
|
||||
|
||||
if (file_exists($filename)) {
|
||||
self::$locales = require $filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
app/Event/TaskModification.php
Normal file
51
app/Event/TaskModification.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace Event;
|
||||
|
||||
use Core\Listener;
|
||||
use Model\Project;
|
||||
|
||||
/**
|
||||
* Task modification listener
|
||||
*
|
||||
* @package events
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class TaskModification implements Listener
|
||||
{
|
||||
/**
|
||||
* Project model
|
||||
*
|
||||
* @accesss private
|
||||
* @var \Model\Project
|
||||
*/
|
||||
private $project;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \Model\Project $project Project model instance
|
||||
*/
|
||||
public function __construct(Project $project)
|
||||
{
|
||||
$this->project = $project;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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'])) {
|
||||
$this->project->updateModificationDate($data['project_id']);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
334
app/Locales/es_ES/translations.php
Normal file
334
app/Locales/es_ES/translations.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Inglés',
|
||||
'French' => 'Francés',
|
||||
'Polish' => 'Polaco',
|
||||
'Portuguese (Brazilian)' => 'Portugués (Brasil)',
|
||||
'Spanish' => 'Español',
|
||||
'None' => 'Ninguno',
|
||||
'edit' => 'modificar',
|
||||
'Edit' => 'Modificar',
|
||||
'remove' => 'suprimir',
|
||||
'Remove' => 'Suprimir',
|
||||
'Update' => 'Actualizar',
|
||||
'Yes' => 'Si',
|
||||
'No' => 'No',
|
||||
'cancel' => 'cancelar',
|
||||
'or' => 'o',
|
||||
'Yellow' => 'Amarillo',
|
||||
'Blue' => 'Azul',
|
||||
'Green' => 'Verde',
|
||||
'Purple' => 'Púrpura',
|
||||
'Red' => 'Rojo',
|
||||
'Orange' => 'Naranja',
|
||||
'Grey' => 'Gris',
|
||||
'Save' => 'Guardar',
|
||||
'Login' => 'Iniciar sesión',
|
||||
'Official website:' => 'Pagina web oficial :',
|
||||
'Unassigned' => 'No asignado',
|
||||
'View this task' => 'Ver esta tarea',
|
||||
'Remove user' => 'Eliminar un usuario',
|
||||
'Do you really want to remove this user: "%s"?' => '¿Realmente desea suprimir este usuario: « %s » ?',
|
||||
'New user' => 'Añadir un usuario',
|
||||
'All users' => 'Todos los usuarios',
|
||||
'Username' => 'Nombre de usuario',
|
||||
'Password' => 'Contraseña',
|
||||
'Default Project' => 'Proyecto por defecto',
|
||||
'Administrator' => 'Administrador',
|
||||
'Sign in' => 'Iniciar sesión',
|
||||
'Users' => 'Usuarios',
|
||||
'No user' => 'Ningún usuario',
|
||||
'Forbidden' => 'Acceso denegado',
|
||||
'Access Forbidden' => 'Acceso denegado',
|
||||
'Only administrators can access to this page.' => 'Solo los administradores pueden acceder a esta pagina.',
|
||||
'Edit user' => 'Editar un usuario',
|
||||
'Logout' => 'Salir',
|
||||
'Bad username or password' => 'Usuario o contraseña incorecto',
|
||||
'users' => 'usuarios',
|
||||
'projects' => 'proyectos',
|
||||
'Edit project' => 'Editar el proyecto',
|
||||
'Name' => 'Nombre',
|
||||
'Activated' => 'Activado',
|
||||
'Projects' => 'Proyectos',
|
||||
'No project' => 'Ningún proyecto',
|
||||
'Project' => 'Proyecto',
|
||||
'Status' => 'Estado',
|
||||
'Tasks' => 'Tareas',
|
||||
'Board' => 'Tablero',
|
||||
'Inactive' => 'Inactivo',
|
||||
'Active' => 'Activo',
|
||||
'Column %d' => 'Columna %d',
|
||||
'Add this column' => 'Añadir esta columna',
|
||||
'%d tasks on the board' => '%d tareas en el tablero',
|
||||
'%d tasks in total' => '%d tareas en total',
|
||||
'Unable to update this board.' => 'No se puede actualizar este tablero.',
|
||||
'Edit board' => 'Editar este tablero',
|
||||
'Disable' => 'Desactivar',
|
||||
'Enable' => 'Activar',
|
||||
'New project' => 'Nuevo proyecto',
|
||||
'Do you really want to remove this project: "%s"?' => '¿Realmente desea eliminar este proyecto: « %s » ?',
|
||||
'Remove project' => 'Suprimir el proyecto',
|
||||
'Boards' => 'Tableros',
|
||||
'Edit the board for "%s"' => 'Modificar el tablero por « %s »',
|
||||
'All projects' => 'Todos los proyectos',
|
||||
'Change columns' => 'Cambiar las columnas',
|
||||
'Add a new column' => 'Añadir una nueva columna',
|
||||
'Title' => 'Titulo',
|
||||
'Add Column' => 'Nueva columna',
|
||||
'Project "%s"' => 'Proyecto « %s »',
|
||||
'Nobody assigned' => 'Persona asignada',
|
||||
'Assigned to %s' => 'Asignada a %s',
|
||||
'Remove a column' => 'Suprimir esta columna',
|
||||
'Remove a column from a board' => 'Suprimir una columna de un tablero',
|
||||
'Unable to remove this column.' => 'No se puede suprimir esta columna.',
|
||||
'Do you really want to remove this column: "%s"?' => '¿Realmente desea eliminar esta columna : « %s » ?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => '¡Esta acción suprimirá todas las tareas asociadas a esta columna!',
|
||||
'Settings' => 'Preferencias',
|
||||
'Application settings' => 'Parámetros de la aplicación',
|
||||
'Language' => 'Idioma',
|
||||
'Webhooks token:' => 'Identificador (token) para los webhooks :',
|
||||
'More information' => 'Más informaciones',
|
||||
'Database size:' => 'Tamaño de la base de datos:',
|
||||
'Download the database' => 'Descargar la base de datos',
|
||||
'Optimize the database' => 'Optimisar la base de datos',
|
||||
'(VACUUM command)' => '(Comando VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(Archivo Sqlite comprimido en Gzip)',
|
||||
'User settings' => 'Parámetros de usuario',
|
||||
'My default project:' => 'Mi proyecto por defecto : ',
|
||||
'Close a task' => 'Cerrar una tarea',
|
||||
'Do you really want to close this task: "%s"?' => '¿Realmente desea cerrar esta tarea: « %s » ?',
|
||||
'Edit a task' => 'Editar una tarea',
|
||||
'Column' => 'Columna',
|
||||
'Color' => 'Coulor',
|
||||
'Assignee' => 'Persona asignada',
|
||||
'Create another task' => 'Crear una nueva tarea',
|
||||
'New task' => 'Nueva tarea',
|
||||
'Open a task' => 'Abrir una tarea',
|
||||
'Do you really want to open this task: "%s"?' => '¿Realomente desea abrir esta tarea: « %s » ?',
|
||||
'Back to the board' => 'Volver al tablero',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Creado el %d/%m/%Y a las %H:%M',
|
||||
'There is nobody assigned' => 'No hay nadie asignado a esta tarea',
|
||||
'Column on the board:' => 'Columna en el tablero: ',
|
||||
'Status is open' => 'Estado abierto',
|
||||
'Status is closed' => 'Estado cerrado',
|
||||
'Close this task' => 'Cerrar esta tarea',
|
||||
'Open this task' => 'Abrir esta tarea',
|
||||
'There is no description.' => 'No hay descripción.',
|
||||
'Add a new task' => 'Añadir una nueva tarea',
|
||||
'The username is required' => 'El nombre de usuario es obligatorio',
|
||||
'The maximum length is %d characters' => 'La longitud máxima es de %d caractères',
|
||||
'The minimum length is %d characters' => 'La longitud mínima es de %d caractères',
|
||||
'The password is required' => 'La contraseña es obligatoria',
|
||||
'This value must be an integer' => 'Este valor debe ser un entero',
|
||||
'The username must be unique' => 'El nombre de usuario debe ser único',
|
||||
'The username must be alphanumeric' => 'El nombre de usuario debe ser alfanumérico',
|
||||
'The user id is required' => 'El identificador del usuario es obligatorio',
|
||||
'Passwords doesn\'t matches' => 'Las contraseñas no corresponden',
|
||||
'The confirmation is required' => 'La confirmación es obligatoria',
|
||||
'The column is required' => 'La columna es obligatoria',
|
||||
'The project is required' => 'El proyecto es obligatorio',
|
||||
'The color is required' => 'El color es obligatorio',
|
||||
'The id is required' => 'El identificador es obligatorio',
|
||||
'The project id is required' => 'El identificador del proyecto es obligatorio',
|
||||
'The project name is required' => 'El nombre del proyecto es obligatorio',
|
||||
'This project must be unique' => 'El nombre del proyecto debe ser único',
|
||||
'The title is required' => 'El titulo es obligatorio',
|
||||
'The language is required' => 'El idioma es obligatorio',
|
||||
'There is no active project, the first step is to create a new project.' => 'No hay proyectos activados, la primera etapa consiste en crear un nuevo proyecto.',
|
||||
'Settings saved successfully.' => 'Parámetros guardados.',
|
||||
'Unable to save your settings.' => 'No se puede guardar sus parámetros.',
|
||||
'Database optimization done.' => 'Optimización de la base de datos terminada.',
|
||||
'Your project have been created successfully.' => 'El proyecto ha sido creado.',
|
||||
'Unable to create your project.' => 'No se puede crear el proyecto.',
|
||||
'Project updated successfully.' => 'El proyecto ha sido actualizado.',
|
||||
'Unable to update this project.' => 'No se puede actualizar el proyecto.',
|
||||
'Unable to remove this project.' => 'No se puede suprimir este proyecto.',
|
||||
'Project removed successfully.' => 'El proyecto ha sido borrado.',
|
||||
'Project activated successfully.' => 'El proyecto ha sido activado.',
|
||||
'Unable to activate this project.' => 'No se puede activar el proyecto.',
|
||||
'Project disabled successfully.' => 'El proyecto ha sido desactivado.',
|
||||
'Unable to disable this project.' => 'No se puede desactivar el proyecto.',
|
||||
'Unable to open this task.' => 'No se puede abrir esta tarea.',
|
||||
'Task opened successfully.' => 'La tarea ha sido abierta.',
|
||||
'Unable to close this task.' => 'No se puede cerrar esta tarea.',
|
||||
'Task closed successfully.' => 'La tarea ha sido cerrada.',
|
||||
'Unable to update your task.' => 'No se puede modificar esta tarea.',
|
||||
'Task updated successfully.' => 'La tarea ha sido actualizada.',
|
||||
'Unable to create your task.' => 'No se puede crear esta tarea.',
|
||||
'Task created successfully.' => 'La tarea ha sido creada.',
|
||||
'User created successfully.' => 'El usuario ha sido creado.',
|
||||
'Unable to create your user.' => 'No se puede crear este usuario.',
|
||||
'User updated successfully.' => 'El usuario ha sido actualizado.',
|
||||
'Unable to update your user.' => 'No se puede actualizar este usuario.',
|
||||
'User removed successfully.' => 'El usuario ha sido creado.',
|
||||
'Unable to remove this user.' => 'No se puede crear este usuario.',
|
||||
'Board updated successfully.' => 'El tablero ha sido actualizado.',
|
||||
'Ready' => 'Listo',
|
||||
'Backlog' => 'En espera',
|
||||
'Work in progress' => 'En curso',
|
||||
'Done' => 'Terminado',
|
||||
'Application version:' => 'Versión de la aplicación:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Terminado el %d/%m/%Y a las %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d/%m/%Y a las %H:%M',
|
||||
'Date created' => 'Fecha de creación',
|
||||
'Date completed' => 'Fecha terminada',
|
||||
'Id' => 'Identificador',
|
||||
'No task' => 'Ninguna tarea',
|
||||
'Completed tasks' => 'Tareas terminadas',
|
||||
'List of projects' => 'Lista de los proyectos',
|
||||
'Completed tasks for "%s"' => 'Tarea completada por « %s »',
|
||||
'%d closed tasks' => '%d tareas completadas',
|
||||
'no task for this project' => 'ninguna tarea para este proyecto',
|
||||
'Public link' => 'Enlace publico',
|
||||
'There is no column in your project!' => '¡No hay ninguna columna para este proyecto!',
|
||||
'Change assignee' => 'Cambiar la persona asignada',
|
||||
'Change assignee for the task "%s"' => 'Cambiar la persona asignada por la tarea « %s »',
|
||||
'Timezone' => 'Zona horaria',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Lo siento no he encontrado información en la base de datos!',
|
||||
'Page not found' => 'Pagina no encontrada',
|
||||
'Story Points' => 'Complejidad',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Número máximo de tareas',
|
||||
'This value must be greater than %d' => 'Este valor no debe ser más grande que %d',
|
||||
'Edit project access list' => 'Editar los permisos del proyecto',
|
||||
'Edit users access' => 'Editar los permisos de usuario',
|
||||
'Allow this user' => 'Autorizar este usuario',
|
||||
'Project access list for "%s"' => 'Permisos del proyecto « %s »',
|
||||
'Only those users have access to this project:' => 'Solo estos usuarios tienen acceso a este proyecto:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'No olvide que los administradores tienen acceso a todo',
|
||||
'revoke' => 'revocar',
|
||||
'List of authorized users' => 'Lista de los usuarios autorizados',
|
||||
'User' => 'Usuario',
|
||||
'Everybody have access to this project.' => 'Todo el mundo tiene acceso al proyecto.',
|
||||
'You are not allowed to access to this project.' => 'No esta autorizado a acceder a este proyecto.',
|
||||
'Comments' => 'Comentarios',
|
||||
'Post comment' => 'Commentar',
|
||||
'Write your text in Markdown' => 'Redacta el texto en Markdown',
|
||||
'Leave a comment' => 'Dejar un comentario',
|
||||
'Comment is required' => 'El comentario es obligatorio',
|
||||
'Leave a description' => 'Dejar una descripción',
|
||||
'Comment added successfully.' => 'El comentario ha sido añadido.',
|
||||
'Unable to create your comment.' => 'No se puede crear este comentario.',
|
||||
'The description is required' => 'La descripción es obligatoria',
|
||||
'Edit this task' => 'Editar esta tarea',
|
||||
'Due Date' => 'Fecha límite',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'día/mes/año', // Help shown to the user
|
||||
'Invalid date' => 'Fecha no valida',
|
||||
'Must be done before %B %e, %G' => 'Debe estar hecho antes del %d/%m/%Y',
|
||||
'%B %e, %G' => '%d/%m/%Y',
|
||||
'Automatic actions' => 'Acciones automatizadas',
|
||||
'Your automatic action have been created successfully.' => 'La acción automatizada ha sido creada',
|
||||
'Unable to create your automatic action.' => 'No se puede crear esta acción automatizada.',
|
||||
'Remove an action' => 'Suprimir una acción',
|
||||
'Unable to remove this action.' => 'No se puede suprimir esta accción',
|
||||
'Action removed successfully.' => 'La acción ha sido borrada.',
|
||||
'Automatic actions for the project "%s"' => 'Acciones automatizadas para este proyecto « %s »',
|
||||
'Defined actions' => 'Acciones definidas',
|
||||
'Event name' => 'Nombre del evento',
|
||||
'Action name' => 'Nombre de la acción',
|
||||
'Action parameters' => 'Parámetros de la acción',
|
||||
'Action' => 'Acción',
|
||||
'Event' => 'Evento',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Cuando el evento seleccionado ocurre ejecutar la acción correspondiente.',
|
||||
'Next step' => 'Etapa siguiente',
|
||||
'Define action parameters' => 'Definición de los parametros de la acción',
|
||||
'Save this action' => 'Guardar esta acción',
|
||||
'Do you really want to remove this action: "%s"?' => '¿Realmente desea suprimir esta acción « %s » ?',
|
||||
'Remove an automatic action' => 'Suprimir una acción automatizada',
|
||||
'Close the task' => 'Cerrar esta tarea',
|
||||
'Assign the task to a specific user' => 'Asignar una tarea a un usuario especifico',
|
||||
'Assign the task to the person who does the action' => 'Asignar la tarea al usuario que hace la acción',
|
||||
'Duplicate the task to another project' => 'Duplicar la tarea a otro proyecto',
|
||||
'Move a task to another column' => 'Mover una tarea a otra columna',
|
||||
'Move a task to another position in the same column' => 'Mover una tarea a otra posición en la misma columna',
|
||||
'Task modification' => 'Modificación de una tarea',
|
||||
'Task creation' => 'Creación de una tarea',
|
||||
'Open a closed task' => 'Abrir una tarea cerrada',
|
||||
'Closing a task' => 'Cerrar una tarea',
|
||||
'Assign a color to a specific user' => 'Asignar un color a un usuario especifico',
|
||||
'Column title' => 'Titulo de la columna',
|
||||
'Position' => 'Posición',
|
||||
'Move Up' => 'Mover hacia arriba',
|
||||
'Move Down' => 'Mover hacia abajo',
|
||||
'Duplicate to another project' => 'Duplicar a otro proyecto',
|
||||
'Duplicate' => 'Duplicar',
|
||||
'link' => 'enlace',
|
||||
'Update this comment' => 'Actualizar este comentario',
|
||||
'Comment updated successfully.' => 'El comentario ha sido actualizado.',
|
||||
'Unable to update your comment.' => 'No se puede actualizar este comentario.',
|
||||
'Remove a comment' => 'Suprimir un comentario',
|
||||
'Comment removed successfully.' => 'El comentario ha sido suprimido.',
|
||||
'Unable to remove this comment.' => 'No se puede suprimir este comentario.',
|
||||
'Do you really want to remove this comment?' => '¿Desea suprimir este comentario?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Solo los administradores o el autor del comentario tienen acceso a esta pagina.',
|
||||
'Details' => 'Detalles',
|
||||
'Current password for the user "%s"' => 'Contraseña actual para el usuario: « %s »',
|
||||
'The current password is required' => 'La contraseña es obligatoria',
|
||||
'Wrong password' => 'contraseña incorrecta',
|
||||
'Reset all tokens' => 'Reiniciar los identificadores (tokens) de seguridad ',
|
||||
'All tokens have been regenerated.' => 'Todos los identificadores (tokens) han sido reiniciados.',
|
||||
// 'Unknown' => '',
|
||||
// 'Last logins' => '',
|
||||
// 'Login date' => '',
|
||||
// 'Authentication method' => '',
|
||||
// 'IP address' => '',
|
||||
// 'User agent' => '',
|
||||
// 'Persistent connections' => '',
|
||||
// 'No session' => '',
|
||||
// 'Expiration date' => '',
|
||||
// 'Remember Me' => '',
|
||||
// 'Creation date' => '',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => ',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
// 'Search' => '',
|
||||
// 'Nothing found.' => '',
|
||||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
// 'Description' => '',
|
||||
// '%d comments' => '',
|
||||
// '%d comment' => '',
|
||||
// 'Email address invalid' => '',
|
||||
// 'Your Google Account is not linked anymore to your profile.' => '',
|
||||
// 'Unable to unlink your Google Account.' => '',
|
||||
// 'Google authentication failed' => '',
|
||||
// 'Unable to link your Google Account.' => '',
|
||||
// 'Your Google Account is linked to your profile successfully.' => '',
|
||||
// 'Email' => '',
|
||||
// 'Link my Google Account' => '',
|
||||
// 'Unlink my Google Account' => '',
|
||||
// 'Login with my Google Account' => '',
|
||||
// 'Project not found.' => '',
|
||||
// 'Task #%d' => '',
|
||||
// 'Task removed successfully.' => '',
|
||||
// 'Unable to remove this task.' => '',
|
||||
// 'Remove a task' => '',
|
||||
// 'Do you really want to remove this task: "%s"?' => '',
|
||||
// 'Assign a color to a specific category' => '',
|
||||
// 'Task creation or modification' => '',
|
||||
// 'Category' => '',
|
||||
// 'Category:' => '',
|
||||
// 'Categories' => '',
|
||||
// 'Category not found.' => '',
|
||||
// 'Your category have been created successfully.' => '',
|
||||
// 'Unable to create your category.' => '',
|
||||
// 'Your category have been updated successfully.' => '',
|
||||
// 'Unable to update your category.' => '',
|
||||
// 'Remove a category' => '',
|
||||
// 'Category removed successfully.' => '',
|
||||
// 'Unable to remove this category.' => '',
|
||||
// 'Category modification for the project "%s"' => '',
|
||||
// 'Category Name' => '',
|
||||
// 'Categories for the project "%s"' => '',
|
||||
// 'Add a new category' => '',
|
||||
// 'Do you really want to remove this category: "%s"?' => '',
|
||||
// 'Filter by category' => '',
|
||||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
);
|
||||
334
app/Locales/fr_FR/translations.php
Normal file
334
app/Locales/fr_FR/translations.php
Normal file
@@ -0,0 +1,334 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Anglais',
|
||||
'French' => 'Français',
|
||||
'Polish' => 'Polonais',
|
||||
'Portuguese (Brazilian)' => 'Portugais (Brésil)',
|
||||
'Spanish' => 'Espagnol',
|
||||
'None' => 'Aucun',
|
||||
'edit' => 'modifier',
|
||||
'Edit' => 'Modifier',
|
||||
'remove' => 'supprimer',
|
||||
'Remove' => 'Supprimer',
|
||||
'Update' => 'Mettre à jour',
|
||||
'Yes' => 'Oui',
|
||||
'No' => 'Non',
|
||||
'cancel' => 'annuler',
|
||||
'or' => 'ou',
|
||||
'Yellow' => 'Jaune',
|
||||
'Blue' => 'Bleu',
|
||||
'Green' => 'Vert',
|
||||
'Purple' => 'Violet',
|
||||
'Red' => 'Rouge',
|
||||
'Orange' => 'Orange',
|
||||
'Grey' => 'Gris',
|
||||
'Save' => 'Enregistrer',
|
||||
'Login' => 'Connexion',
|
||||
'Official website:' => 'Site web officiel :',
|
||||
'Unassigned' => 'Non assigné',
|
||||
'View this task' => 'Voir cette tâche',
|
||||
'Remove user' => 'Supprimer un utilisateur',
|
||||
'Do you really want to remove this user: "%s"?' => 'Voulez-vous vraiment supprimer cet utilisateur : « %s » ?',
|
||||
'New user' => 'Ajouter un utilisateur',
|
||||
'All users' => 'Tous les utilisateurs',
|
||||
'Username' => 'Identifiant',
|
||||
'Password' => 'Mot de passe',
|
||||
'Default Project' => 'Projet par défaut',
|
||||
'Administrator' => 'Administrateur',
|
||||
'Sign in' => 'Connexion',
|
||||
'Users' => 'Utilisateurs',
|
||||
'No user' => 'Aucun utilisateur',
|
||||
'Forbidden' => 'Accès interdit',
|
||||
'Access Forbidden' => 'Accès interdit',
|
||||
'Only administrators can access to this page.' => 'Uniquement les administrateurs peuvent accéder à cette page.',
|
||||
'Edit user' => 'Modifier un utilisateur',
|
||||
'Logout' => 'Déconnexion',
|
||||
'Bad username or password' => 'Identifiant ou mot de passe incorrect',
|
||||
'users' => 'utilisateurs',
|
||||
'projects' => 'projets',
|
||||
'Edit project' => 'Modifier le projet',
|
||||
'Name' => 'Nom',
|
||||
'Activated' => 'Actif',
|
||||
'Projects' => 'Projets',
|
||||
'No project' => 'Aucun projet',
|
||||
'Project' => 'Projet',
|
||||
'Status' => 'État',
|
||||
'Tasks' => 'Tâches',
|
||||
'Board' => 'Tableau',
|
||||
'Inactive' => 'Inactif',
|
||||
'Active' => 'Actif',
|
||||
'Column %d' => 'Colonne %d',
|
||||
'Add this column' => 'Ajouter cette colonne',
|
||||
'%d tasks on the board' => '%d tâches sur le tableau',
|
||||
'%d tasks in total' => '%d tâches au total',
|
||||
'Unable to update this board.' => 'Impossible de mettre à jour ce tableau.',
|
||||
'Edit board' => 'Modifier le tableau',
|
||||
'Disable' => 'Désactiver',
|
||||
'Enable' => 'Activer',
|
||||
'New project' => 'Nouveau projet',
|
||||
'Do you really want to remove this project: "%s"?' => 'Voulez-vous vraiment supprimer ce projet : « %s » ?',
|
||||
'Remove project' => 'Supprimer le projet',
|
||||
'Boards' => 'Tableaux',
|
||||
'Edit the board for "%s"' => 'Modifier le tableau pour « %s »',
|
||||
'All projects' => 'Tous les projets',
|
||||
'Change columns' => 'Changer les colonnes',
|
||||
'Add a new column' => 'Ajouter une nouvelle colonne',
|
||||
'Title' => 'Titre',
|
||||
'Add Column' => 'Nouvelle colonne',
|
||||
'Project "%s"' => 'Projet « %s »',
|
||||
'Nobody assigned' => 'Personne assigné',
|
||||
'Assigned to %s' => 'Assigné à %s',
|
||||
'Remove a column' => 'Supprimer une colonne',
|
||||
'Remove a column from a board' => 'Supprimer une colonne d\'un tableau',
|
||||
'Unable to remove this column.' => 'Impossible de supprimer cette colonne.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Voulez vraiment supprimer cette colonne : « %s » ?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Cette action va supprimer toutes les tâches associées à cette colonne !',
|
||||
'Settings' => 'Préférences',
|
||||
'Application settings' => 'Paramètres de l\'application',
|
||||
'Language' => 'Langue',
|
||||
'Webhooks token:' => 'Jeton de securité pour les webhooks :',
|
||||
'More information' => 'Plus d\'informations',
|
||||
'Database size:' => 'Taille de la base de données :',
|
||||
'Download the database' => 'Télécharger la base de données',
|
||||
'Optimize the database' => 'Optimiser la base de données',
|
||||
'(VACUUM command)' => '(Commande VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(Fichier Sqlite compressé en Gzip)',
|
||||
'User settings' => 'Paramètres utilisateur',
|
||||
'My default project:' => 'Mon projet par défaut : ',
|
||||
'Close a task' => 'Fermer une tâche',
|
||||
'Do you really want to close this task: "%s"?' => 'Voulez-vous vraiment fermer cettre tâche : « %s » ?',
|
||||
'Edit a task' => 'Modifier une tâche',
|
||||
'Column' => 'Colonne',
|
||||
'Color' => 'Couleur',
|
||||
'Assignee' => 'Personne assignée',
|
||||
'Create another task' => 'Créer une autre tâche',
|
||||
'New task' => 'Nouvelle tâche',
|
||||
'Open a task' => 'Ouvrir une tâche',
|
||||
'Do you really want to open this task: "%s"?' => 'Voulez-vous vraiment ouvrir cette tâche : « %s » ?',
|
||||
'Back to the board' => 'Retour au tableau',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Créé le %d/%m/%Y à %H:%M',
|
||||
'There is nobody assigned' => 'Il n\'y a personne d\'assigné à cette tâche',
|
||||
'Column on the board:' => 'Colonne sur le tableau : ',
|
||||
'Status is open' => 'État ouvert',
|
||||
'Status is closed' => 'État fermé',
|
||||
'Close this task' => 'Fermer cette tâche',
|
||||
'Open this task' => 'Ouvrir cette tâche',
|
||||
'There is no description.' => 'Il n\'y a pas de description.',
|
||||
'Add a new task' => 'Ajouter une nouvelle tâche',
|
||||
'The username is required' => 'Le nom d\'utilisateur est obligatoire',
|
||||
'The maximum length is %d characters' => 'La longueur maximale est de %d caractères',
|
||||
'The minimum length is %d characters' => 'La longueur minimale est de %d caractères',
|
||||
'The password is required' => 'Le mot de passe est obligatoire',
|
||||
'This value must be an integer' => 'Cette valeur doit être un entier',
|
||||
'The username must be unique' => 'Le nom d\'utilisateur doit être unique',
|
||||
'The username must be alphanumeric' => 'Le nom d\'utilisateur doit être alpha-numérique',
|
||||
'The user id is required' => 'L\'id de l\'utilisateur est obligatoire',
|
||||
'Passwords doesn\'t matches' => 'Les mots de passe ne correspondent pas',
|
||||
'The confirmation is required' => 'Le confirmation est requise',
|
||||
'The column is required' => 'La colonne est obligatoire',
|
||||
'The project is required' => 'Le projet est obligatoire',
|
||||
'The color is required' => 'La couleur est obligatoire',
|
||||
'The id is required' => 'L\'identifiant est obligatoire',
|
||||
'The project id is required' => 'L\'identifiant du projet est obligatoire',
|
||||
'The project name is required' => 'Le nom du projet est obligatoire',
|
||||
'This project must be unique' => 'Le nom du projet doit être unique',
|
||||
'The title is required' => 'Le titre est obligatoire',
|
||||
'The language is required' => 'La langue est obligatoire',
|
||||
'There is no active project, the first step is to create a new project.' => 'Il n\'y a aucun projet actif, la première étape est de créer un nouveau projet.',
|
||||
'Settings saved successfully.' => 'Paramètres sauvegardés avec succès.',
|
||||
'Unable to save your settings.' => 'Impossible de sauvegarder vos réglages.',
|
||||
'Database optimization done.' => 'Optmisation de la base de données terminée.',
|
||||
'Your project have been created successfully.' => 'Votre projet a été créé avec succès.',
|
||||
'Unable to create your project.' => 'Impossible de créer un projet.',
|
||||
'Project updated successfully.' => 'Votre projet a été mis à jour avec succès.',
|
||||
'Unable to update this project.' => 'Impossible de mettre à jour ce projet.',
|
||||
'Unable to remove this project.' => 'Impossible de supprimer ce projet.',
|
||||
'Project removed successfully.' => 'Votre projet a été supprimé avec succès.',
|
||||
'Project activated successfully.' => 'Votre projet a été activé avec succès.',
|
||||
'Unable to activate this project.' => 'Impossible d\'activer ce projet.',
|
||||
'Project disabled successfully.' => 'Votre projet a été désactivé avec succès.',
|
||||
'Unable to disable this project.' => 'Impossible de désactiver ce projet.',
|
||||
'Unable to open this task.' => 'Impossible d\'ouvrir cette tâche.',
|
||||
'Task opened successfully.' => 'Tâche ouverte avec succès.',
|
||||
'Unable to close this task.' => 'Impossible de fermer cette tâche.',
|
||||
'Task closed successfully.' => 'Tâche fermé avec succès.',
|
||||
'Unable to update your task.' => 'Impossible de modifier cette tâche.',
|
||||
'Task updated successfully.' => 'Tâche mise à jour avec succès.',
|
||||
'Unable to create your task.' => 'Impossible de créer cette tâche.',
|
||||
'Task created successfully.' => 'Tâche créée avec succès.',
|
||||
'User created successfully.' => 'Utilisateur créé avec succès.',
|
||||
'Unable to create your user.' => 'Impossible de créer cet utilisateur.',
|
||||
'User updated successfully.' => 'Utilisateur mis à jour avec succès.',
|
||||
'Unable to update your user.' => 'Impossible de mettre à jour cet utilisateur.',
|
||||
'User removed successfully.' => 'Utilisateur supprimé avec succès.',
|
||||
'Unable to remove this user.' => 'Impossible de supprimer cet utilisateur.',
|
||||
'Board updated successfully.' => 'Tableau mis à jour avec succès.',
|
||||
'Ready' => 'Prêt',
|
||||
'Backlog' => 'En attente',
|
||||
'Work in progress' => 'En cours',
|
||||
'Done' => 'Terminé',
|
||||
'Application version:' => 'Version de l\'application :',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Terminé le %d/%m/%Y à %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d/%m/%Y à %H:%M',
|
||||
'Date created' => 'Date de création',
|
||||
'Date completed' => 'Date de clôture',
|
||||
'Id' => 'Identifiant',
|
||||
'No task' => 'Aucune tâche',
|
||||
'Completed tasks' => 'Tâches terminées',
|
||||
'List of projects' => 'Liste des projets',
|
||||
'Completed tasks for "%s"' => 'Tâches terminées pour « %s »',
|
||||
'%d closed tasks' => '%d tâches terminées',
|
||||
'no task for this project' => 'aucune tâche pour ce projet',
|
||||
'Public link' => 'Accès public',
|
||||
'There is no column in your project!' => 'Il n\'y a aucune colonne dans votre projet !',
|
||||
'Change assignee' => 'Changer la personne assignée',
|
||||
'Change assignee for the task "%s"' => 'Changer la personne assignée pour la tâche « %s »',
|
||||
'Timezone' => 'Fuseau horaire',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Désolé, je n\'ai pas trouvé cette information dans ma base de données !',
|
||||
'Page not found' => 'Page introuvable',
|
||||
'Story Points' => 'Complexité',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Nombre maximum 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',
|
||||
'Allow this user' => 'Autoriser cet utilisateur',
|
||||
'Project access list for "%s"' => 'Liste des accès au projet « %s »',
|
||||
'Only those users have access to this project:' => 'Seulement ces utilisateurs ont accès à ce projet :',
|
||||
'Don\'t forget that administrators have access to everything.' => 'N\'oubliez pas que les administrateurs ont accès à tout.',
|
||||
'revoke' => 'révoquer',
|
||||
'List of authorized users' => 'Liste des utilisateurs autorisés',
|
||||
'User' => 'Utilisateur',
|
||||
'Everybody have access to this project.' => 'Tout le monde a accès au projet.',
|
||||
'You are not allowed to access to this project.' => 'Vous n\'êtes pas autorisé à accéder à ce projet.',
|
||||
'Comments' => 'Commentaires',
|
||||
'Post comment' => 'Commenter',
|
||||
'Write your text in Markdown' => 'Écrivez votre texte en Markdown',
|
||||
'Leave a comment' => 'Laissez un commentaire',
|
||||
'Comment is required' => 'Le commentaire est obligatoire',
|
||||
'Leave a description' => 'Laissez une description',
|
||||
'Comment added successfully.' => 'Commentaire ajouté avec succès.',
|
||||
'Unable to create your comment.' => 'Impossible de sauvegarder votre commentaire.',
|
||||
'The description is required' => 'La description est obligatoire',
|
||||
'Edit this task' => 'Modifier cette tâche',
|
||||
'Due Date' => 'Date d\'échéance',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'jour/mois/année', // Help shown to the user
|
||||
'Invalid date' => 'Date invalide',
|
||||
'Must be done before %B %e, %G' => 'Doit être fait avant le %d/%m/%Y',
|
||||
'%B %e, %G' => '%d/%m/%Y',
|
||||
'Automatic actions' => 'Actions automatisées',
|
||||
'Your automatic action have been created successfully.' => 'Votre action automatisée a été ajouté avec succès.',
|
||||
'Unable to create your automatic action.' => 'Impossible de créer votre action automatisée.',
|
||||
'Remove an action' => 'Supprimer une action',
|
||||
'Unable to remove this action.' => 'Impossible de supprimer cette action',
|
||||
'Action removed successfully.' => 'Action supprimée avec succès.',
|
||||
'Automatic actions for the project "%s"' => 'Actions automatisées pour le projet « %s »',
|
||||
'Defined actions' => 'Actions définies',
|
||||
'Event name' => 'Nom de l\'événement',
|
||||
'Action name' => 'Nom de l\'action',
|
||||
'Action parameters' => 'Paramètres de l\'action',
|
||||
'Action' => 'Action',
|
||||
'Event' => 'Événement',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Lorsque l\'événement sélectionné se déclenche, executer l\'action correspondante.',
|
||||
'Next step' => 'Étape suivante',
|
||||
'Define action parameters' => 'Définition des paramètres de l\'action',
|
||||
'Save this action' => 'Sauvegarder cette action',
|
||||
'Do you really want to remove this action: "%s"?' => 'Voulez-vous vraiment supprimer cette action « %s » ?',
|
||||
'Remove an automatic action' => 'Supprimer une action automatisée',
|
||||
'Close the task' => 'Fermer cette tâche',
|
||||
'Assign the task to a specific user' => 'Assigner la tâche à un utilisateur spécifique',
|
||||
'Assign the task to the person who does the action' => 'Assigner la tâche à la personne qui fait l\'action',
|
||||
'Duplicate the task to another project' => 'Dupliquer la tâche vers un autre projet',
|
||||
'Move a task to another column' => 'Déplacement d\'une tâche vers un autre colonne',
|
||||
'Move a task to another position in the same column' => 'Déplacement d\'une tâche à une autre position mais dans la même colonne',
|
||||
'Task modification' => 'Modification d\'une tâche',
|
||||
'Task creation' => 'Création d\'une tâche',
|
||||
'Open a closed task' => 'Ouverture d\'une tâche fermée',
|
||||
'Closing a task' => 'Fermeture d\'une tâche',
|
||||
'Assign a color to a specific user' => 'Assigner une couleur à un utilisateur',
|
||||
'Column title' => 'Titre de la colonne',
|
||||
'Position' => 'Position',
|
||||
'Move Up' => 'Déplacer vers le haut',
|
||||
'Move Down' => 'Déplacer vers le bas',
|
||||
'Duplicate to another project' => 'Dupliquer dans un autre projet',
|
||||
'Duplicate' => 'Dupliquer',
|
||||
'link' => 'lien',
|
||||
'Update this comment' => 'Mettre à jour ce commentaire',
|
||||
'Comment updated successfully.' => 'Commentaire mis à jour avec succès.',
|
||||
'Unable to update your comment.' => 'Impossible de supprimer votre commentaire.',
|
||||
'Remove a comment' => 'Supprimer un commentaire',
|
||||
'Comment removed successfully.' => 'Commentaire supprimé avec succès.',
|
||||
'Unable to remove this comment.' => 'Impossible de supprimer ce commentaire.',
|
||||
'Do you really want to remove this comment?' => 'Voulez-vous vraiment supprimer ce commentaire ?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Uniquement les administrateurs ou le créateur du commentaire peuvent accéder à cette page.',
|
||||
'Details' => 'Détails',
|
||||
'Current password for the user "%s"' => 'Mot de passe actuel pour l\'utilisateur « %s »',
|
||||
'The current password is required' => 'Le mot de passe actuel est obligatoire',
|
||||
'Wrong password' => 'Mauvais mot de passe',
|
||||
'Reset all tokens' => 'Réinitialiser tous les jetons de sécurité',
|
||||
'All tokens have been regenerated.' => 'Tous les jetons de sécurité ont été réinitialisés.',
|
||||
'Unknown' => 'Inconnu',
|
||||
'Last logins' => 'Dernières connexions',
|
||||
'Login date' => 'Date de connexion',
|
||||
'Authentication method' => 'Méthode d\'authentification',
|
||||
'IP address' => 'Adresse IP',
|
||||
'User agent' => 'Agent utilisateur',
|
||||
'Persistent connections' => 'Connexions persistantes',
|
||||
'No session' => 'Aucune session',
|
||||
'Expiration date' => 'Date d\'expiration',
|
||||
'Remember Me' => 'Connexion automatique',
|
||||
'Creation date' => 'Date de création',
|
||||
'Filter by user' => 'Filtrer par utilisateur',
|
||||
'Filter by due date' => 'Filtrer par date d\'échéance',
|
||||
'Everybody' => 'Tout le monde',
|
||||
'Open' => 'Ouvert',
|
||||
'Closed' => 'Fermé',
|
||||
'Search' => 'Rechercher',
|
||||
'Nothing found.' => 'Rien trouvé.',
|
||||
'Search in the project "%s"' => 'Rechercher dans le projet « %s »',
|
||||
'Due date' => 'Date d\'échéance',
|
||||
'Others formats accepted: %s and %s' => 'Autres formats acceptés : %s et %s',
|
||||
'Description' => 'Description',
|
||||
'%d comments' => '%d commentaires',
|
||||
'%d comment' => '%d commentaire',
|
||||
'Email address invalid' => 'Adresse email invalide',
|
||||
'Your Google Account is not linked anymore to your profile.' => 'Votre compte Google n\'est plus relié à votre profile.',
|
||||
'Unable to unlink your Google Account.' => 'Impossible de supprimer votre compte Google.',
|
||||
'Google authentication failed' => 'Authentification Google échouée',
|
||||
'Unable to link your Google Account.' => 'Impossible de lier votre compte Google.',
|
||||
'Your Google Account is linked to your profile successfully.' => 'Votre compte Google est désormais lié à votre profile.',
|
||||
'Email' => 'Email',
|
||||
'Link my Google Account' => 'Lier mon compte Google',
|
||||
'Unlink my Google Account' => 'Ne plus utiliser mon compte Google',
|
||||
'Login with my Google Account' => 'Se connecter avec mon compte Google',
|
||||
'Project not found.' => 'Projet introuvable.',
|
||||
'Task #%d' => 'Tâche n°%d',
|
||||
'Task removed successfully.' => 'Tâche supprimée avec succès.',
|
||||
'Unable to remove this task.' => 'Impossible de supprimer cette tâche.',
|
||||
'Remove a task' => 'Supprimer une tâche',
|
||||
'Do you really want to remove this task: "%s"?' => 'Voulez-vous vraiment supprimer cette tâche « %s » ?',
|
||||
'Assign a color to a specific category' => 'Assigner une couleur à une catégorie spécifique',
|
||||
'Task creation or modification' => 'Création ou modification d\'une tâche',
|
||||
'Category' => 'Catégorie',
|
||||
'Category:' => 'Catégorie :',
|
||||
'Categories' => 'Catégories',
|
||||
'Category not found.' => 'Catégorie introuvable',
|
||||
'Your category have been created successfully.' => 'Votre catégorie a été créé avec succès.',
|
||||
'Unable to create your category.' => 'Impossible de créer votre catégorie.',
|
||||
'Your category have been updated successfully.' => 'Votre catégorie a été mise à jour avec succès.',
|
||||
'Unable to update your category.' => 'Impossible de mettre à jour votre catégorie.',
|
||||
'Remove a category' => 'Supprimer une catégorie',
|
||||
'Category removed successfully.' => 'Catégorie supprimée avec succès.',
|
||||
'Unable to remove this category.' => 'Impossible de supprimer cette catégorie.',
|
||||
'Category modification for the project "%s"' => 'Modification d\'une catégorie pour le projet « %s »',
|
||||
'Category Name' => 'Nom de la catégorie',
|
||||
'Categories for the project "%s"' => 'Catégories du projet « %s »',
|
||||
'Add a new category' => 'Ajouter une nouvelle catégorie',
|
||||
'Do you really want to remove this category: "%s"?' => 'Voulez-vous vraiment supprimer cette catégorie « %s » ?',
|
||||
'Filter by category' => 'Filtrer par catégorie',
|
||||
'All categories' => 'Toutes les catégories',
|
||||
'No category' => 'Aucune catégorie',
|
||||
'The name is required' => 'Le nom est requis',
|
||||
);
|
||||
339
app/Locales/pl_PL/translations.php
Normal file
339
app/Locales/pl_PL/translations.php
Normal file
@@ -0,0 +1,339 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'angielski',
|
||||
'French' => 'francuski',
|
||||
'Polish' => 'polski',
|
||||
'Portuguese (Brazilian)' => 'Portugalski (brazylijski)',
|
||||
'Spanish' => 'Hiszpański',
|
||||
'None' => 'Brak',
|
||||
'edit' => 'edytuj',
|
||||
'Edit' => 'Edytuj',
|
||||
'remove' => 'usuń',
|
||||
'Remove' => 'Usuń',
|
||||
'Update' => 'Aktualizuj',
|
||||
'Yes' => 'Tak',
|
||||
'No' => 'Nie',
|
||||
'cancel' => 'anuluj',
|
||||
'or' => 'lub',
|
||||
'Yellow' => 'Żółty',
|
||||
'Blue' => 'Niebieski',
|
||||
'Green' => 'Zielony',
|
||||
'Purple' => 'Fioletowy',
|
||||
'Red' => 'Czerwony',
|
||||
'Orange' => 'Pomarańczowy',
|
||||
'Grey' => 'Szary',
|
||||
'Save' => 'Zapisz',
|
||||
'Login' => 'Login',
|
||||
'Official website:' => 'Oficjalna strona:',
|
||||
'Unassigned' => 'Nieprzypisany',
|
||||
'View this task' => 'Zobacz zadanie',
|
||||
'Remove user' => 'Usuń użytkownika',
|
||||
'Do you really want to remove this user: "%s"?' => 'Na pewno chcesz usunąć użytkownika: "%s"?',
|
||||
'New user' => 'Nowy użytkownik',
|
||||
'All users' => 'Wszyscy użytkownicy',
|
||||
'Username' => 'Nazwa użytkownika',
|
||||
'Password' => 'Hasło',
|
||||
'Default Project' => 'Domyślny projekt',
|
||||
'Administrator' => 'Administrator',
|
||||
'Sign in' => 'Zaloguj',
|
||||
'Users' => 'Użytkownicy',
|
||||
'No user' => 'Brak użytkowników',
|
||||
'Forbidden' => 'Zabroniony',
|
||||
'Access Forbidden' => 'Dostęp zabroniony',
|
||||
'Only administrators can access to this page.' => 'Tylko administrator może wejść na tą stronę.',
|
||||
'Edit user' => 'Edytuj użytkownika',
|
||||
'Logout' => 'Wyloguj',
|
||||
'Bad username or password' => 'Zła nazwa uyżytkownika lub hasło',
|
||||
'users' => 'użytkownicy',
|
||||
'projects' => 'projekty',
|
||||
'Edit project' => 'Edytuj projekt',
|
||||
'Name' => 'Nazwa',
|
||||
'Activated' => 'Aktywny',
|
||||
'Projects' => 'Projekty',
|
||||
'No project' => 'Brak projektów',
|
||||
'Project' => 'Projekt',
|
||||
'Status' => 'Status',
|
||||
'Tasks' => 'Zadania',
|
||||
'Board' => 'Tablica',
|
||||
'Inactive' => 'Nieaktywny',
|
||||
'Active' => 'Aktywny',
|
||||
'Column %d' => 'Kolumna %d',
|
||||
'Add this column' => 'Dodaj kolumnę',
|
||||
'%d tasks on the board' => '%d zadań na tablicy',
|
||||
'%d tasks in total' => '%d wszystkich zadań',
|
||||
'Unable to update this board.' => 'Nie można zaktualizować tablicy.',
|
||||
'Edit board' => 'Edytuj tablicę',
|
||||
'Disable' => 'Wyłącz',
|
||||
'Enable' => 'Włącz',
|
||||
'New project' => 'Nowy projekt',
|
||||
'Do you really want to remove this project: "%s"?' => 'Na pewno chcesz usunąć projekt: "%s"?',
|
||||
'Remove project' => 'Usuń projekt',
|
||||
'Boards' => 'Tablice',
|
||||
'Edit the board for "%s"' => 'Edytuj tablię dla "%s"',
|
||||
'All projects' => 'Wszystkie projekty',
|
||||
'Change columns' => 'Zmień kolumny',
|
||||
'Add a new column' => 'Dodaj nową kolumnę',
|
||||
'Title' => 'Tytuł',
|
||||
'Add Column' => 'Dodaj kolumnę',
|
||||
'Project "%s"' => 'Projekt "%s"',
|
||||
'Nobody assigned' => 'Nikt nie przypisany',
|
||||
'Assigned to %s' => 'Przypisane do %s',
|
||||
'Remove a column' => 'Usuń kolumnę',
|
||||
'Remove a column from a board' => 'Usuń kolumnę z tablicy',
|
||||
'Unable to remove this column.' => 'Nie udało się usunąć kolumny.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Na pewno chcesz usunąć kolumnę: "%s"?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Wszystkie zadania w kolumnie zostaną usunięte!',
|
||||
'Settings' => 'Ustawienia',
|
||||
'Application settings' => 'Ustawienia aplikacji',
|
||||
'Language' => 'Język',
|
||||
'Webhooks token:' => 'Token :',
|
||||
'More information' => 'Więcej informacji',
|
||||
'Database size:' => 'Rozmiar bazy danych :',
|
||||
'Download the database' => 'Pobierz bazę danych',
|
||||
'Optimize the database' => 'Optymalizuj bazę danych',
|
||||
'(VACUUM command)' => '(komenda VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(baza danych spakowana Gzip)',
|
||||
'User settings' => 'Ustawienia użytkownika',
|
||||
'My default project:' => 'Mój domyślny projekt:',
|
||||
'Close a task' => 'Zakończ zadanie',
|
||||
'Do you really want to close this task: "%s"?' => 'Na pewno chcesz zakończyć to zadanie: "%s"?',
|
||||
'Edit a task' => 'Edytuj zadanie',
|
||||
'Column' => 'Kolumna',
|
||||
'Color' => 'Kolor',
|
||||
'Assignee' => 'Odpowiedzialny',
|
||||
'Create another task' => 'Dodaj kolejne zadanie',
|
||||
'New task' => 'Nowe zadanie',
|
||||
'Open a task' => 'Otwórz zadanie',
|
||||
'Do you really want to open this task: "%s"?' => 'Na pewno chcesz otworzyć zadanie: "%s"?',
|
||||
'Back to the board' => 'Powrót do tablicy',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Utworzono dnia %e %B %G o %k:%M',
|
||||
'There is nobody assigned' => 'Nikt nie jest przypisany',
|
||||
'Column on the board:' => 'Kolumna na tablicy:',
|
||||
'Status is open' => 'Status otwarty',
|
||||
'Status is closed' => 'Status zamknięty',
|
||||
'Close this task' => 'Zamknij zadanie',
|
||||
'Open this task' => 'Otwórz zadanie',
|
||||
'There is no description.' => 'Brak opisu.',
|
||||
'Add a new task' => 'Dodaj zadanie',
|
||||
'The username is required' => 'Nazwa użytkownika jest wymagana',
|
||||
'The maximum length is %d characters' => 'Maksymalna długość wynosi %d znaków',
|
||||
'The minimum length is %d characters' => 'Minimalna długość wynosi %d znaków',
|
||||
'The password is required' => 'Hasło jest wymagane',
|
||||
'This value must be an integer' => 'Wartość musi być liczbą całkowitą',
|
||||
'The username must be unique' => 'Nazwa użytkownika musi być unikalna',
|
||||
'The username must be alphanumeric' => 'Nazwa użytkownika musi być alfanumeryczna',
|
||||
'The user id is required' => 'ID użytkownika jest wymagane',
|
||||
'Passwords doesn\'t matches' => 'Hasła nie pasują do siebie',
|
||||
'The confirmation is required' => 'Wymagane jest potwierdzenie',
|
||||
'The column is required' => 'Kolumna jest wymagana',
|
||||
'The project is required' => 'Projekt jest wymagany',
|
||||
'The color is required' => 'Kolor jest wymagany',
|
||||
'The id is required' => 'ID jest wymagane',
|
||||
'The project id is required' => 'ID projektu jest wymagane',
|
||||
'The project name is required' => 'Nazwa projektu jest wymagana',
|
||||
'This project must be unique' => 'Projekt musi być unikalny',
|
||||
'The title is required' => 'Tutył jest wymagany',
|
||||
'The language is required' => 'Język jest wymagany',
|
||||
'There is no active project, the first step is to create a new project.' => 'Brak aktywnych projektów. Pierwszym krokiem jest utworzenie nowego projektu.',
|
||||
'Settings saved successfully.' => 'Ustawienia zapisane.',
|
||||
'Unable to save your settings.' => 'Nie udało się zapisać ustawień.',
|
||||
'Database optimization done.' => 'Optymalizacja bazy danych zakończona.',
|
||||
'Your project have been created successfully.' => 'Projekt został pomyślnie utworzony.',
|
||||
'Unable to create your project.' => 'Nie udało się stworzyć projektu.',
|
||||
'Project updated successfully.' => 'Projekt zaktualizowany.',
|
||||
'Unable to update this project.' => 'Nie można zaktualizować projektu.',
|
||||
'Unable to remove this project.' => 'Nie można usunąć projektu.',
|
||||
'Project removed successfully.' => 'Projekt usunięty.',
|
||||
'Project activated successfully.' => 'Projekt aktywowany.',
|
||||
'Unable to activate this project.' => 'Nie można aktywować projektu.',
|
||||
'Project disabled successfully.' => 'Projekt wyłączony.',
|
||||
'Unable to disable this project.' => 'Nie można wyłączyć projektu.',
|
||||
'Unable to open this task.' => 'Nie można otworzyć tego zadania.',
|
||||
'Task opened successfully.' => 'Zadanie otwarte.',
|
||||
'Unable to close this task.' => 'Nie można zamknąć tego zadania.',
|
||||
'Task closed successfully.' => 'Zadanie zamknięte.',
|
||||
'Unable to update your task.' => 'Nie można zaktualizować tego zadania.',
|
||||
'Task updated successfully.' => 'Zadanie zaktualizowane.',
|
||||
'Unable to create your task.' => 'Nie można dodać zadania.',
|
||||
'Task created successfully.' => 'Zadanie zostało utworzone.',
|
||||
'User created successfully.' => 'Użytkownik dodany',
|
||||
'Unable to create your user.' => 'Nie udało się dodać użytkownika.',
|
||||
'User updated successfully.' => 'Użytkownik zaktualizowany.',
|
||||
'Unable to update your user.' => 'Nie udało się zaktualizować użytkownika.',
|
||||
'User removed successfully.' => 'Użytkownik usunięty.',
|
||||
'Unable to remove this user.' => 'Nie udało się usunąć użytkownika.',
|
||||
'Board updated successfully.' => 'Tablica została zaktualizowana.',
|
||||
'Ready' => 'Gotowe',
|
||||
'Backlog' => 'Log',
|
||||
'Work in progress' => 'W trakcie',
|
||||
'Done' => 'Zakończone',
|
||||
'Application version:' => 'Wersja aplikacji:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Zakończono dnia %e %B %G o %k:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%e %B %G o %k:%M',
|
||||
'Date created' => 'Data utworzenia',
|
||||
'Date completed' => 'Data zakończenia',
|
||||
'Id' => 'Ident',
|
||||
'No task' => 'Brak zadań',
|
||||
'Completed tasks' => 'Ukończone zadania',
|
||||
'List of projects' => 'Lista projektów',
|
||||
'Completed tasks for "%s"' => 'Zadania zakończone dla "%s"',
|
||||
'%d closed tasks' => '%d zamkniętych zadań',
|
||||
'no task for this project' => 'brak zadań dla tego projektu',
|
||||
'Public link' => 'Link publiczny',
|
||||
'There is no column in your project!' => 'Brak kolumny w Twoim projekcie',
|
||||
'Change assignee' => 'Zmień odpowiedzialną osobę',
|
||||
'Change assignee for the task "%s"' => 'Zmień odpowiedzialną osobę dla zadania "%s"',
|
||||
'Timezone' => 'Strefa czasowa',
|
||||
'Actions' => 'Akcje',
|
||||
'Confirmation' => 'Powtórzenie hasła',
|
||||
'Description' => 'Opis',
|
||||
'Details' => 'Informacje',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Niestety nie znaleziono tej informacji w bazie danych',
|
||||
'Page not found' => 'Strona nie istnieje',
|
||||
'Story Points' => 'Poziom trudności',
|
||||
'limit' => 'limit',
|
||||
'Task limit' => 'Limit zadań',
|
||||
'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',
|
||||
'Allow this user' => 'Dodaj użytkownika',
|
||||
'Project access list for "%s"' => 'Lista uprawnionych dla projektu "%s"',
|
||||
'Only those users have access to this project:' => 'Użytkownicy mający dostęp:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Pamiętaj: Administratorzy mają zawsze dostęp do wszystkiego!',
|
||||
'revoke' => 'odbierz dostęp',
|
||||
'List of authorized users' => 'Lista użytkowników mających dostęp',
|
||||
'User' => 'Użytkownik',
|
||||
'Everybody have access to this project.' => 'Każdy ma dostęp do tego projektu.',
|
||||
'You are not allowed to access to this project.' => 'Nie masz dostępu do tego projektu.',
|
||||
'%B %e, %G at %k:%M %p' => '%e %B %G o %k:%M',
|
||||
'Comments' => 'Komentarze',
|
||||
'Post comment' => 'Dodaj komentarz',
|
||||
'Write your text in Markdown' => 'Możesz użyć Markdown',
|
||||
'Leave a comment' => 'Zostaw komentarz',
|
||||
'Comment is required' => 'Komentarz jest wymagany',
|
||||
'Comment added successfully.' => 'Komentarz dodany',
|
||||
'Unable to create your comment.' => 'Nie udało się dodać komentarza',
|
||||
'The description is required' => 'Opis jest wymagany',
|
||||
'Edit this task' => 'Edytuj zadanie',
|
||||
'Due Date' => 'Termin',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'dzień/miesiąc/rok', // Help shown to the user
|
||||
'Invalid date' => 'Błędna data',
|
||||
'Must be done before %B %e, %G' => 'Termin do %e %B %G',
|
||||
'%B %e, %G' => '%e %B %G',
|
||||
'Automatic actions' => 'Akcje automatyczne',
|
||||
'Your automatic action have been created successfully.' => 'Twoja akcja została dodana',
|
||||
'Unable to create your automatic action.' => 'Nie udało się utworzyć akcji',
|
||||
'Remove an action' => 'Usuń akcję',
|
||||
'Unable to remove this action.' => 'Nie można usunąć akcji',
|
||||
'Action removed successfully.' => 'Akcja usunięta',
|
||||
'Automatic actions for the project "%s"' => 'Akcje automatyczne dla projektu "%s"',
|
||||
'Defined actions' => 'Zdefiniowane akcje',
|
||||
'Event name' => 'Nazwa zdarzenia',
|
||||
'Action name' => 'Nazwa akcji',
|
||||
'Action parameters' => 'Parametry akcji',
|
||||
'Action' => 'Akcja',
|
||||
'Event' => 'Zdarzenie',
|
||||
'When the selected event occurs execute the corresponding action.' => 'Gdy następuje wybrane zdarzenie, uruchom odpowiednią akcję',
|
||||
'Next step' => 'Następny krok',
|
||||
'Define action parameters' => 'Zdefiniuj parametry akcji',
|
||||
'Save this action' => 'Zapisz akcję',
|
||||
'Do you really want to remove this action: "%s"?' => 'Na pewno chcesz usunąć akcję "%s"?',
|
||||
'Remove an automatic action' => 'Usuń akcję automatyczną',
|
||||
'Close the task' => 'Zamknij zadanie',
|
||||
'Assign the task to a specific user' => 'Przypisz zadanie do wybranego użytkownika',
|
||||
'Assign the task to the person who does the action' => 'Przypisz zadanie to osoby wykonującej akcję',
|
||||
'Duplicate the task to another project' => 'Kopiuj zadanie do innego projektu',
|
||||
'Move a task to another column' => 'Przeniesienie zadania do innej kolumny',
|
||||
'Move a task to another position in the same column' => 'Zmiania pozycji zadania w kolumnie',
|
||||
'Task modification' => 'Modyfikacja zadania',
|
||||
'Task creation' => 'Tworzenie zadania',
|
||||
'Open a closed task' => 'Otwarcie zamkniętego zadania',
|
||||
'Closing a task' => 'Zamknięcie zadania',
|
||||
'Assign a color to a specific user' => 'Przypisz kolor do wybranego użytkownika',
|
||||
'Add an action' => 'Nowa akcja',
|
||||
'Column title' => 'Tytuł kolumny',
|
||||
'Position' => 'Pozycja',
|
||||
'Move Up' => 'Przenieś wyżej',
|
||||
'Move Down' => 'Przenieś niżej',
|
||||
'Duplicate to another project' => 'Skopiuj do innego projektu',
|
||||
'Duplicate' => 'Utwórz kopię',
|
||||
'link' => 'link',
|
||||
'Update this comment' => 'Zapisz komentarz',
|
||||
'Comment updated successfully.' => 'Komentarz został zapisany.',
|
||||
'Unable to update your comment.' => 'Nie udało się zapisanie komentarza.',
|
||||
'Remove a comment' => 'Usuń komentarz',
|
||||
'Comment removed successfully.' => 'Komentarz został usunięty.',
|
||||
'Unable to remove this comment.' => 'Nie udało się usunąć komentarza.',
|
||||
'Do you really want to remove this comment?' => 'Czy na pewno usunąć ten komentarz?',
|
||||
'Only administrators or the creator of the comment can access to this page.' => 'Tylko administratorzy oraz autor komentarza ma dostęp do tej strony.',
|
||||
'Details' => 'Szczegóły',
|
||||
'Current password for the user "%s"' => 'Aktualne hasło dla użytkownika "%s"',
|
||||
'The current password is required' => 'Wymanage jest aktualne hasło',
|
||||
'Wrong password' => 'Błędne hasło',
|
||||
'Reset all tokens' => 'Zresetuj wszystkie tokeny',
|
||||
'All tokens have been regenerated.' => 'Wszystkie tokeny zostały zresetowane.',
|
||||
'Unknown' => 'Nieznany',
|
||||
'Last logins' => 'Ostatnie logowania',
|
||||
'Login date' => 'Data logowania',
|
||||
'Authentication method' => 'Sposób autentykacji',
|
||||
'IP address' => 'Adres IP',
|
||||
'User agent' => 'Przeglądarka',
|
||||
'Persistent connections' => 'Stałe połączenia',
|
||||
'No session' => 'Brak sesji',
|
||||
'Expiration date' => 'Data zakończenia',
|
||||
'Remember Me' => 'Pamiętaj mnie',
|
||||
'Creation date' => 'Data utworzenia',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => ',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
// 'Search' => '',
|
||||
// 'Nothing found.' => '',
|
||||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
// 'Description' => '',
|
||||
// '%d comments' => '',
|
||||
// '%d comment' => '',
|
||||
// 'Email address invalid' => '',
|
||||
// 'Your Google Account is not linked anymore to your profile.' => '',
|
||||
// 'Unable to unlink your Google Account.' => '',
|
||||
// 'Google authentication failed' => '',
|
||||
// 'Unable to link your Google Account.' => '',
|
||||
// 'Your Google Account is linked to your profile successfully.' => '',
|
||||
// 'Email' => '',
|
||||
// 'Link my Google Account' => '',
|
||||
// 'Unlink my Google Account' => '',
|
||||
// 'Login with my Google Account' => '',
|
||||
// 'Project not found.' => '',
|
||||
// 'Task #%d' => '',
|
||||
// 'Task removed successfully.' => '',
|
||||
// 'Unable to remove this task.' => '',
|
||||
// 'Remove a task' => '',
|
||||
// 'Do you really want to remove this task: "%s"?' => '',
|
||||
// 'Assign a color to a specific category' => '',
|
||||
// 'Task creation or modification' => '',
|
||||
// 'Category' => '',
|
||||
// 'Category:' => '',
|
||||
// 'Categories' => '',
|
||||
// 'Category not found.' => '',
|
||||
// 'Your category have been created successfully.' => '',
|
||||
// 'Unable to create your category.' => '',
|
||||
// 'Your category have been updated successfully.' => '',
|
||||
// 'Unable to update your category.' => '',
|
||||
// 'Remove a category' => '',
|
||||
// 'Category removed successfully.' => '',
|
||||
// 'Unable to remove this category.' => '',
|
||||
// 'Category modification for the project "%s"' => '',
|
||||
// 'Category Name' => '',
|
||||
// 'Categories for the project "%s"' => '',
|
||||
// 'Add a new category' => '',
|
||||
// 'Do you really want to remove this category: "%s"?' => '',
|
||||
// 'Filter by category' => '',
|
||||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
);
|
||||
335
app/Locales/pt_BR/translations.php
Normal file
335
app/Locales/pt_BR/translations.php
Normal file
@@ -0,0 +1,335 @@
|
||||
<?php
|
||||
|
||||
return array(
|
||||
'English' => 'Inglês',
|
||||
'French' => 'Francês',
|
||||
'Polish' => 'Polonês',
|
||||
'Portuguese (Brazilian)' => 'Português (Brasil)',
|
||||
'Spanish' => 'Espanhol',
|
||||
'None' => 'Nenhum',
|
||||
'edit' => 'editar',
|
||||
'Edit' => 'Editar',
|
||||
'remove' => 'apagar',
|
||||
'Remove' => 'Apagar',
|
||||
'Update' => 'Atualizar',
|
||||
'Yes' => 'Sim',
|
||||
'No' => 'Não',
|
||||
'cancel' => 'cancelar',
|
||||
'or' => 'ou',
|
||||
'Yellow' => 'Amarelo',
|
||||
'Blue' => 'Azul',
|
||||
'Green' => 'Verde',
|
||||
'Purple' => 'Violeta',
|
||||
'Red' => 'Vermelho',
|
||||
'Orange' => 'Laranja',
|
||||
'Grey' => 'Cinza',
|
||||
'Save' => 'Salvar',
|
||||
'Login' => 'Login',
|
||||
'Official website:' => 'Site web oficial :',
|
||||
'Unassigned' => 'Não Atribuída',
|
||||
'View this task' => 'Ver esta tarefa',
|
||||
'Remove user' => 'Remover usuário',
|
||||
'Do you really want to remove this user: "%s"?' => 'Quer realmente remover este usuário: "%s"?',
|
||||
'New user' => 'Novo usuário',
|
||||
'All users' => 'Todos os usuários',
|
||||
'Username' => 'Nome do usuário',
|
||||
'Password' => 'Senha',
|
||||
'Default Project' => 'Projeto default',
|
||||
'Administrator' => 'Administrador',
|
||||
'Sign in' => 'Logar',
|
||||
'Users' => 'Usuários',
|
||||
'No user' => 'Sem usuário',
|
||||
'Forbidden' => 'Proibido',
|
||||
'Access Forbidden' => 'Acesso negado',
|
||||
'Only administrators can access to this page.' => 'Somente administradores têm acesso a esta página.',
|
||||
'Edit user' => 'Editar usuário',
|
||||
'Logout' => 'Logout',
|
||||
'Bad username or password' => 'Usuário ou senha inválidos',
|
||||
'users' => 'usuários',
|
||||
'projects' => 'projetos',
|
||||
'Edit project' => 'Editar projeto',
|
||||
'Name' => 'Nome',
|
||||
'Activated' => 'Ativo',
|
||||
'Projects' => 'Projetos',
|
||||
'No project' => 'Nenhum projeto',
|
||||
'Project' => 'Projeto',
|
||||
'Status' => 'Status',
|
||||
'Tasks' => 'Tarefas',
|
||||
'Board' => 'Quadro',
|
||||
'Inactive' => 'Inativo',
|
||||
'Active' => 'Ativo',
|
||||
'Column %d' => 'Coluna %d',
|
||||
'Add this column' => 'Adicionar esta coluna',
|
||||
'%d tasks on the board' => '%d tarefas no quadro',
|
||||
'%d tasks in total' => '%d tarefas no total',
|
||||
'Unable to update this board.' => 'Impossível atualizar este quadro.',
|
||||
'Edit board' => 'Modificar quadro',
|
||||
'Disable' => 'Desativar',
|
||||
'Enable' => 'Ativar',
|
||||
'New project' => 'Novo projeto',
|
||||
'Do you really want to remove this project: "%s"?' => 'Quer realmente remover este projeto: "%s" ?',
|
||||
'Remove project' => 'Remover projeto',
|
||||
'Boards' => 'Quadros',
|
||||
'Edit the board for "%s"' => 'Editar o quadro para "%s"',
|
||||
'All projects' => 'Todos os projetos',
|
||||
'Change columns' => 'Modificar colunas',
|
||||
'Add a new column' => 'Adicionar uma nova coluna',
|
||||
'Title' => 'Título',
|
||||
'Add Column' => 'Adicionar coluna',
|
||||
'Project "%s"' => 'Projeto "%s"',
|
||||
'Nobody assigned' => 'Ninguém designado',
|
||||
'Assigned to %s' => 'Designado para %s',
|
||||
'Remove a column' => 'Remover uma coluna',
|
||||
'Remove a column from a board' => 'Remover uma coluna do quadro',
|
||||
'Unable to remove this column.' => 'Impossível remover esta coluna.',
|
||||
'Do you really want to remove this column: "%s"?' => 'Quer realmente remover esta coluna: "%s"?',
|
||||
'This action will REMOVE ALL TASKS associated to this column!' => 'Esta ação vai REMOVER TODAS AS TAREFAS associadas a esta coluna!',
|
||||
'Settings' => 'Preferências',
|
||||
'Application settings' => 'Preferências da aplicação',
|
||||
'Language' => 'Idioma',
|
||||
'Webhooks token:' => 'Token de webhooks:',
|
||||
'More information' => 'Mais informação',
|
||||
'Database size:' => 'Tamanho do banco de dados:',
|
||||
'Download the database' => 'Download do banco de dados',
|
||||
'Optimize the database' => 'Otimizar o banco de dados',
|
||||
'(VACUUM command)' => '(Comando VACUUM)',
|
||||
'(Gzip compressed Sqlite file)' => '(Arquivo Sqlite comprimido com Gzip)',
|
||||
'User settings' => 'Configurações do usuário',
|
||||
'My default project:' => 'Meu projeto default:',
|
||||
'Close a task' => 'Encerrar uma tarefa',
|
||||
'Do you really want to close this task: "%s"?' => 'Quer realmente encerrar esta tarefa: "%s"?',
|
||||
'Edit a task' => 'Editar uma tarefa',
|
||||
'Column' => 'Coluna',
|
||||
'Color' => 'Cor',
|
||||
'Assignee' => 'Designação',
|
||||
'Create another task' => 'Criar uma outra tarefa (aproveitando os dados preenchidos)',
|
||||
'New task' => 'Nova tarefa',
|
||||
'Open a task' => 'Abrir uma tarefa',
|
||||
'Do you really want to open this task: "%s"?' => 'Quer realmente abrir esta tarefa: "%s"?',
|
||||
'Back to the board' => 'Voltar ao quadro',
|
||||
'Created on %B %e, %G at %k:%M %p' => 'Criado em %d %B %G às %H:%M',
|
||||
'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',
|
||||
'Open this task' => 'Abrir esta tarefa',
|
||||
'There is no description.' => 'Não há descrição.',
|
||||
'Add a new task' => 'Adicionar uma nova tarefa',
|
||||
'The username is required' => 'O nome de usuário é obrigatório',
|
||||
'The maximum length is %d characters' => 'O tamanho máximo são %d caracteres',
|
||||
'The minimum length is %d characters' => 'O tamanho mínimo são %d caracteres',
|
||||
'The password is required' => 'A senha é obrigatória',
|
||||
'This value must be an integer' => 'O valor deve ser um inteiro',
|
||||
'The username must be unique' => 'O nome de usuário deve ser único',
|
||||
'The username must be alphanumeric' => 'O nome de usuário deve ser alfanumérico, sem espaços ou _',
|
||||
'The user id is required' => 'O id de usuário é obrigatório',
|
||||
'Passwords doesn\'t matches' => 'As senhas não conferem',
|
||||
'The confirmation is required' => 'A confirmação é obrigatória',
|
||||
'The column is required' => 'A coluna é obrigatória',
|
||||
'The project is required' => 'O projeto é obrigatório',
|
||||
'The color is required' => 'A cor é obrigatória',
|
||||
'The id is required' => 'O id é obrigatório',
|
||||
'The project id is required' => 'O id do projeto é obrigatório',
|
||||
'The project name is required' => 'O nome do projeto é obrigatório',
|
||||
'This project must be unique' => 'Este projeto deve ser único',
|
||||
'The title is required' => 'O título é obrigatório',
|
||||
'The language is required' => 'O idioma é obrigatório',
|
||||
'There is no active project, the first step is to create a new project.' => 'Não há projeto ativo. O primeiro passo é criar um novo projeto.',
|
||||
'Settings saved successfully.' => 'Configurações salvas com sucesso.',
|
||||
'Unable to save your settings.' => 'Impossível salvar suas configurações.',
|
||||
'Database optimization done.' => 'Otimização do banco de dados terminada.',
|
||||
'Your project have been created successfully.' => 'Seu projeto foi criado com sucesso.',
|
||||
'Unable to create your project.' => 'Impossível criar seu projeto.',
|
||||
'Project updated successfully.' => 'Projeto atualizado com sucesso.',
|
||||
'Unable to update this project.' => 'Impossível atualizar este projeto.',
|
||||
'Unable to remove this project.' => 'Impossível remover este projeto.',
|
||||
'Project removed successfully.' => 'Projeto removido com sucesso.',
|
||||
'Project activated successfully.' => 'Projeto ativado com sucesso.',
|
||||
'Unable to activate this project.' => 'Impossível ativar este projeto.',
|
||||
'Project disabled successfully.' => 'Projeto desabilitado com sucesso.',
|
||||
'Unable to disable this project.' => 'Impossível desabilitar este projeto.',
|
||||
'Unable to open this task.' => 'Impossível abrir esta tarefa.',
|
||||
'Task opened successfully.' => 'Tarefa aberta com sucesso.',
|
||||
'Unable to close this task.' => 'Impossível encerrar esta tarefa.',
|
||||
'Task closed successfully.' => 'Tarefa encerrada com sucesso.',
|
||||
'Unable to update your task.' => 'Impossível atualizar sua tarefa.',
|
||||
'Task updated successfully.' => 'Tarefa atualizada com sucesso.',
|
||||
'Unable to create your task.' => 'Impossível criar sua tarefa.',
|
||||
'Task created successfully.' => 'Tarefa criada com sucesso.',
|
||||
'User created successfully.' => 'Usuário criado com sucesso.',
|
||||
'Unable to create your user.' => 'Impossível criar seu usuário.',
|
||||
'User updated successfully.' => 'Usuário atualizado com sucesso.',
|
||||
'Unable to update your user.' => 'Impossível atualizar seu usuário.',
|
||||
'User removed successfully.' => 'Usuário removido com sucesso.',
|
||||
'Unable to remove this user.' => 'Impossível remover este usuário.',
|
||||
'Board updated successfully.' => 'Quadro atualizado com sucesso.',
|
||||
'Ready' => 'Pronto',
|
||||
'Backlog' => 'Backlog',
|
||||
'Work in progress' => 'Em andamento',
|
||||
'Done' => 'Encerrado',
|
||||
'Application version:' => 'Versão da aplicação:',
|
||||
'Completed on %B %e, %G at %k:%M %p' => 'Encerrado em %d %B %G às %H:%M',
|
||||
'%B %e, %G at %k:%M %p' => '%d %B %G às %H:%M',
|
||||
'Date created' => 'Data de criação',
|
||||
'Date completed' => 'Data de encerramento',
|
||||
'Id' => 'Id',
|
||||
'No task' => 'Nenhuma tarefa',
|
||||
'Completed tasks' => 'tarefas completadas',
|
||||
'List of projects' => 'Lista de projetos',
|
||||
'Completed tasks for "%s"' => 'Tarefas completadas por "%s"',
|
||||
'%d closed tasks' => '%d tarefas encerradas',
|
||||
'no task for this project' => 'nenhuma tarefa para este projeto',
|
||||
'Public link' => 'Link público',
|
||||
'There is no column in your project!' => 'Não há colunas no seu projeto!',
|
||||
'Change assignee' => 'Mudar a designação',
|
||||
'Change assignee for the task "%s"' => 'Modificar designação para a tarefa "%s"',
|
||||
'Timezone' => 'Fuso horário',
|
||||
'Sorry, I didn\'t found this information in my database!' => 'Desculpe, não encontrei esta informação no meu banco de dados!',
|
||||
'Page not found' => 'Página não encontrada',
|
||||
'Story Points' => 'Complexidade',
|
||||
'limit' => 'limite',
|
||||
'Task limit' => 'Limite da tarefa',
|
||||
'This value must be greater than %d' => 'Este valor deve ser maior que %d',
|
||||
'Edit project access list' => 'Editar lista de acesso ao projeto', // new translations to brazilian portuguese starts here
|
||||
'Edit users access' => 'Editar acesso de usuários',
|
||||
'Allow this user' => 'Permitir esse usuário',
|
||||
'Project access list for "%s"' => 'Lista de acesso ao projeto para "%s"',
|
||||
'Only those users have access to this project:' => 'Somente estes usuários têm acesso a este projeto:',
|
||||
'Don\'t forget that administrators have access to everything.' => 'Não esqueça que administradores têm acesso a tudo.',
|
||||
'revoke' => 'revogar',
|
||||
'List of authorized users' => 'Lista de usuários autorizados',
|
||||
'User' => 'Usuário',
|
||||
'Everybody have access to this project.' => 'Todos têm acesso a este projeto.',
|
||||
'You are not allowed to access to this project.' => 'Você não está autorizado a acessar este projeto.',
|
||||
'%B %e, %G at %k:%M %p' => '%d %B %G às %H:%M',
|
||||
'Comments' => 'Comentários',
|
||||
'Post comment' => 'Postar comentário',
|
||||
'Write your text in Markdown' => 'Escreva seu texto em Markdown',
|
||||
'Leave a comment' => 'Deixe um comentário',
|
||||
'Comment is required' => 'Comentário é obrigatório',
|
||||
'Leave a description' => 'Deixe uma descrição',
|
||||
'Comment added successfully.' => 'Cpmentário adicionado com sucesso.',
|
||||
'Unable to create your comment.' => 'Impossível criar seu comentário.',
|
||||
'The description is required' => 'A descrição é obrigatória',
|
||||
'Edit this task' => 'Editar esta tarefa',
|
||||
'Due Date' => 'Data de vencimento',
|
||||
'm/d/Y' => 'd/m/Y', // Date format parsed with php
|
||||
'month/day/year' => 'dia/mês/ano', // Help shown to the user
|
||||
'Invalid date' => 'Data inválida',
|
||||
'Must be done before %B %e, %G' => 'Deve ser feito antes de %d %B %G',
|
||||
'%B %e, %G' => '%d %B %G',
|
||||
'Automatic actions' => 'Ações automáticas',
|
||||
'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',
|
||||
'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',
|
||||
'Event name' => 'Nome do evento',
|
||||
'Action name' => 'Nome da ação',
|
||||
'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',
|
||||
'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',
|
||||
'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',
|
||||
'Move a task to another column' => 'Mover a tarefa para outra coluna',
|
||||
'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',
|
||||
'Assign a color to a specific user' => 'Designar uma cor para um usuário específico',
|
||||
// 'Column title' => '',
|
||||
// 'Position' => '',
|
||||
// 'Move Up' => '',
|
||||
// 'Move Down' => '',
|
||||
// 'Duplicate to another project' => '',
|
||||
// 'Duplicate' => '',
|
||||
// 'link' => '',
|
||||
// 'Update this comment' => '',
|
||||
// 'Comment updated successfully.' => '',
|
||||
// 'Unable to update your comment.' => '',
|
||||
// 'Remove a comment' => '',
|
||||
// 'Comment removed successfully.' => '',
|
||||
// 'Unable to remove this comment.' => '',
|
||||
// 'Do you really want to remove this comment?' => '',
|
||||
// 'Only administrators or the creator of the comment can access to this page.' => '',
|
||||
// 'Details' => '',
|
||||
// 'Current password for the user "%s"' => '',
|
||||
// 'The current password is required' => '',
|
||||
// 'Wrong password' => '',
|
||||
// 'Reset all tokens' => '',
|
||||
// 'All tokens have been regenerated.' => '',
|
||||
// 'Unknown' => '',
|
||||
// 'Last logins' => '',
|
||||
// 'Login date' => '',
|
||||
// 'Authentication method' => '',
|
||||
// 'IP address' => '',
|
||||
// 'User agent' => '',
|
||||
// 'Persistent connections' => '',
|
||||
// 'No session' => '',
|
||||
// 'Expiration date' => '',
|
||||
// 'Remember Me' => '',
|
||||
// 'Creation date' => '',
|
||||
// 'Filter by user' => '',
|
||||
// 'Filter by due date' => ',
|
||||
// 'Everybody' => '',
|
||||
// 'Open' => '',
|
||||
// 'Closed' => '',
|
||||
// 'Search' => '',
|
||||
// 'Nothing found.' => '',
|
||||
// 'Search in the project "%s"' => '',
|
||||
// 'Due date' => '',
|
||||
// 'Others formats accepted: %s and %s' => '',
|
||||
// 'Description' => '',
|
||||
// '%d comments' => '',
|
||||
// '%d comment' => '',
|
||||
// 'Email address invalid' => '',
|
||||
// 'Your Google Account is not linked anymore to your profile.' => '',
|
||||
// 'Unable to unlink your Google Account.' => '',
|
||||
// 'Google authentication failed' => '',
|
||||
// 'Unable to link your Google Account.' => '',
|
||||
// 'Your Google Account is linked to your profile successfully.' => '',
|
||||
// 'Email' => '',
|
||||
// 'Link my Google Account' => '',
|
||||
// 'Unlink my Google Account' => '',
|
||||
// 'Login with my Google Account' => '',
|
||||
// 'Project not found.' => '',
|
||||
// 'Task #%d' => '',
|
||||
// 'Task removed successfully.' => '',
|
||||
// 'Unable to remove this task.' => '',
|
||||
// 'Remove a task' => '',
|
||||
// 'Do you really want to remove this task: "%s"?' => '',
|
||||
// 'Assign a color to a specific category' => '',
|
||||
// 'Task creation or modification' => '',
|
||||
// 'Category' => '',
|
||||
// 'Category:' => '',
|
||||
// 'Categories' => '',
|
||||
// 'Category not found.' => '',
|
||||
// 'Your category have been created successfully.' => '',
|
||||
// 'Unable to create your category.' => '',
|
||||
// 'Your category have been updated successfully.' => '',
|
||||
// 'Unable to update your category.' => '',
|
||||
// 'Remove a category' => '',
|
||||
// 'Category removed successfully.' => '',
|
||||
// 'Unable to remove this category.' => '',
|
||||
// 'Category modification for the project "%s"' => '',
|
||||
// 'Category Name' => '',
|
||||
// 'Categories for the project "%s"' => '',
|
||||
// 'Add a new category' => '',
|
||||
// 'Do you really want to remove this category: "%s"?' => '',
|
||||
// 'Filter by category' => '',
|
||||
// 'All categories' => '',
|
||||
// 'No category' => '',
|
||||
// 'The name is required' => '',
|
||||
);
|
||||
159
app/Model/Acl.php
Normal file
159
app/Model/Acl.php
Normal file
@@ -0,0 +1,159 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* Acl model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Acl extends Base
|
||||
{
|
||||
/**
|
||||
* Controllers and actions allowed from outside
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $public_actions = array(
|
||||
'user' => array('login', 'check', 'google'),
|
||||
'task' => array('add'),
|
||||
'board' => array('readonly'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Controllers and actions allowed for regular users
|
||||
*
|
||||
* @access private
|
||||
* @var array
|
||||
*/
|
||||
private $user_actions = array(
|
||||
'app' => array('index'),
|
||||
'board' => array('index', 'show', 'assign', 'assigntask', 'save', 'check'),
|
||||
'project' => array('tasks', 'index', 'forbidden', 'search'),
|
||||
'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'confirmclose', 'open', 'confirmopen', 'description', 'duplicate', 'remove', 'confirmremove'),
|
||||
'comment' => array('save', 'confirm', 'remove', 'update', 'edit'),
|
||||
'user' => array('index', 'edit', 'update', 'forbidden', 'logout', 'index', 'unlinkgoogle'),
|
||||
'config' => array('index', 'removeremembermetoken'),
|
||||
);
|
||||
|
||||
/**
|
||||
* Return true if the specified controller/action is allowed according to the given acl
|
||||
*
|
||||
* @access public
|
||||
* @param array $acl Acl list
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isAllowedAction(array $acl, $controller, $action)
|
||||
{
|
||||
if (isset($acl[$controller])) {
|
||||
return in_array($action, $acl[$controller]);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is public
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isPublicAction($controller, $action)
|
||||
{
|
||||
return $this->isAllowedAction($this->public_actions, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given action is allowed for a regular user
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isUserAction($controller, $action)
|
||||
{
|
||||
return $this->isAllowedAction($this->user_actions, $controller, $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the logged user is admin
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isAdminUser()
|
||||
{
|
||||
return isset($_SESSION['user']['is_admin']) && $_SESSION['user']['is_admin'] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the logged user is not admin
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isRegularUser()
|
||||
{
|
||||
return isset($_SESSION['user']['is_admin']) && $_SESSION['user']['is_admin'] === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the connected user id
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getUserId()
|
||||
{
|
||||
return isset($_SESSION['user']['id']) ? (int) $_SESSION['user']['id'] : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is the user is connected
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function isLogged()
|
||||
{
|
||||
return ! empty($_SESSION['user']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check is the user was authenticated with the RememberMe or set the value
|
||||
*
|
||||
* @access public
|
||||
* @param bool $value Set true if the user use the RememberMe
|
||||
* @return bool
|
||||
*/
|
||||
public function isRememberMe($value = null)
|
||||
{
|
||||
if ($value !== null) {
|
||||
$_SESSION['is_remember_me'] = $value;
|
||||
}
|
||||
|
||||
return empty($_SESSION['is_remember_me']) ? false : $_SESSION['is_remember_me'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an action is allowed for the logged user
|
||||
*
|
||||
* @access public
|
||||
* @param string $controller Controller name
|
||||
* @param string $action Action name
|
||||
* @return bool
|
||||
*/
|
||||
public function isPageAccessAllowed($controller, $action)
|
||||
{
|
||||
return $this->isPublicAction($controller, $action) ||
|
||||
$this->isAdminUser() ||
|
||||
($this->isRegularUser() && $this->isUserAction($controller, $action));
|
||||
}
|
||||
}
|
||||
267
app/Model/Action.php
Normal file
267
app/Model/Action.php
Normal file
@@ -0,0 +1,267 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use LogicException;
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Action model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Action extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name for actions
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'actions';
|
||||
|
||||
/**
|
||||
* SQL table name for action parameters
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE_PARAMS = 'action_has_params';
|
||||
|
||||
/**
|
||||
* Return the name and description of available actions
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableActions()
|
||||
{
|
||||
return array(
|
||||
'TaskClose' => t('Close the task'),
|
||||
'TaskAssignSpecificUser' => t('Assign the task to a specific user'),
|
||||
'TaskAssignCurrentUser' => t('Assign the task to the person who does the action'),
|
||||
'TaskDuplicateAnotherProject' => t('Duplicate the task to another project'),
|
||||
'TaskAssignColorUser' => t('Assign a color to a specific user'),
|
||||
'TaskAssignColorCategory' => t('Assign a color to a specific category'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the name and description of available actions
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAvailableEvents()
|
||||
{
|
||||
return array(
|
||||
Task::EVENT_MOVE_COLUMN => t('Move a task to another column'),
|
||||
Task::EVENT_MOVE_POSITION => t('Move a task to another position in the same column'),
|
||||
Task::EVENT_UPDATE => t('Task modification'),
|
||||
Task::EVENT_CREATE => t('Task creation'),
|
||||
Task::EVENT_OPEN => t('Open a closed task'),
|
||||
Task::EVENT_CLOSE => t('Closing a task'),
|
||||
Task::EVENT_CREATE_UPDATE => t('Task creation or modification'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return actions and parameters for a given project
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAllByProject($project_id)
|
||||
{
|
||||
$actions = $this->db->table(self::TABLE)->eq('project_id', $project_id)->findAll();
|
||||
|
||||
foreach ($actions as &$action) {
|
||||
$action['params'] = $this->db->table(self::TABLE_PARAMS)->eq('action_id', $action['id'])->findAll();
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all actions and parameters
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
$actions = $this->db->table(self::TABLE)->findAll();
|
||||
|
||||
foreach ($actions as &$action) {
|
||||
$action['params'] = $this->db->table(self::TABLE_PARAMS)->eq('action_id', $action['id'])->findAll();
|
||||
}
|
||||
|
||||
return $actions;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all required action parameters for all registered actions
|
||||
*
|
||||
* @access public
|
||||
* @return array All required parameters for all actions
|
||||
*/
|
||||
public function getAllActionParameters()
|
||||
{
|
||||
$params = array();
|
||||
|
||||
foreach ($this->getAll() as $action) {
|
||||
|
||||
$action = $this->load($action['action_name'], $action['project_id']);
|
||||
$params += $action->getActionRequiredParameters();
|
||||
}
|
||||
|
||||
return $params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch an action
|
||||
*
|
||||
* @access public
|
||||
* @param integer $action_id Action id
|
||||
* @return array Action data
|
||||
*/
|
||||
public function getById($action_id)
|
||||
{
|
||||
$action = $this->db->table(self::TABLE)->eq('id', $action_id)->findOne();
|
||||
$action['params'] = $this->db->table(self::TABLE_PARAMS)->eq('action_id', $action_id)->findAll();
|
||||
|
||||
return $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an action
|
||||
*
|
||||
* @access public
|
||||
* @param integer $action_id Action id
|
||||
* @return bool Success or not
|
||||
*/
|
||||
public function remove($action_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $action_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Required parameters to save an action
|
||||
* @return bool Success or not
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$action = array(
|
||||
'project_id' => $values['project_id'],
|
||||
'event_name' => $values['event_name'],
|
||||
'action_name' => $values['action_name'],
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($action)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$action_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
foreach ($values['params'] as $param_name => $param_value) {
|
||||
|
||||
$action_param = array(
|
||||
'action_id' => $action_id,
|
||||
'name' => $param_name,
|
||||
'value' => $param_value,
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE_PARAMS)->save($action_param)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load all actions and attach events
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
foreach ($this->getAll() as $action) {
|
||||
|
||||
$listener = $this->load($action['action_name'], $action['project_id']);
|
||||
|
||||
foreach ($action['params'] as $param) {
|
||||
$listener->setParam($param['name'], $param['value']);
|
||||
}
|
||||
|
||||
$this->event->attach($action['event_name'], $listener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load an action
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Action class name
|
||||
* @param integer $project_id Project id
|
||||
* @return mixed Action Instance
|
||||
* @throw LogicException
|
||||
*/
|
||||
public function load($name, $project_id)
|
||||
{
|
||||
switch ($name) {
|
||||
case 'TaskClose':
|
||||
$className = '\Action\TaskClose';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignCurrentUser':
|
||||
$className = '\Action\TaskAssignCurrentUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event), new Acl($this->db, $this->event));
|
||||
case 'TaskAssignSpecificUser':
|
||||
$className = '\Action\TaskAssignSpecificUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskDuplicateAnotherProject':
|
||||
$className = '\Action\TaskDuplicateAnotherProject';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignColorUser':
|
||||
$className = '\Action\TaskAssignColorUser';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
case 'TaskAssignColorCategory':
|
||||
$className = '\Action\TaskAssignColorCategory';
|
||||
return new $className($project_id, new Task($this->db, $this->event));
|
||||
default:
|
||||
throw new LogicException('Action not found: '.$name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate action creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Required parameters to save an action
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('event_name', t('This value is required')),
|
||||
new Validators\Required('action_name', t('This value is required')),
|
||||
new Validators\Required('params', t('This value is required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
76
app/Model/Base.php
Normal file
76
app/Model/Base.php
Normal file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validator.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Base.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Required.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Unique.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/MaxLength.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/MinLength.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Integer.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Equals.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/AlphaNumeric.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/GreaterThan.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Date.php';
|
||||
require __DIR__.'/../../vendor/SimpleValidator/Validators/Email.php';
|
||||
|
||||
use Core\Event;
|
||||
use PicoDb\Database;
|
||||
|
||||
/**
|
||||
* Base model class
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
abstract class Base
|
||||
{
|
||||
/**
|
||||
* Database instance
|
||||
*
|
||||
* @access protected
|
||||
* @var PicoDb
|
||||
*/
|
||||
protected $db;
|
||||
|
||||
/**
|
||||
* Event dispatcher instance
|
||||
*
|
||||
* @access protected
|
||||
* @var Core\Event
|
||||
*/
|
||||
protected $event;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @access public
|
||||
* @param \PicoDb\Database $db Database instance
|
||||
* @param \Core\Event $event Event dispatcher instance
|
||||
*/
|
||||
public function __construct(Database $db, Event $event)
|
||||
{
|
||||
$this->db = $db;
|
||||
$this->event = $event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random token with different methods: openssl or /dev/urandom or fallback to uniqid()
|
||||
*
|
||||
* @static
|
||||
* @access public
|
||||
* @return string Random token
|
||||
*/
|
||||
public static function generateToken()
|
||||
{
|
||||
if (function_exists('openssl_random_pseudo_bytes')) {
|
||||
return bin2hex(\openssl_random_pseudo_bytes(16));
|
||||
}
|
||||
else if (ini_get('open_basedir') === '' && strtoupper(substr(PHP_OS, 0, 3)) !== 'WIN') {
|
||||
return hash('sha256', file_get_contents('/dev/urandom', false, null, 0, 30));
|
||||
}
|
||||
|
||||
return hash('sha256', uniqid(mt_rand(), true));
|
||||
}
|
||||
}
|
||||
340
app/Model/Board.php
Normal file
340
app/Model/Board.php
Normal file
@@ -0,0 +1,340 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Board model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Board extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'columns';
|
||||
|
||||
/**
|
||||
* Save task positions for each column
|
||||
*
|
||||
* @access public
|
||||
* @param array $values [['task_id' => X, 'column_id' => X, 'position' => X], ...]
|
||||
* @return boolean
|
||||
*/
|
||||
public function saveTasksPosition(array $values)
|
||||
{
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach ($values as $value) {
|
||||
if (! $taskModel->move($value['task_id'], $value['column_id'], $value['position'])) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a board with default columns, must be executed inside a transaction
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $columns List of columns title ['column1', 'column2', ...]
|
||||
* @return boolean
|
||||
*/
|
||||
public function create($project_id, array $columns)
|
||||
{
|
||||
$position = 0;
|
||||
|
||||
foreach ($columns as $title) {
|
||||
|
||||
$values = array(
|
||||
'title' => $title,
|
||||
'position' => ++$position,
|
||||
'project_id' => $project_id,
|
||||
);
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new column to the board
|
||||
*
|
||||
* @access public
|
||||
* @param array $values ['title' => X, 'project_id' => X]
|
||||
* @return boolean
|
||||
*/
|
||||
public function add(array $values)
|
||||
{
|
||||
$values['position'] = $this->getLastColumnPosition($values['project_id']) + 1;
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update columns
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
foreach (array('title', 'task_limit') as $field) {
|
||||
foreach ($values[$field] as $column_id => $field_value) {
|
||||
$this->db->table(self::TABLE)->eq('id', $column_id)->update(array($field => $field_value));
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a column down, increment the column position value
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $column_id Column id
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveDown($project_id, $column_id)
|
||||
{
|
||||
$columns = $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'position');
|
||||
$positions = array_flip($columns);
|
||||
|
||||
if (isset($columns[$column_id]) && $columns[$column_id] < count($columns)) {
|
||||
|
||||
$position = ++$columns[$column_id];
|
||||
$columns[$positions[$position]]--;
|
||||
|
||||
$this->db->startTransaction();
|
||||
$this->db->table(self::TABLE)->eq('id', $column_id)->update(array('position' => $position));
|
||||
$this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $columns[$positions[$position]]));
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a column up, decrement the column position value
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $column_id Column id
|
||||
* @return boolean
|
||||
*/
|
||||
public function moveUp($project_id, $column_id)
|
||||
{
|
||||
$columns = $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'position');
|
||||
$positions = array_flip($columns);
|
||||
|
||||
if (isset($columns[$column_id]) && $columns[$column_id] > 1) {
|
||||
|
||||
$position = --$columns[$column_id];
|
||||
$columns[$positions[$position]]++;
|
||||
|
||||
$this->db->startTransaction();
|
||||
$this->db->table(self::TABLE)->eq('id', $column_id)->update(array('position' => $position));
|
||||
$this->db->table(self::TABLE)->eq('id', $positions[$position])->update(array('position' => $columns[$positions[$position]]));
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all columns and tasks for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function get($project_id, array $filters = array())
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$columns = $this->getColumns($project_id);
|
||||
|
||||
$filters[] = array('column' => 'project_id', 'operator' => 'eq', 'value' => $project_id);
|
||||
$filters[] = array('column' => 'is_active', 'operator' => 'eq', 'value' => Task::STATUS_OPEN);
|
||||
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$tasks = $taskModel->find($filters);
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
|
||||
$column['tasks'] = array();
|
||||
|
||||
foreach ($tasks as &$task) {
|
||||
if ($task['column_id'] == $column['id']) {
|
||||
$column['tasks'][] = $task;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first column id for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return integer
|
||||
*/
|
||||
public function getFirstColumn($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findOneColumn('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of columns sorted by position [ column_id => title ]
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getColumnsList($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->listing('id', 'title');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all columns sorted by position for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getColumns($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of columns for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return integer
|
||||
*/
|
||||
public function countColumns($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('project_id', $project_id)->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a column by the id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $column_id Column id
|
||||
* @return array
|
||||
*/
|
||||
public function getColumn($column_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $column_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the position of the last column for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return integer
|
||||
*/
|
||||
public function getLastColumnPosition($project_id)
|
||||
{
|
||||
return (int) $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->desc('position')
|
||||
->findOneColumn('position');
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a column and all tasks associated to this column
|
||||
*
|
||||
* @access public
|
||||
* @param integer $column_id Column id
|
||||
* @return boolean
|
||||
*/
|
||||
public function removeColumn($column_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $column_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate column modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $columns Original columns List
|
||||
* @param array $values Required parameters to update a column
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $columns, array $values)
|
||||
{
|
||||
$rules = array();
|
||||
|
||||
foreach ($columns as $column_id => $column_title) {
|
||||
$rules[] = new Validators\Integer('task_limit['.$column_id.']', t('This value must be an integer'));
|
||||
$rules[] = new Validators\GreaterThan('task_limit['.$column_id.']', t('This value must be greater than %d', 0), 0);
|
||||
$rules[] = new Validators\Required('title['.$column_id.']', t('The title is required'));
|
||||
$rules[] = new Validators\MaxLength('title['.$column_id.']', t('The maximum length is %d characters', 50), 50);
|
||||
}
|
||||
|
||||
$v = new Validator($values, $rules);
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate column creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Required parameters to save an action
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 50), 50),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
150
app/Model/Category.php
Normal file
150
app/Model/Category.php
Normal file
@@ -0,0 +1,150 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Category model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Category extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'project_has_categories';
|
||||
|
||||
/**
|
||||
* Get a category by the id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $category_id Category id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($category_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $category_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all categories
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param bool $prepend_none If true, prepend to the list the value 'None'
|
||||
* @param bool $prepend_all If true, prepend to the list the value 'All'
|
||||
* @return array
|
||||
*/
|
||||
public function getList($project_id, $prepend_none = true, $prepend_all = false)
|
||||
{
|
||||
$listing = $this->db->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->asc('name')
|
||||
->listing('id', 'name');
|
||||
|
||||
$prepend = array();
|
||||
|
||||
if ($prepend_all) {
|
||||
$prepend[-1] = t('All categories');
|
||||
}
|
||||
|
||||
if ($prepend_none) {
|
||||
$prepend[0] = t('No category');
|
||||
}
|
||||
|
||||
return $prepend + $listing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a category
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a category
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a category
|
||||
*
|
||||
* @access public
|
||||
* @param integer $category_id Category id
|
||||
* @return bool
|
||||
*/
|
||||
public function remove($category_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
$r1 = $this->db->table(Task::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0));
|
||||
$r2 = $this->db->table(self::TABLE)->eq('id', $category_id)->remove();
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $r1 && $r2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate category creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $array Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('The project id must be an integer')),
|
||||
new Validators\Required('name', t('The name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate category modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $array Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('The id must be an integer')),
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('The project id must be an integer')),
|
||||
new Validators\Required('name', t('The name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50)
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
171
app/Model/Comment.php
Normal file
171
app/Model/Comment.php
Normal file
@@ -0,0 +1,171 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Comment model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Comment extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'comments';
|
||||
|
||||
/**
|
||||
* Get all comments for a given task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.date',
|
||||
self::TABLE.'.task_id',
|
||||
self::TABLE.'.user_id',
|
||||
self::TABLE.'.comment',
|
||||
User::TABLE.'.username'
|
||||
)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->orderBy(self::TABLE.'.date', 'ASC')
|
||||
->eq(self::TABLE.'.task_id', $task_id)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a comment
|
||||
*
|
||||
* @access public
|
||||
* @param integer $comment_id Comment id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($comment_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.task_id',
|
||||
self::TABLE.'.user_id',
|
||||
self::TABLE.'.date',
|
||||
self::TABLE.'.comment',
|
||||
User::TABLE.'.username'
|
||||
)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq(self::TABLE.'.id', $comment_id)
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of comments for a given task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return integer
|
||||
*/
|
||||
public function count($task_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq(self::TABLE.'.task_id', $task_id)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a comment in the database
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$values['date'] = time();
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a comment in the database
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $values['id'])
|
||||
->update(array('comment' => $values['comment']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a comment
|
||||
*
|
||||
* @access public
|
||||
* @param integer $comment_id Comment id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($comment_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $comment_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate comment creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Required parameters to save an action
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('task_id', t('This value is required')),
|
||||
new Validators\Integer('task_id', t('This value must be an integer')),
|
||||
new Validators\Required('user_id', t('This value is required')),
|
||||
new Validators\Integer('user_id', t('This value must be an integer')),
|
||||
new Validators\Required('comment', t('Comment is required'))
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate comment modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Required parameters to save an action
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('This value is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('comment', t('Comment is required'))
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
}
|
||||
182
app/Model/Config.php
Normal file
182
app/Model/Config.php
Normal file
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* Config model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Config extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'config';
|
||||
|
||||
/**
|
||||
* Get available timezones
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getTimezones()
|
||||
{
|
||||
$timezones = \timezone_identifiers_list();
|
||||
return array_combine(array_values($timezones), $timezones);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get available languages
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getLanguages()
|
||||
{
|
||||
$languages = array(
|
||||
'en_US' => t('English'),
|
||||
'es_ES' => t('Spanish'),
|
||||
'fr_FR' => t('French'),
|
||||
'pl_PL' => t('Polish'),
|
||||
'pt_BR' => t('Portuguese (Brazilian)'),
|
||||
);
|
||||
|
||||
asort($languages);
|
||||
|
||||
return $languages;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a config variable from the session or the database
|
||||
*
|
||||
* @access public
|
||||
* @param string $name Parameter name
|
||||
* @param string $default_value Default value of the parameter
|
||||
* @return mixed
|
||||
*/
|
||||
public function get($name, $default_value = '')
|
||||
{
|
||||
if (! isset($_SESSION['config'][$name])) {
|
||||
$_SESSION['config'] = $this->getAll();
|
||||
}
|
||||
|
||||
if (isset($_SESSION['config'][$name])) {
|
||||
return $_SESSION['config'][$name];
|
||||
}
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all settings
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Save settings in the database
|
||||
*
|
||||
* @access public
|
||||
* @param $values array Settings values
|
||||
* @return boolean
|
||||
*/
|
||||
public function save(array $values)
|
||||
{
|
||||
$_SESSION['config'] = $values;
|
||||
return $this->db->table(self::TABLE)->update($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload settings in the session and the translations
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function reload()
|
||||
{
|
||||
$_SESSION['config'] = $this->getAll();
|
||||
|
||||
$language = $this->get('language', 'en_US');
|
||||
if ($language !== 'en_US') \Translator\load($language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate settings modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('language', t('The language is required')),
|
||||
new Validators\Required('timezone', t('The timezone is required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimize the Sqlite database
|
||||
*
|
||||
* @access public
|
||||
* @return boolean
|
||||
*/
|
||||
public function optimizeDatabase()
|
||||
{
|
||||
return $this->db->getconnection()->exec("VACUUM");
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress the Sqlite database
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function downloadDatabase()
|
||||
{
|
||||
return gzencode(file_get_contents(DB_FILENAME));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Sqlite database size in bytes
|
||||
*
|
||||
* @access public
|
||||
* @return integer
|
||||
*/
|
||||
public function getDatabaseSize()
|
||||
{
|
||||
return DB_DRIVER === 'sqlite' ? filesize(DB_FILENAME) : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Regenerate all tokens (projects and webhooks)
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function regenerateTokens()
|
||||
{
|
||||
$this->db->table(self::TABLE)->update(array('webhooks_token' => $this->generateToken()));
|
||||
|
||||
$projects = $this->db->table(Project::TABLE)->findAllByColumn('id');
|
||||
|
||||
foreach ($projects as $project_id) {
|
||||
$this->db->table(Project::TABLE)->eq('id', $project_id)->update(array('token' => $this->generateToken()));
|
||||
}
|
||||
}
|
||||
}
|
||||
152
app/Model/Google.php
Normal file
152
app/Model/Google.php
Normal file
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
require __DIR__.'/../../vendor/OAuth/bootstrap.php';
|
||||
|
||||
use OAuth\Common\Storage\Session;
|
||||
use OAuth\Common\Consumer\Credentials;
|
||||
use OAuth\Common\Http\Uri\UriFactory;
|
||||
use OAuth\ServiceFactory;
|
||||
use OAuth\Common\Http\Exception\TokenResponseException;
|
||||
|
||||
/**
|
||||
* Google model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Google extends Base
|
||||
{
|
||||
/**
|
||||
* Authenticate a Google user
|
||||
*
|
||||
* @access public
|
||||
* @param string $google_id Google unique id
|
||||
* @return boolean
|
||||
*/
|
||||
public function authenticate($google_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByGoogleId($google_id);
|
||||
|
||||
if ($user) {
|
||||
|
||||
// Create the user session
|
||||
$userModel->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
LastLogin::AUTH_GOOGLE,
|
||||
$user['id'],
|
||||
$userModel->getIpAddress(),
|
||||
$userModel->getUserAgent()
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink a Google account for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return boolean
|
||||
*/
|
||||
public function unlink($user_id)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => '',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user table based on the Google profile information
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param array $profile Google profile
|
||||
* @return boolean
|
||||
*/
|
||||
public function updateUser($user_id, array $profile)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
return $userModel->update(array(
|
||||
'id' => $user_id,
|
||||
'google_id' => $profile['id'],
|
||||
'email' => $profile['email'],
|
||||
'name' => $profile['name'],
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the Google service instance
|
||||
*
|
||||
* @access public
|
||||
* @return \OAuth\OAuth2\Service\Google
|
||||
*/
|
||||
public function getService()
|
||||
{
|
||||
$uriFactory = new UriFactory();
|
||||
$currentUri = $uriFactory->createFromSuperGlobalArray($_SERVER);
|
||||
$currentUri->setQuery('controller=user&action=google');
|
||||
|
||||
$storage = new Session(false);
|
||||
|
||||
$credentials = new Credentials(
|
||||
GOOGLE_CLIENT_ID,
|
||||
GOOGLE_CLIENT_SECRET,
|
||||
$currentUri->getAbsoluteUri()
|
||||
);
|
||||
|
||||
$serviceFactory = new ServiceFactory();
|
||||
|
||||
return $serviceFactory->createService(
|
||||
'google',
|
||||
$credentials,
|
||||
$storage,
|
||||
array('userinfo_email', 'userinfo_profile')
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the authorization URL
|
||||
*
|
||||
* @access public
|
||||
* @return \OAuth\Common\Http\Uri\Uri
|
||||
*/
|
||||
public function getAuthorizationUrl()
|
||||
{
|
||||
return $this->getService()->getAuthorizationUri();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Google profile information from the API
|
||||
*
|
||||
* @access public
|
||||
* @param string $code Google authorization code
|
||||
* @return bool|array
|
||||
*/
|
||||
public function getGoogleProfile($code)
|
||||
{
|
||||
try {
|
||||
|
||||
$googleService = $this->getService();
|
||||
$googleService->requestAccessToken($code);
|
||||
return json_decode($googleService->request('https://www.googleapis.com/oauth2/v1/userinfo'), true);
|
||||
}
|
||||
catch (TokenResponseException $e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
91
app/Model/LastLogin.php
Normal file
91
app/Model/LastLogin.php
Normal file
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* LastLogin model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class LastLogin extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'last_logins';
|
||||
|
||||
/**
|
||||
* Number of connections to keep for history
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const NB_LOGINS = 10;
|
||||
|
||||
/**
|
||||
* Authentication methods
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const AUTH_DATABASE = 'database';
|
||||
const AUTH_REMEMBER_ME = 'remember_me';
|
||||
const AUTH_LDAP = 'ldap';
|
||||
const AUTH_GOOGLE = 'google';
|
||||
|
||||
/**
|
||||
* Create a new record
|
||||
*
|
||||
* @access public
|
||||
* @param string $auth_type Authentication method
|
||||
* @param integer $user_id User id
|
||||
* @param string $ip IP Address
|
||||
* @param string $user_agent User Agent
|
||||
* @return array
|
||||
*/
|
||||
public function create($auth_type, $user_id, $ip, $user_agent)
|
||||
{
|
||||
// Cleanup old sessions if necessary
|
||||
$connections = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->desc('date_creation')
|
||||
->findAllByColumn('id');
|
||||
|
||||
if (count($connections) >= self::NB_LOGINS) {
|
||||
|
||||
$this->db->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->notin('id', array_slice($connections, 0, self::NB_LOGINS - 1))
|
||||
->remove();
|
||||
}
|
||||
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->insert(array(
|
||||
'auth_type' => $auth_type,
|
||||
'user_id' => $user_id,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $user_agent,
|
||||
'date_creation' => time(),
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the last connections for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->desc('date_creation')
|
||||
->columns('id', 'auth_type', 'ip', 'user_agent', 'date_creation')
|
||||
->findAll();
|
||||
}
|
||||
}
|
||||
79
app/Model/Ldap.php
Normal file
79
app/Model/Ldap.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* LDAP model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Ldap extends Base
|
||||
{
|
||||
/**
|
||||
* Authenticate a user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return null|boolean
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
if (! function_exists('ldap_connect')) {
|
||||
die('The PHP LDAP extension is required');
|
||||
}
|
||||
|
||||
$ldap = ldap_connect(LDAP_SERVER, LDAP_PORT);
|
||||
|
||||
if (! is_resource($ldap)) {
|
||||
die('Unable to connect to the LDAP server: "'.LDAP_SERVER.'"');
|
||||
}
|
||||
|
||||
ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
|
||||
|
||||
if (@ldap_bind($ldap, sprintf(LDAP_USER_DN, $username), $password)) {
|
||||
return $this->create($username);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create automatically a new local user after the LDAP authentication
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @return bool
|
||||
*/
|
||||
public function create($username)
|
||||
{
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$user = $userModel->getByUsername($username);
|
||||
|
||||
// There is an existing user account
|
||||
if ($user) {
|
||||
|
||||
if ($user['is_ldap_user'] == 1) {
|
||||
|
||||
// LDAP user already created
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
||||
// There is already a local user with that username
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create a LDAP user
|
||||
$values = array(
|
||||
'username' => $username,
|
||||
'is_admin' => 0,
|
||||
'is_ldap_user' => 1,
|
||||
);
|
||||
|
||||
return $userModel->create($values);
|
||||
}
|
||||
}
|
||||
558
app/Model/Project.php
Normal file
558
app/Model/Project.php
Normal file
@@ -0,0 +1,558 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use Event\TaskModification;
|
||||
|
||||
/**
|
||||
* Project model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Project extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name for projects
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'projects';
|
||||
|
||||
/**
|
||||
* SQL table name for users
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE_USERS = 'project_has_users';
|
||||
|
||||
/**
|
||||
* Value for active project
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const ACTIVE = 1;
|
||||
|
||||
/**
|
||||
* Value for inactive project
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const INACTIVE = 0;
|
||||
|
||||
/**
|
||||
* Get a list of people that can be assigned for tasks
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param bool $prepend_unassigned Prepend the 'Unassigned' value
|
||||
* @param bool $prepend_everybody Prepend the 'Everbody' value
|
||||
* @return array
|
||||
*/
|
||||
public function getUsersList($project_id, $prepend_unassigned = true, $prepend_everybody = false)
|
||||
{
|
||||
$allowed_users = $this->getAllowedUsers($project_id);
|
||||
$userModel = new User($this->db, $this->event);
|
||||
|
||||
if (empty($allowed_users)) {
|
||||
$allowed_users = $userModel->getList();
|
||||
}
|
||||
|
||||
if ($prepend_unassigned) {
|
||||
$allowed_users = array(t('Unassigned')) + $allowed_users;
|
||||
}
|
||||
|
||||
if ($prepend_everybody) {
|
||||
$allowed_users = array(User::EVERYBODY_ID => t('Everybody')) + $allowed_users;
|
||||
}
|
||||
|
||||
return $allowed_users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of allowed people for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllowedUsers($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->join(User::TABLE, 'id', 'user_id')
|
||||
->eq('project_id', $project_id)
|
||||
->asc('username')
|
||||
->listing('user_id', 'username');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get allowed and not allowed users for a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getAllUsers($project_id)
|
||||
{
|
||||
$users = array(
|
||||
'allowed' => array(),
|
||||
'not_allowed' => array(),
|
||||
);
|
||||
|
||||
$userModel = new User($this->db, $this->event);
|
||||
$all_users = $userModel->getList();
|
||||
|
||||
$users['allowed'] = $this->getAllowedUsers($project_id);
|
||||
|
||||
foreach ($all_users as $user_id => $username) {
|
||||
|
||||
if (! isset($users['allowed'][$user_id])) {
|
||||
$users['not_allowed'][$user_id] = $username;
|
||||
}
|
||||
}
|
||||
|
||||
return $users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allow a specific user for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function allowUser($project_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->save(array('project_id' => $project_id, 'user_id' => $user_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a specific user for a given project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function revokeUser($project_id, $user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
// If there is nobody specified, everybody have access to the project
|
||||
$nb_users = $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->eq('project_id', $project_id)
|
||||
->count();
|
||||
|
||||
if ($nb_users < 1) return true;
|
||||
|
||||
// Check if user has admin rights
|
||||
$nb_users = $this->db
|
||||
->table(User::TABLE)
|
||||
->eq('id', $user_id)
|
||||
->eq('is_admin', 1)
|
||||
->count();
|
||||
|
||||
if ($nb_users > 0) return true;
|
||||
|
||||
// Otherwise, allow only specific users
|
||||
return (bool) $this->db
|
||||
->table(self::TABLE_USERS)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('user_id', $user_id)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a project by the id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch project data by using the token
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Token
|
||||
* @return array
|
||||
*/
|
||||
public function getByToken($token)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('token', $token)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the first project from the database (no sorting)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getFirst()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects, optionaly fetch stats for each project and can check users permissions
|
||||
*
|
||||
* @access public
|
||||
* @param bool $fetch_stats If true, return metrics about each projects
|
||||
* @param bool $check_permissions If true, remove projects not allowed for the current user
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($fetch_stats = false, $check_permissions = false)
|
||||
{
|
||||
if (! $fetch_stats) {
|
||||
return $this->db->table(self::TABLE)->asc('name')->findAll();
|
||||
}
|
||||
|
||||
$this->db->startTransaction();
|
||||
|
||||
$projects = $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('name')
|
||||
->findAll();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$taskModel = new Task($this->db, $this->event);
|
||||
$aclModel = new Acl($this->db, $this->event);
|
||||
|
||||
foreach ($projects as $pkey => &$project) {
|
||||
|
||||
if ($check_permissions && ! $this->isUserAllowed($project['id'], $aclModel->getUserId())) {
|
||||
unset($projects[$pkey]);
|
||||
}
|
||||
else {
|
||||
|
||||
$columns = $boardModel->getcolumns($project['id']);
|
||||
$project['nb_active_tasks'] = 0;
|
||||
|
||||
foreach ($columns as &$column) {
|
||||
$column['nb_active_tasks'] = $taskModel->countByColumnId($project['id'], $column['id']);
|
||||
$project['nb_active_tasks'] += $column['nb_active_tasks'];
|
||||
}
|
||||
|
||||
$project['columns'] = $columns;
|
||||
$project['nb_tasks'] = $taskModel->countByProjectId($project['id']);
|
||||
$project['nb_inactive_tasks'] = $project['nb_tasks'] - $project['nb_active_tasks'];
|
||||
}
|
||||
}
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return $projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all projects
|
||||
*
|
||||
* @access public
|
||||
* @param bool $prepend If true, prepend to the list the value 'None'
|
||||
* @return array
|
||||
*/
|
||||
public function getList($prepend = true)
|
||||
{
|
||||
if ($prepend) {
|
||||
return array(t('None')) + $this->db->table(self::TABLE)->asc('name')->listing('id', 'name');
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->asc('name')->listing('id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all projects with all its data for a given status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $status Proejct status: self::ACTIVE or self:INACTIVE
|
||||
* @return array
|
||||
*/
|
||||
public function getAllByStatus($status)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('name')
|
||||
->eq('is_active', $status)
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a list of project by status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $status Proejct status: self::ACTIVE or self:INACTIVE
|
||||
* @return array
|
||||
*/
|
||||
public function getListByStatus($status)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('name')
|
||||
->eq('is_active', $status)
|
||||
->listing('id', 'name');
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the number of projects by status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $status Status: self::ACTIVE or self:INACTIVE
|
||||
* @return integer
|
||||
*/
|
||||
public function countByStatus($status)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('is_active', $status)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a list of projects for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param array $projects Project list: ['project_id' => 'project_name']
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function filterListByAccess(array $projects, $user_id)
|
||||
{
|
||||
foreach ($projects as $project_id => $project_name) {
|
||||
if (! $this->isUserAllowed($project_id, $user_id)) {
|
||||
unset($projects[$project_id]);
|
||||
}
|
||||
}
|
||||
|
||||
return $projects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a project
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return integer Project id
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$values['token'] = self::generateToken();
|
||||
|
||||
if (! $this->db->table(self::TABLE)->save($values)) {
|
||||
$this->db->cancelTransaction();
|
||||
return false;
|
||||
}
|
||||
|
||||
$project_id = $this->db->getConnection()->getLastId();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
$boardModel->create($project_id, array(
|
||||
t('Backlog'),
|
||||
t('Ready'),
|
||||
t('Work in progress'),
|
||||
t('Done'),
|
||||
));
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return (int) $project_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the project have been modified
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $timestamp Timestamp
|
||||
* @return bool
|
||||
*/
|
||||
public function isModifiedSince($project_id, $timestamp)
|
||||
{
|
||||
return (bool) $this->db->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->gt('last_modified', $timestamp)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update modification date
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function updateModificationDate($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->save(array(
|
||||
'last_modified' => time()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a project
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return bool
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function remove($project_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $project_id)->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function enable($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_active' => 1));
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable a project
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @return bool
|
||||
*/
|
||||
public function disable($project_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $project_id)
|
||||
->save(array('is_active' => 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate project creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $array Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('name', t('The project name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE)
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate project modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $array Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The project id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('name', t('The project name is required')),
|
||||
new Validators\MaxLength('name', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Unique('name', t('This project must be unique'), $this->db->getConnection(), self::TABLE)
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate allowed users
|
||||
*
|
||||
* @access public
|
||||
* @param array $array Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateUserAccess(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('project_id', t('The project id is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('user_id', t('The user id is required')),
|
||||
new Validators\Integer('user_id', t('This value must be an integer')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attach events
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function attachEvents()
|
||||
{
|
||||
$events = array(
|
||||
Task::EVENT_UPDATE,
|
||||
Task::EVENT_CREATE,
|
||||
Task::EVENT_CLOSE,
|
||||
Task::EVENT_OPEN,
|
||||
);
|
||||
|
||||
$listener = new TaskModification($this);
|
||||
|
||||
foreach ($events as $event_name) {
|
||||
$this->event->attach($event_name, $listener);
|
||||
}
|
||||
}
|
||||
}
|
||||
333
app/Model/RememberMe.php
Normal file
333
app/Model/RememberMe.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
/**
|
||||
* RememberMe model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class RememberMe extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'remember_me';
|
||||
|
||||
/**
|
||||
* Cookie name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const COOKIE_NAME = '__R';
|
||||
|
||||
/**
|
||||
* Expiration (60 days)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const EXPIRATION = 5184000;
|
||||
|
||||
/**
|
||||
* Get a remember me record
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function find($token, $sequence)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('token', $token)
|
||||
->eq('sequence', $sequence)
|
||||
->gt('expiration', time())
|
||||
->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all sessions for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getAll($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->desc('date_creation')
|
||||
->columns('id', 'ip', 'user_agent', 'date_creation', 'expiration')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate the user with the cookie
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function authenticate()
|
||||
{
|
||||
$credentials = $this->readCookie();
|
||||
|
||||
if ($credentials !== false) {
|
||||
|
||||
$record = $this->find($credentials['token'], $credentials['sequence']);
|
||||
|
||||
if ($record) {
|
||||
|
||||
// Update the sequence
|
||||
$this->writeCookie(
|
||||
$record['token'],
|
||||
$this->update($record['token'], $record['sequence']),
|
||||
$record['expiration']
|
||||
);
|
||||
|
||||
// Create the session
|
||||
$user = new User($this->db, $this->event);
|
||||
$acl = new Acl($this->db, $this->event);
|
||||
|
||||
$user->updateSession($user->getById($record['user_id']));
|
||||
$acl->isRememberMe(true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the database and the cookie with a new sequence
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function refresh()
|
||||
{
|
||||
$credentials = $this->readCookie();
|
||||
|
||||
if ($credentials !== false) {
|
||||
|
||||
$record = $this->find($credentials['token'], $credentials['sequence']);
|
||||
|
||||
if ($record) {
|
||||
|
||||
// Update the sequence
|
||||
$this->writeCookie(
|
||||
$record['token'],
|
||||
$this->update($record['token'], $record['sequence']),
|
||||
$record['expiration']
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a session record
|
||||
*
|
||||
* @access public
|
||||
* @param integer $session_id Session id
|
||||
* @return mixed
|
||||
*/
|
||||
public function remove($session_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $session_id)
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current RememberMe session and the cookie
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
*/
|
||||
public function destroy($user_id)
|
||||
{
|
||||
$credentials = $this->readCookie();
|
||||
|
||||
if ($credentials !== false) {
|
||||
|
||||
$this->deleteCookie();
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->eq('token', $credentials['token'])
|
||||
->remove();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new RememberMe session
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @param string $ip IP Address
|
||||
* @param string $user_agent User Agent
|
||||
* @return array
|
||||
*/
|
||||
public function create($user_id, $ip, $user_agent)
|
||||
{
|
||||
$token = hash('sha256', $user_id.$user_agent.$ip.$this->generateToken());
|
||||
$sequence = $this->generateToken();
|
||||
$expiration = time() + self::EXPIRATION;
|
||||
|
||||
$this->cleanup($user_id);
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->insert(array(
|
||||
'user_id' => $user_id,
|
||||
'ip' => $ip,
|
||||
'user_agent' => $user_agent,
|
||||
'token' => $token,
|
||||
'sequence' => $sequence,
|
||||
'expiration' => $expiration,
|
||||
'date_creation' => time(),
|
||||
));
|
||||
|
||||
return array(
|
||||
'token' => $token,
|
||||
'sequence' => $sequence,
|
||||
'expiration' => $expiration,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old sessions for a given user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return bool
|
||||
*/
|
||||
public function cleanup($user_id)
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('user_id', $user_id)
|
||||
->lt('expiration', time())
|
||||
->remove();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new sequence token and update the database
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @return string
|
||||
*/
|
||||
public function update($token, $sequence)
|
||||
{
|
||||
$new_sequence = $this->generateToken();
|
||||
|
||||
$this->db
|
||||
->table(self::TABLE)
|
||||
->eq('token', $token)
|
||||
->eq('sequence', $sequence)
|
||||
->update(array('sequence' => $new_sequence));
|
||||
|
||||
return $new_sequence;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @return string
|
||||
*/
|
||||
public function encodeCookie($token, $sequence)
|
||||
{
|
||||
return implode('|', array($token, $sequence));
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the value of a cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Raw cookie data
|
||||
* @return array
|
||||
*/
|
||||
public function decodeCookie($value)
|
||||
{
|
||||
list($token, $sequence) = explode('|', $value);
|
||||
|
||||
return array(
|
||||
'token' => $token,
|
||||
'sequence' => $sequence,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the current user has a RememberMe cookie
|
||||
*
|
||||
* @access public
|
||||
* @return bool
|
||||
*/
|
||||
public function hasCookie()
|
||||
{
|
||||
return ! empty($_COOKIE[self::COOKIE_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write and encode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @param string $token Session token
|
||||
* @param string $sequence Sequence token
|
||||
* @param string $expiration Cookie expiration
|
||||
*/
|
||||
public function writeCookie($token, $sequence, $expiration)
|
||||
{
|
||||
setcookie(
|
||||
self::COOKIE_NAME,
|
||||
$this->encodeCookie($token, $sequence),
|
||||
$expiration,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
! empty($_SERVER['HTTPS']),
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Read and decode the cookie
|
||||
*
|
||||
* @access public
|
||||
* @return mixed
|
||||
*/
|
||||
public function readCookie()
|
||||
{
|
||||
if (empty($_COOKIE[self::COOKIE_NAME])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->decodeCookie($_COOKIE[self::COOKIE_NAME]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the cookie
|
||||
*
|
||||
* @access public
|
||||
*/
|
||||
public function deleteCookie()
|
||||
{
|
||||
setcookie(
|
||||
self::COOKIE_NAME,
|
||||
'',
|
||||
time() - 3600,
|
||||
BASE_URL_DIRECTORY,
|
||||
null,
|
||||
! empty($_SERVER['HTTPS']),
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
627
app/Model/Task.php
Normal file
627
app/Model/Task.php
Normal file
@@ -0,0 +1,627 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
use DateTime;
|
||||
|
||||
/**
|
||||
* Task model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class Task extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'tasks';
|
||||
|
||||
/**
|
||||
* Task status
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const STATUS_OPEN = 1;
|
||||
const STATUS_CLOSED = 0;
|
||||
|
||||
/**
|
||||
* Events
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const EVENT_MOVE_COLUMN = 'task.move.column';
|
||||
const EVENT_MOVE_POSITION = 'task.move.position';
|
||||
const EVENT_UPDATE = 'task.update';
|
||||
const EVENT_CREATE = 'task.create';
|
||||
const EVENT_CLOSE = 'task.close';
|
||||
const EVENT_OPEN = 'task.open';
|
||||
const EVENT_CREATE_UPDATE = 'task.create_update';
|
||||
|
||||
/**
|
||||
* Get available colors
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getColors()
|
||||
{
|
||||
return array(
|
||||
'yellow' => t('Yellow'),
|
||||
'blue' => t('Blue'),
|
||||
'green' => t('Green'),
|
||||
'purple' => t('Purple'),
|
||||
'red' => t('Red'),
|
||||
'orange' => t('Orange'),
|
||||
'grey' => t('Grey'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch one task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param boolean $more If true, fetch all related information
|
||||
* @return array
|
||||
*/
|
||||
public function getById($task_id, $more = false)
|
||||
{
|
||||
if ($more) {
|
||||
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
self::TABLE.'.id',
|
||||
self::TABLE.'.title',
|
||||
self::TABLE.'.description',
|
||||
self::TABLE.'.date_creation',
|
||||
self::TABLE.'.date_completed',
|
||||
self::TABLE.'.date_due',
|
||||
self::TABLE.'.color_id',
|
||||
self::TABLE.'.project_id',
|
||||
self::TABLE.'.column_id',
|
||||
self::TABLE.'.owner_id',
|
||||
self::TABLE.'.position',
|
||||
self::TABLE.'.is_active',
|
||||
self::TABLE.'.score',
|
||||
self::TABLE.'.category_id',
|
||||
Category::TABLE.'.name AS category_name',
|
||||
Project::TABLE.'.name AS project_name',
|
||||
Board::TABLE.'.title AS column_title',
|
||||
User::TABLE.'.username'
|
||||
)
|
||||
->join(Category::TABLE, 'id', 'category_id')
|
||||
->join(Project::TABLE, 'id', 'project_id')
|
||||
->join(Board::TABLE, 'id', 'column_id')
|
||||
->join(User::TABLE, 'id', 'owner_id')
|
||||
->eq(self::TABLE.'.id', $task_id)
|
||||
->findOne();
|
||||
}
|
||||
else {
|
||||
|
||||
return $this->db->table(self::TABLE)->eq('id', $task_id)->findOne();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Count all tasks for a given project and status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param array $status List of status id
|
||||
* @return integer
|
||||
*/
|
||||
public function countByProjectId($project_id, array $status = array(self::STATUS_OPEN, self::STATUS_CLOSED))
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->in('is_active', $status)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get tasks that match defined filters
|
||||
*
|
||||
* @access public
|
||||
* @param array $filters Filters: [ ['column' => '...', 'operator' => '...', 'value' => '...'], ... ]
|
||||
* @param array $sorting Sorting: [ 'column' => 'date_creation', 'direction' => 'asc']
|
||||
* @return array
|
||||
*/
|
||||
public function find(array $filters, array $sorting = array())
|
||||
{
|
||||
$table = $this->db
|
||||
->table(self::TABLE)
|
||||
->columns(
|
||||
'(SELECT count(*) FROM comments WHERE task_id=tasks.id) AS nb_comments',
|
||||
'tasks.id',
|
||||
'tasks.title',
|
||||
'tasks.description',
|
||||
'tasks.date_creation',
|
||||
'tasks.date_completed',
|
||||
'tasks.date_due',
|
||||
'tasks.color_id',
|
||||
'tasks.project_id',
|
||||
'tasks.column_id',
|
||||
'tasks.owner_id',
|
||||
'tasks.position',
|
||||
'tasks.is_active',
|
||||
'tasks.score',
|
||||
'tasks.category_id',
|
||||
'users.username'
|
||||
)
|
||||
->join('users', 'id', 'owner_id');
|
||||
|
||||
foreach ($filters as $key => $filter) {
|
||||
|
||||
if ($key === 'or') {
|
||||
|
||||
$table->beginOr();
|
||||
|
||||
foreach ($filter as $subfilter) {
|
||||
$table->$subfilter['operator']($subfilter['column'], $subfilter['value']);
|
||||
}
|
||||
|
||||
$table->closeOr();
|
||||
}
|
||||
else if (isset($filter['operator']) && isset($filter['column']) && isset($filter['value'])) {
|
||||
$table->$filter['operator']($filter['column'], $filter['value']);
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($sorting)) {
|
||||
$table->orderBy('tasks.position', 'ASC');
|
||||
}
|
||||
else {
|
||||
$table->orderBy($sorting['column'], $sorting['direction']);
|
||||
}
|
||||
|
||||
return $table->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Count the number of tasks for a given column and status
|
||||
*
|
||||
* @access public
|
||||
* @param integer $project_id Project id
|
||||
* @param integer $column_id Column id
|
||||
* @param array $status List of status id
|
||||
* @return integer
|
||||
*/
|
||||
public function countByColumnId($project_id, $column_id, array $status = array(self::STATUS_OPEN))
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('project_id', $project_id)
|
||||
->eq('column_id', $column_id)
|
||||
->in('is_active', $status)
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicate($task_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
|
||||
// Get the original task
|
||||
$task = $this->getById($task_id);
|
||||
|
||||
// Cleanup data
|
||||
unset($task['id']);
|
||||
unset($task['date_completed']);
|
||||
|
||||
// Assign new values
|
||||
$task['date_creation'] = time();
|
||||
$task['is_active'] = 1;
|
||||
$task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']);
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(self::TABLE)->save($task)) {
|
||||
$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) + $task);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
|
||||
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Duplicate a task to another project (always copy to the first column)
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @param integer $project_id Destination project id
|
||||
* @return boolean
|
||||
*/
|
||||
public function duplicateToAnotherProject($task_id, $project_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
$boardModel = new Board($this->db, $this->event);
|
||||
|
||||
// Get the original task
|
||||
$task = $this->getById($task_id);
|
||||
|
||||
// Cleanup data
|
||||
unset($task['id']);
|
||||
unset($task['date_completed']);
|
||||
|
||||
// Assign new values
|
||||
$task['date_creation'] = time();
|
||||
$task['owner_id'] = 0;
|
||||
$task['category_id'] = 0;
|
||||
$task['is_active'] = 1;
|
||||
$task['column_id'] = $boardModel->getFirstColumn($project_id);
|
||||
$task['project_id'] = $project_id;
|
||||
$task['position'] = $this->countByColumnId($task['project_id'], $task['column_id']);
|
||||
|
||||
// Save task
|
||||
if (! $this->db->table(self::TABLE)->save($task)) {
|
||||
$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) + $task);
|
||||
$this->event->trigger(self::EVENT_CREATE, array('task_id' => $task_id) + $task);
|
||||
|
||||
return $task_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a task
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// Prepare data
|
||||
if (isset($values['another_task'])) {
|
||||
unset($values['another_task']);
|
||||
}
|
||||
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
$values['date_creation'] = time();
|
||||
$values['position'] = $this->countByColumnId($values['project_id'], $values['column_id']);
|
||||
|
||||
// Save task
|
||||
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
|
||||
* @return boolean
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
// Prepare data
|
||||
if (! empty($values['date_due']) && ! is_numeric($values['date_due'])) {
|
||||
$values['date_due'] = $this->parseDate($values['date_due']);
|
||||
}
|
||||
|
||||
$original_task = $this->getById($values['id']);
|
||||
|
||||
if ($original_task === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$updated_task = $values;
|
||||
unset($updated_task['id']);
|
||||
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($updated_task);
|
||||
|
||||
// Trigger events
|
||||
if ($result) {
|
||||
|
||||
$events = array();
|
||||
|
||||
if (! in_array($this->event->getLastTriggeredEvent(), array(self::EVENT_CREATE_UPDATE))) {
|
||||
$events[] = self::EVENT_CREATE_UPDATE;
|
||||
$events[] = self::EVENT_UPDATE;
|
||||
}
|
||||
|
||||
if (isset($values['column_id']) && $original_task['column_id'] != $values['column_id']) {
|
||||
$events[] = self::EVENT_MOVE_COLUMN;
|
||||
}
|
||||
else if (isset($values['position']) && $original_task['position'] != $values['position']) {
|
||||
$events[] = self::EVENT_MOVE_POSITION;
|
||||
}
|
||||
|
||||
$event_data = array_merge($original_task, $values);
|
||||
$event_data['task_id'] = $original_task['id'];
|
||||
|
||||
foreach ($events as $event) {
|
||||
$this->event->trigger($event, $event_data);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task closed
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function close($task_id)
|
||||
{
|
||||
$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->getById($task_id));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Mark a task open
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function open($task_id)
|
||||
{
|
||||
$result = $this->db
|
||||
->table(self::TABLE)
|
||||
->eq('id', $task_id)
|
||||
->update(array(
|
||||
'is_active' => 1,
|
||||
'date_completed' => ''
|
||||
));
|
||||
|
||||
if ($result) {
|
||||
$this->event->trigger(self::EVENT_OPEN, array('task_id' => $task_id) + $this->getById($task_id));
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a task
|
||||
*
|
||||
* @access public
|
||||
* @param integer $task_id Task id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($task_id)
|
||||
{
|
||||
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 $task_id Task id
|
||||
* @param integer $column_id Column id
|
||||
* @param integer $position Position (must be greater than 1)
|
||||
* @return boolean
|
||||
*/
|
||||
public function move($task_id, $column_id, $position)
|
||||
{
|
||||
return $this->update(array(
|
||||
'id' => $task_id,
|
||||
'column_id' => $column_id,
|
||||
'position' => $position,
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate task creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('color_id', t('The color is required')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('column_id', t('The column is required')),
|
||||
new Validators\Integer('column_id', t('This value must be an integer')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
new Validators\Integer('score', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
|
||||
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate description creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateDescriptionCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('description', t('The description is required')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate task modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('color_id', t('The color is required')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('column_id', t('The column is required')),
|
||||
new Validators\Integer('column_id', t('This value must be an integer')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
new Validators\Integer('score', t('This value must be an integer')),
|
||||
new Validators\Required('title', t('The title is required')),
|
||||
new Validators\MaxLength('title', t('The maximum length is %d characters', 200), 200),
|
||||
new Validators\Date('date_due', t('Invalid date'), $this->getDateFormats()),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate assignee change
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateAssigneeModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The id is required')),
|
||||
new Validators\Integer('id', t('This value must be an integer')),
|
||||
new Validators\Required('project_id', t('The project is required')),
|
||||
new Validators\Integer('project_id', t('This value must be an integer')),
|
||||
new Validators\Required('owner_id', t('This value is required')),
|
||||
new Validators\Integer('owner_id', t('This value must be an integer')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a timestamp if the given date format is correct otherwise return 0
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
* @param string $format Date format
|
||||
* @return integer
|
||||
*/
|
||||
public function getValidDate($value, $format)
|
||||
{
|
||||
$date = DateTime::createFromFormat($format, $value);
|
||||
|
||||
if ($date !== false) {
|
||||
$errors = DateTime::getLastErrors();
|
||||
if ($errors['error_count'] === 0 && $errors['warning_count'] === 0) {
|
||||
$timestamp = $date->getTimestamp();
|
||||
return $timestamp > 0 ? $timestamp : 0;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a date ad return a unix timestamp, try different date formats
|
||||
*
|
||||
* @access public
|
||||
* @param string $value Date to parse
|
||||
* @return integer
|
||||
*/
|
||||
public function parseDate($value)
|
||||
{
|
||||
foreach ($this->getDateFormats() as $format) {
|
||||
|
||||
$timestamp = $this->getValidDate($value, $format);
|
||||
|
||||
if ($timestamp !== 0) {
|
||||
return $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of supported date formats
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getDateFormats()
|
||||
{
|
||||
return array(
|
||||
t('m/d/Y'),
|
||||
'Y-m-d',
|
||||
'Y_m_d',
|
||||
);
|
||||
}
|
||||
}
|
||||
426
app/Model/User.php
Normal file
426
app/Model/User.php
Normal file
@@ -0,0 +1,426 @@
|
||||
<?php
|
||||
|
||||
namespace Model;
|
||||
|
||||
use SimpleValidator\Validator;
|
||||
use SimpleValidator\Validators;
|
||||
|
||||
/**
|
||||
* User model
|
||||
*
|
||||
* @package model
|
||||
* @author Frederic Guillot
|
||||
*/
|
||||
class User extends Base
|
||||
{
|
||||
/**
|
||||
* SQL table name
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TABLE = 'users';
|
||||
|
||||
/**
|
||||
* Id used for everbody (filtering)
|
||||
*
|
||||
* @var integer
|
||||
*/
|
||||
const EVERYBODY_ID = -1;
|
||||
|
||||
/**
|
||||
* Get a specific user by id
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return array
|
||||
*/
|
||||
public function getById($user_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('id', $user_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user by the Google id
|
||||
*
|
||||
* @access public
|
||||
* @param string $google_id Google unique id
|
||||
* @return array
|
||||
*/
|
||||
public function getByGoogleId($google_id)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('google_id', $google_id)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific user by the username
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @return array
|
||||
*/
|
||||
public function getByUsername($username)
|
||||
{
|
||||
return $this->db->table(self::TABLE)->eq('username', $username)->findOne();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all users
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getAll()
|
||||
{
|
||||
return $this->db
|
||||
->table(self::TABLE)
|
||||
->asc('username')
|
||||
->columns('id', 'username', 'name', 'email', 'is_admin', 'default_project_id', 'is_ldap_user')
|
||||
->findAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all users (key-value pairs with id/username)
|
||||
*
|
||||
* @access public
|
||||
* @return array
|
||||
*/
|
||||
public function getList()
|
||||
{
|
||||
return $this->db->table(self::TABLE)->asc('username')->listing('id', 'username');
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new user in the database
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return boolean
|
||||
*/
|
||||
public function create(array $values)
|
||||
{
|
||||
if (isset($values['confirmation'])) {
|
||||
unset($values['confirmation']);
|
||||
}
|
||||
|
||||
if (isset($values['password'])) {
|
||||
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
|
||||
return $this->db->table(self::TABLE)->save($values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify a new user
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array
|
||||
*/
|
||||
public function update(array $values)
|
||||
{
|
||||
if (! empty($values['password'])) {
|
||||
$values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT);
|
||||
}
|
||||
else {
|
||||
unset($values['password']);
|
||||
}
|
||||
|
||||
if (isset($values['confirmation'])) {
|
||||
unset($values['confirmation']);
|
||||
}
|
||||
|
||||
if (isset($values['current_password'])) {
|
||||
unset($values['current_password']);
|
||||
}
|
||||
|
||||
$result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values);
|
||||
|
||||
if ($_SESSION['user']['id'] == $values['id']) {
|
||||
$this->updateSession();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a specific user
|
||||
*
|
||||
* @access public
|
||||
* @param integer $user_id User id
|
||||
* @return boolean
|
||||
*/
|
||||
public function remove($user_id)
|
||||
{
|
||||
$this->db->startTransaction();
|
||||
|
||||
// All tasks assigned to this user will be unassigned
|
||||
$this->db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => ''));
|
||||
$this->db->table(self::TABLE)->eq('id', $user_id)->remove();
|
||||
|
||||
$this->db->closeTransaction();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user session information
|
||||
*
|
||||
* @access public
|
||||
* @param array $user User data
|
||||
*/
|
||||
public function updateSession(array $user = array())
|
||||
{
|
||||
if (empty($user)) {
|
||||
$user = $this->getById($_SESSION['user']['id']);
|
||||
}
|
||||
|
||||
if (isset($user['password'])) {
|
||||
unset($user['password']);
|
||||
}
|
||||
|
||||
$user['id'] = (int) $user['id'];
|
||||
$user['default_project_id'] = (int) $user['default_project_id'];
|
||||
$user['is_admin'] = (bool) $user['is_admin'];
|
||||
$user['is_ldap_user'] = (bool) $user['is_ldap_user'];
|
||||
|
||||
$_SESSION['user'] = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user creation
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateCreation(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\AlphaNumeric('username', t('The username must be alphanumeric')),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Required('confirmation', t('The confirmation is required')),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords doesn\'t matches')),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateModification(array $values)
|
||||
{
|
||||
if (! empty($values['password'])) {
|
||||
return $this->validatePasswordModification($values);
|
||||
}
|
||||
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The user id is required')),
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\AlphaNumeric('username', t('The username must be alphanumeric')),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
|
||||
return array(
|
||||
$v->execute(),
|
||||
$v->getErrors()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate password modification
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validatePasswordModification(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('id', t('The user id is required')),
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\AlphaNumeric('username', t('The username must be alphanumeric')),
|
||||
new Validators\Unique('username', t('The username must be unique'), $this->db->getConnection(), self::TABLE, 'id'),
|
||||
new Validators\Required('current_password', t('The current password is required')),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
new Validators\MinLength('password', t('The minimum length is %d characters', 6), 6),
|
||||
new Validators\Required('confirmation', t('The confirmation is required')),
|
||||
new Validators\Equals('password', 'confirmation', t('Passwords doesn\'t matches')),
|
||||
new Validators\Integer('default_project_id', t('This value must be an integer')),
|
||||
new Validators\Integer('is_admin', t('This value must be an integer')),
|
||||
new Validators\Email('email', t('Email address invalid')),
|
||||
));
|
||||
|
||||
if ($v->execute()) {
|
||||
|
||||
// Check password
|
||||
list($authenticated,) = $this->authenticate($_SESSION['user']['username'], $values['current_password']);
|
||||
|
||||
if ($authenticated) {
|
||||
return array(true, array());
|
||||
}
|
||||
else {
|
||||
return array(false, array('current_password' => array(t('Wrong password'))));
|
||||
}
|
||||
}
|
||||
|
||||
return array(false, $v->getErrors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate user login
|
||||
*
|
||||
* @access public
|
||||
* @param array $values Form values
|
||||
* @return array $valid, $errors [0] = Success or not, [1] = List of errors
|
||||
*/
|
||||
public function validateLogin(array $values)
|
||||
{
|
||||
$v = new Validator($values, array(
|
||||
new Validators\Required('username', t('The username is required')),
|
||||
new Validators\MaxLength('username', t('The maximum length is %d characters', 50), 50),
|
||||
new Validators\Required('password', t('The password is required')),
|
||||
));
|
||||
|
||||
$result = $v->execute();
|
||||
$errors = $v->getErrors();
|
||||
|
||||
if ($result) {
|
||||
|
||||
list($authenticated, $method) = $this->authenticate($values['username'], $values['password']);
|
||||
|
||||
if ($authenticated === true) {
|
||||
|
||||
// Create the user session
|
||||
$user = $this->getByUsername($values['username']);
|
||||
$this->updateSession($user);
|
||||
|
||||
// Update login history
|
||||
$lastLogin = new LastLogin($this->db, $this->event);
|
||||
$lastLogin->create(
|
||||
$method,
|
||||
$user['id'],
|
||||
$this->getIpAddress(),
|
||||
$this->getUserAgent()
|
||||
);
|
||||
|
||||
// Setup the remember me feature
|
||||
if (! empty($values['remember_me'])) {
|
||||
$rememberMe = new RememberMe($this->db, $this->event);
|
||||
$credentials = $rememberMe->create($user['id'], $this->getIpAddress(), $this->getUserAgent());
|
||||
$rememberMe->writeCookie($credentials['token'], $credentials['sequence'], $credentials['expiration']);
|
||||
}
|
||||
}
|
||||
else {
|
||||
$result = false;
|
||||
$errors['login'] = t('Bad username or password');
|
||||
}
|
||||
}
|
||||
|
||||
return array(
|
||||
$result,
|
||||
$errors
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate a user
|
||||
*
|
||||
* @access public
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return array
|
||||
*/
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
// Database authentication
|
||||
$user = $this->db->table(self::TABLE)->eq('username', $username)->eq('is_ldap_user', 0)->findOne();
|
||||
$authenticated = $user && \password_verify($password, $user['password']);
|
||||
$method = LastLogin::AUTH_DATABASE;
|
||||
|
||||
// LDAP authentication
|
||||
if (! $authenticated && LDAP_AUTH) {
|
||||
require __DIR__.'/ldap.php';
|
||||
$ldap = new Ldap($this->db, $this->event);
|
||||
$authenticated = $ldap->authenticate($username, $password);
|
||||
$method = LastLogin::AUTH_LDAP;
|
||||
}
|
||||
|
||||
return array($authenticated, $method);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user agent of the connected user
|
||||
*
|
||||
* @access public
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent()
|
||||
{
|
||||
return empty($_SERVER['HTTP_USER_AGENT']) ? t('Unknown') : $_SERVER['HTTP_USER_AGENT'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the real IP address of the connected user
|
||||
*
|
||||
* @access public
|
||||
* @param bool $only_public Return only public IP address
|
||||
* @return string
|
||||
*/
|
||||
public function getIpAddress($only_public = false)
|
||||
{
|
||||
$keys = array(
|
||||
'HTTP_CLIENT_IP',
|
||||
'HTTP_X_FORWARDED_FOR',
|
||||
'HTTP_X_FORWARDED',
|
||||
'HTTP_X_CLUSTER_CLIENT_IP',
|
||||
'HTTP_FORWARDED_FOR',
|
||||
'HTTP_FORWARDED',
|
||||
'REMOTE_ADDR'
|
||||
);
|
||||
|
||||
foreach ($keys as $key) {
|
||||
|
||||
if (isset($_SERVER[$key])) {
|
||||
|
||||
foreach (explode(',', $_SERVER[$key]) as $ip_address) {
|
||||
|
||||
$ip_address = trim($ip_address);
|
||||
|
||||
if ($only_public) {
|
||||
|
||||
// Return only public IP address
|
||||
if (filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {
|
||||
return $ip_address;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
return $ip_address;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t('Unknown');
|
||||
}
|
||||
}
|
||||
236
app/Schema/Mysql.php
Normal file
236
app/Schema/Mysql.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
|
||||
namespace Schema;
|
||||
|
||||
const VERSION = 16;
|
||||
|
||||
function version_16($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_has_categories (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(255),
|
||||
project_id INT,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY `idx_project_category` (project_id, name),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8"
|
||||
);
|
||||
|
||||
$pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INT DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_15($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INT DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_14($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN name VARCHAR(255)");
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN email VARCHAR(255)");
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN google_id VARCHAR(30)");
|
||||
}
|
||||
|
||||
function version_13($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN is_ldap_user TINYINT(1) DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_12($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE remember_me (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
user_id INT,
|
||||
ip VARCHAR(40),
|
||||
user_agent VARCHAR(255),
|
||||
token VARCHAR(255),
|
||||
sequence VARCHAR(255),
|
||||
expiration INT,
|
||||
date_creation INT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8"
|
||||
);
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE last_logins (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
auth_type VARCHAR(25),
|
||||
user_id INT,
|
||||
ip VARCHAR(40),
|
||||
user_agent VARCHAR(255),
|
||||
date_creation INT,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (id),
|
||||
INDEX (user_id)
|
||||
) ENGINE=InnoDB CHARSET=utf8"
|
||||
);
|
||||
}
|
||||
|
||||
function version_11($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_10($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_9($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_8($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_7($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_6($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_5($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_4($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_3($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_2($pdo)
|
||||
{
|
||||
}
|
||||
|
||||
function version_1($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
language CHAR(5) DEFAULT 'en_US',
|
||||
webhooks_token VARCHAR(255),
|
||||
timezone VARCHAR(50) DEFAULT 'UTC'
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE users (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
username VARCHAR(50),
|
||||
password VARCHAR(255),
|
||||
is_admin TINYINT DEFAULT 0,
|
||||
default_project_id INT DEFAULT 0,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE projects (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
name VARCHAR(50) UNIQUE,
|
||||
is_active TINYINT DEFAULT 1,
|
||||
token VARCHAR(255),
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_has_users (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
project_id INT,
|
||||
user_id INT,
|
||||
PRIMARY KEY (id),
|
||||
UNIQUE KEY `idx_project_user` (project_id, user_id),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE columns (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
title VARCHAR(255),
|
||||
position INT NOT NULL,
|
||||
project_id INT NOT NULL,
|
||||
task_limit INT DEFAULT '0',
|
||||
UNIQUE KEY `idx_title_project` (title, project_id),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE tasks (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
title VARCHAR(255),
|
||||
description TEXT,
|
||||
date_creation INT,
|
||||
date_completed INT,
|
||||
date_due INT,
|
||||
color_id VARCHAR(50),
|
||||
project_id INT,
|
||||
column_id INT,
|
||||
owner_id INT DEFAULT '0',
|
||||
position INT,
|
||||
score INT,
|
||||
is_active TINYINT DEFAULT 1,
|
||||
PRIMARY KEY (id),
|
||||
INDEX `idx_task_active` (is_active),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE comments (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
task_id INT,
|
||||
user_id INT,
|
||||
date INT,
|
||||
comment TEXT,
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE actions (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
project_id INT,
|
||||
event_name VARCHAR(50),
|
||||
action_name VARCHAR(50),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE action_has_params (
|
||||
id INT NOT NULL AUTO_INCREMENT,
|
||||
action_id INT,
|
||||
name VARCHAR(50),
|
||||
value VARCHAR(50),
|
||||
PRIMARY KEY (id),
|
||||
FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB CHARSET=utf8
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO users
|
||||
(username, password, is_admin)
|
||||
VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1')
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO config
|
||||
(webhooks_token)
|
||||
VALUES ('".\Model\Base::generateToken()."')
|
||||
");
|
||||
}
|
||||
259
app/Schema/Sqlite.php
Normal file
259
app/Schema/Sqlite.php
Normal file
@@ -0,0 +1,259 @@
|
||||
<?php
|
||||
|
||||
namespace Schema;
|
||||
|
||||
const VERSION = 16;
|
||||
|
||||
function version_16($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_has_categories (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT COLLATE NOCASE,
|
||||
project_id INT,
|
||||
UNIQUE (project_id, name),
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)"
|
||||
);
|
||||
|
||||
$pdo->exec("ALTER TABLE tasks ADD COLUMN category_id INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_15($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE projects ADD COLUMN last_modified INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_14($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN name TEXT");
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN email TEXT");
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN google_id TEXT");
|
||||
}
|
||||
|
||||
function version_13($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE users ADD COLUMN is_ldap_user INTEGER DEFAULT 0");
|
||||
}
|
||||
|
||||
function version_12($pdo)
|
||||
{
|
||||
$pdo->exec(
|
||||
'CREATE TABLE remember_me (
|
||||
id INTEGER PRIMARY KEY,
|
||||
user_id INTEGER,
|
||||
ip TEXT,
|
||||
user_agent TEXT,
|
||||
token TEXT,
|
||||
sequence TEXT,
|
||||
expiration INTEGER,
|
||||
date_creation INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE last_logins (
|
||||
id INTEGER PRIMARY KEY,
|
||||
auth_type TEXT,
|
||||
user_id INTEGER,
|
||||
ip TEXT,
|
||||
user_agent TEXT,
|
||||
date_creation INTEGER,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
|
||||
$pdo->exec('CREATE INDEX last_logins_user_idx ON last_logins(user_id)');
|
||||
}
|
||||
|
||||
function version_11($pdo)
|
||||
{
|
||||
$pdo->exec(
|
||||
'ALTER TABLE comments RENAME TO comments_bak'
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE comments (
|
||||
id INTEGER PRIMARY KEY,
|
||||
task_id INTEGER,
|
||||
user_id INTEGER,
|
||||
date INTEGER,
|
||||
comment TEXT,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
'INSERT INTO comments SELECT * FROM comments_bak'
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
'DROP TABLE comments_bak'
|
||||
);
|
||||
}
|
||||
|
||||
function version_10($pdo)
|
||||
{
|
||||
$pdo->exec(
|
||||
'CREATE TABLE actions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
project_id INTEGER,
|
||||
event_name TEXT,
|
||||
action_name TEXT,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
|
||||
$pdo->exec(
|
||||
'CREATE TABLE action_has_params (
|
||||
id INTEGER PRIMARY KEY,
|
||||
action_id INTEGER,
|
||||
name TEXT,
|
||||
value TEXT,
|
||||
FOREIGN KEY(action_id) REFERENCES actions(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
function version_9($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE tasks ADD COLUMN date_due INTEGER");
|
||||
}
|
||||
|
||||
function version_8($pdo)
|
||||
{
|
||||
$pdo->exec(
|
||||
'CREATE TABLE comments (
|
||||
id INTEGER PRIMARY KEY,
|
||||
task_id INTEGER,
|
||||
user_id INTEGER,
|
||||
date INTEGER,
|
||||
comment TEXT,
|
||||
FOREIGN KEY(task_id) REFERENCES tasks(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES tasks(id) ON DELETE CASCADE
|
||||
)'
|
||||
);
|
||||
}
|
||||
|
||||
function version_7($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE project_has_users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
project_id INTEGER,
|
||||
user_id INTEGER,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(user_id) REFERENCES users(id) ON DELETE CASCADE,
|
||||
UNIQUE(project_id, user_id)
|
||||
)
|
||||
");
|
||||
}
|
||||
|
||||
function version_6($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE columns ADD COLUMN task_limit INTEGER DEFAULT '0'");
|
||||
}
|
||||
|
||||
function version_5($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE tasks ADD COLUMN score INTEGER");
|
||||
}
|
||||
|
||||
function version_4($pdo)
|
||||
{
|
||||
$pdo->exec("ALTER TABLE config ADD COLUMN timezone TEXT DEFAULT 'UTC'");
|
||||
}
|
||||
|
||||
function version_3($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE projects ADD COLUMN token TEXT');
|
||||
|
||||
// For each existing project, assign a different token
|
||||
$rq = $pdo->prepare("SELECT id FROM projects WHERE token IS NULL");
|
||||
$rq->execute();
|
||||
$results = $rq->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
if ($results !== false) {
|
||||
|
||||
foreach ($results as &$result) {
|
||||
$rq = $pdo->prepare('UPDATE projects SET token=? WHERE id=?');
|
||||
$rq->execute(array(\Model\Base::generateToken(), $result['id']));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function version_2($pdo)
|
||||
{
|
||||
$pdo->exec('ALTER TABLE tasks ADD COLUMN date_completed INTEGER');
|
||||
$pdo->exec('UPDATE tasks SET date_completed=date_creation WHERE is_active=0');
|
||||
}
|
||||
|
||||
function version_1($pdo)
|
||||
{
|
||||
$pdo->exec("
|
||||
CREATE TABLE config (
|
||||
language TEXT,
|
||||
webhooks_token TEXT
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE users (
|
||||
id INTEGER PRIMARY KEY,
|
||||
username TEXT,
|
||||
password TEXT,
|
||||
is_admin INTEGER DEFAULT 0,
|
||||
default_project_id INTEGER DEFAULT 0
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE projects (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOCASE UNIQUE,
|
||||
is_active INTEGER DEFAULT 1
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE columns (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT,
|
||||
position INTEGER,
|
||||
project_id INTEGER,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
UNIQUE (title, project_id)
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
CREATE TABLE tasks (
|
||||
id INTEGER PRIMARY KEY,
|
||||
title TEXT,
|
||||
description TEXT,
|
||||
date_creation INTEGER,
|
||||
color_id TEXT,
|
||||
project_id INTEGER,
|
||||
column_id INTEGER,
|
||||
owner_id INTEGER DEFAULT '0',
|
||||
position INTEGER,
|
||||
is_active INTEGER DEFAULT 1,
|
||||
FOREIGN KEY(project_id) REFERENCES projects(id) ON DELETE CASCADE,
|
||||
FOREIGN KEY(column_id) REFERENCES columns(id) ON DELETE CASCADE
|
||||
)
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO users
|
||||
(username, password, is_admin)
|
||||
VALUES ('admin', '".\password_hash('admin', PASSWORD_BCRYPT)."', '1')
|
||||
");
|
||||
|
||||
$pdo->exec("
|
||||
INSERT INTO config
|
||||
(language, webhooks_token)
|
||||
VALUES ('en_US', '".\Model\Base::generateToken()."')
|
||||
");
|
||||
}
|
||||
77
app/Templates/action_index.php
Normal file
77
app/Templates/action_index.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<?php if (! empty($actions)): ?>
|
||||
|
||||
<h3><?= t('Defined actions') ?></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Event name') ?></th>
|
||||
<th><?= t('Action name') ?></th>
|
||||
<th><?= t('Action parameters') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($actions as $action): ?>
|
||||
<tr>
|
||||
<td><?= Helper\in_list($action['event_name'], $available_events) ?></td>
|
||||
<td><?= Helper\in_list($action['action_name'], $available_actions) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php foreach ($action['params'] as $param): ?>
|
||||
<li>
|
||||
<?= Helper\in_list($param['name'], $available_params) ?> =
|
||||
<strong>
|
||||
<?php if (Helper\contains($param['name'], 'column_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $columns_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'user_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $users_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'project_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $projects_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'color_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $colors_list) ?>
|
||||
<?php elseif (Helper\contains($param['name'], 'category_id')): ?>
|
||||
<?= Helper\in_list($param['value'], $categories_list) ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?controller=action&action=confirm&action_id=<?= $action['id'] ?>"><?= t('Remove') ?></a>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
|
||||
</table>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add an action') ?></h3>
|
||||
<form method="post" action="?controller=action&action=params&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Event'), 'event_name') ?>
|
||||
<?= Helper\form_select('event_name', $available_events, $values) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Action'), 'action_name') ?>
|
||||
<?= Helper\form_select('action_name', $available_actions, $values) ?><br/>
|
||||
|
||||
<div class="form-help">
|
||||
<?= t('When the selected event occurs execute the corresponding action.') ?>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Next step') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
43
app/Templates/action_params.php
Normal file
43
app/Templates/action_params.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Automatic actions for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Define action parameters') ?></h3>
|
||||
<form method="post" action="?controller=action&action=create&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_hidden('event_name', $values) ?>
|
||||
<?= Helper\form_hidden('action_name', $values) ?>
|
||||
|
||||
<?php foreach ($action_params as $param_name => $param_desc): ?>
|
||||
|
||||
<?php if (Helper\contains($param_name, 'column_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $columns_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'user_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $users_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'project_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $projects_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'color_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $colors_list, $values) ?><br/>
|
||||
<?php elseif (Helper\contains($param_name, 'category_id')): ?>
|
||||
<?= Helper\form_label($param_desc, $param_name) ?>
|
||||
<?= Helper\form_select('params['.$param_name.']', $categories_list, $values) ?><br/>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save this action') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
16
app/Templates/action_remove.php
Normal file
16
app/Templates/action_remove.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove an automatic action') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this action: "%s"?', Helper\in_list($action['event_name'], $available_events).'/'.Helper\in_list($action['action_name'], $available_actions)) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=action&action=remove&action_id=<?= $action['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $action['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
9
app/Templates/app_notfound.php
Normal file
9
app/Templates/app_notfound.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Page not found') ?></h2>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-error">
|
||||
<?= t('Sorry, I didn\'t found this information in my database!') ?>
|
||||
</p>
|
||||
</section>
|
||||
35
app/Templates/board_assign.php
Normal file
35
app/Templates/board_assign.php
Normal file
@@ -0,0 +1,35 @@
|
||||
<section id="main">
|
||||
|
||||
<div class="page-header board">
|
||||
<h2>
|
||||
<?= t('Project "%s"', $current_project_name) ?>
|
||||
</h2>
|
||||
<ul>
|
||||
<?php foreach ($projects as $project_id => $project_name): ?>
|
||||
<?php if ($project_id != $current_project_id): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=show&project_id=<?= $project_id ?>"><?= Helper\escape($project_name) ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<section>
|
||||
<h3><?= t('Change assignee for the task "%s"', $values['title']) ?></h3>
|
||||
<form method="post" action="?controller=board&action=assignTask" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Assignee'), 'owner_id') ?>
|
||||
<?= Helper\form_select('owner_id', $users_list, $values, $errors) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=board&action=show&project_id=<?= $values['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
</div>
|
||||
66
app/Templates/board_edit.php
Normal file
66
app/Templates/board_edit.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit the board for "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<h3><?= t('Change columns') ?></h3>
|
||||
<form method="post" action="?controller=board&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?php $i = 0; ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Position') ?></th>
|
||||
<th><?= t('Column title') ?></th>
|
||||
<th><?= t('Task limit') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<tr>
|
||||
<td><?= Helper\form_label(t('Column %d', ++$i), 'title['.$column['id'].']', array('title="column_id='.$column['id'].'"')) ?></td>
|
||||
<td><?= Helper\form_text('title['.$column['id'].']', $values, $errors, array('required')) ?></td>
|
||||
<td><?= Helper\form_number('task_limit['.$column['id'].']', $values, $errors, array('placeholder="'.t('limit').'"')) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php if ($column['position'] != 1): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Move Up') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($column['position'] != count($columns)): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Move Down') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=confirm&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Update') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<h3><?= t('Add a new column') ?></h3>
|
||||
<form method="post" action="?controller=board&action=add&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_label(t('Title'), 'title') ?>
|
||||
<?= Helper\form_text('title', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Add this column') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
42
app/Templates/board_index.php
Normal file
42
app/Templates/board_index.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<section id="main">
|
||||
|
||||
<div class="page-header board">
|
||||
<h2>
|
||||
<?= t('Project "%s"', $current_project_name) ?>
|
||||
</h2>
|
||||
<ul>
|
||||
<?php foreach ($projects as $project_id => $project_name): ?>
|
||||
<?php if ($project_id != $current_project_id): ?>
|
||||
<li>
|
||||
<a href="?controller=board&action=show&project_id=<?= $project_id ?>"><?= Helper\escape($project_name) ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="project-menu">
|
||||
<ul>
|
||||
<li>
|
||||
<?= t('Filter by user') ?>
|
||||
<?= Helper\form_select('user_id', $users, $filters) ?>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Filter by category') ?>
|
||||
<?= Helper\form_select('category_id', $categories, $filters) ?>
|
||||
</li>
|
||||
<li><a href="#" id="filter-due-date"><?= t('Filter by due date') ?></a></li>
|
||||
<li><a href="?controller=project&action=search&project_id=<?= $current_project_id ?>"><?= t('Search') ?></a></li>
|
||||
<li><a href="?controller=project&action=tasks&project_id=<?= $current_project_id ?>"><?= t('Completed tasks') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<?php if (empty($board)): ?>
|
||||
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
|
||||
<?php else: ?>
|
||||
<?= Helper\template('board_show', array('current_project_id' => $current_project_id, 'board' => $board, 'categories' => $categories)) ?>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
|
||||
<script type="text/javascript" src="assets/js/board.js"></script>
|
||||
79
app/Templates/board_public.php
Normal file
79
app/Templates/board_public.php
Normal file
@@ -0,0 +1,79 @@
|
||||
<section id="main" class="public-board">
|
||||
|
||||
<?php if (empty($columns)): ?>
|
||||
<p class="alert alert-error"><?= t('There is no column in your project!') ?></p>
|
||||
<?php else: ?>
|
||||
<table id="board">
|
||||
<tr>
|
||||
<?php $column_with = round(100 / count($columns), 2); ?>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<th width="<?= $column_with ?>%">
|
||||
<?= Helper\escape($column['title']) ?>
|
||||
<?php if ($column['task_limit']): ?>
|
||||
<span title="<?= t('Task limit') ?>" class="task-limit">(<?= Helper\escape(count($column['tasks']).'/'.$column['task_limit']) ?>)</span>
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<?php endforeach ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php foreach ($columns as $column): ?>
|
||||
<td class="column <?= $column['task_limit'] && count($column['tasks']) > $column['task_limit'] ? 'task-limit-warning' : '' ?>">
|
||||
<?php foreach ($column['tasks'] as $task): ?>
|
||||
<div class="task task-<?= $task['color_id'] ?>">
|
||||
|
||||
#<?= $task['id'] ?> -
|
||||
|
||||
<span class="task-user">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<?= t('Assigned to %s', $task['username']) ?>
|
||||
<?php else: ?>
|
||||
<span class="task-nobody"><?= t('Nobody assigned') ?></span>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
|
||||
<?php if ($task['score']): ?>
|
||||
<span class="task-score"><?= Helper\escape($task['score']) ?></span>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-title">
|
||||
<?= Helper\escape($task['title']) ?>
|
||||
</div>
|
||||
|
||||
<?php if ($task['category_id']): ?>
|
||||
<div class="task-category-container">
|
||||
<span class="task-category">
|
||||
<?= Helper\in_list($task['category_id'], $categories) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
|
||||
<div class="task-footer">
|
||||
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
<div class="task-date">
|
||||
<?= dt('%B %e, %G', $task['date_due']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-icons">
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<i class="fa fa-file-text-o" title="<?= t('Description') ?>"></i>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</td>
|
||||
<?php endforeach ?>
|
||||
</tr>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
17
app/Templates/board_remove.php
Normal file
17
app/Templates/board_remove.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove a column') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this column: "%s"?', $column['title']) ?>
|
||||
<?= t('This action will REMOVE ALL TASKS associated to this column!') ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=board&action=remove&column_id=<?= $column['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=board&action=edit&project_id=<?= $column['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
88
app/Templates/board_show.php
Normal file
88
app/Templates/board_show.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<table id="board" data-project-id="<?= $current_project_id ?>" data-time="<?= time() ?>" data-check-interval="<?= BOARD_CHECK_INTERVAL ?>">
|
||||
<tr>
|
||||
<?php $column_with = round(100 / count($board), 2); ?>
|
||||
<?php foreach ($board as $column): ?>
|
||||
<th width="<?= $column_with ?>%">
|
||||
<a href="?controller=task&action=create&project_id=<?= $column['project_id'] ?>&column_id=<?= $column['id'] ?>" title="<?= t('Add a new task') ?>">+</a>
|
||||
<?= Helper\escape($column['title']) ?>
|
||||
<?php if ($column['task_limit']): ?>
|
||||
<span title="<?= t('Task limit') ?>" class="task-limit">
|
||||
(
|
||||
<span id="task-number-column-<?= $column['id'] ?>"><?= count($column['tasks']) ?></span>
|
||||
/
|
||||
<?= Helper\escape($column['task_limit']) ?>
|
||||
)
|
||||
</span>
|
||||
<?php endif ?>
|
||||
</th>
|
||||
<?php endforeach ?>
|
||||
</tr>
|
||||
<tr>
|
||||
<?php foreach ($board as $column): ?>
|
||||
<td
|
||||
id="column-<?= $column['id'] ?>"
|
||||
class="column <?= $column['task_limit'] && count($column['tasks']) > $column['task_limit'] ? 'task-limit-warning' : '' ?>"
|
||||
data-column-id="<?= $column['id'] ?>"
|
||||
data-task-limit="<?= $column['task_limit'] ?>"
|
||||
>
|
||||
<?php foreach ($column['tasks'] as $task): ?>
|
||||
<div class="task draggable-item task-<?= $task['color_id'] ?>"
|
||||
data-task-id="<?= $task['id'] ?>"
|
||||
data-owner-id="<?= $task['owner_id'] ?>"
|
||||
data-category-id="<?= $task['category_id'] ?>"
|
||||
data-due-date="<?= $task['date_due'] ?>"
|
||||
title="<?= t('View this task') ?>">
|
||||
|
||||
<a href="?controller=task&action=edit&task_id=<?= $task['id'] ?>" title="<?= t('Edit this task') ?>">#<?= $task['id'] ?></a> -
|
||||
|
||||
<span class="task-user">
|
||||
<?php if (! empty($task['owner_id'])): ?>
|
||||
<a href="?controller=board&action=assign&task_id=<?= $task['id'] ?>" title="<?= t('Change assignee') ?>"><?= t('Assigned to %s', $task['username']) ?></a>
|
||||
<?php else: ?>
|
||||
<a href="?controller=board&action=assign&task_id=<?= $task['id'] ?>" title="<?= t('Change assignee') ?>" class="task-nobody"><?= t('Nobody assigned') ?></a>
|
||||
<?php endif ?>
|
||||
</span>
|
||||
|
||||
<?php if ($task['score']): ?>
|
||||
<span class="task-score"><?= Helper\escape($task['score']) ?></span>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-title">
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
|
||||
</div>
|
||||
|
||||
<?php if ($task['category_id']): ?>
|
||||
<div class="task-category-container">
|
||||
<span class="task-category">
|
||||
<?= Helper\in_list($task['category_id'], $categories) ?>
|
||||
</span>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['date_due']) || ! empty($task['nb_comments']) || ! empty($task['description'])): ?>
|
||||
<div class="task-footer">
|
||||
|
||||
<?php if (! empty($task['date_due'])): ?>
|
||||
<div class="task-date">
|
||||
<?= dt('%B %e, %G', $task['date_due']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="task-icons">
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<i class="fa fa-file-text-o" title="<?= t('Description') ?>"></i>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
<?php endforeach ?>
|
||||
</td>
|
||||
<?php endforeach ?>
|
||||
</tr>
|
||||
</table>
|
||||
24
app/Templates/category_edit.php
Normal file
24
app/Templates/category_edit.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<form method="post" action="?controller=category&action=update&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
48
app/Templates/category_index.php
Normal file
48
app/Templates/category_index.php
Normal file
@@ -0,0 +1,48 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Categories for the project "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<?php if (! empty($categories)): ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Category Name') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($categories as $category_id => $category_name): ?>
|
||||
<tr>
|
||||
<td><?= Helper\escape($category_name) ?></td>
|
||||
<td>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="?controller=category&action=edit&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Edit') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=category&action=confirm&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('Add a new category') ?></h3>
|
||||
<form method="post" action="?controller=category&action=save&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Category Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
16
app/Templates/category_remove.php
Normal file
16
app/Templates/category_remove.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove a category') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this category: "%s"?', $category['name']) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=category&action=remove&project_id=<?= $project['id'] ?>&category_id=<?= $category['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=category&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
9
app/Templates/comment_forbidden.php
Normal file
9
app/Templates/comment_forbidden.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Forbidden') ?></h2>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-error">
|
||||
<?= t('Only administrators or the creator of the comment can access to this page.') ?>
|
||||
</p>
|
||||
</section>
|
||||
18
app/Templates/comment_remove.php
Normal file
18
app/Templates/comment_remove.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove a comment') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this comment?') ?>
|
||||
</p>
|
||||
|
||||
<?= Helper\template('comment_show', array('comment' => $comment)) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=comment&action=remove&project_id=<?= $project_id ?>&comment_id=<?= $comment['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $comment['task_id'] ?>#comment-<?= $comment['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
36
app/Templates/comment_show.php
Normal file
36
app/Templates/comment_show.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<div class="<?= isset($display_edit_form) && $display_edit_form === true ? 'comment-edit' : 'comment' ?>" id="comment-<?= $comment['id'] ?>">
|
||||
<p class="comment-title">
|
||||
<span class="comment-username"><?= Helper\escape($comment['username']) ?></span> @ <span class="comment-date"><?= dt('%B %e, %G at %k:%M %p', $comment['date']) ?></span>
|
||||
</p>
|
||||
<?php if (isset($task)): ?>
|
||||
<ul class="comment-actions">
|
||||
<li><a href="#comment-<?= $comment['id'] ?>"><?= t('link') ?></a></li>
|
||||
<?php if (Helper\is_admin() || Helper\is_current_user($comment['user_id'])): ?>
|
||||
<li>
|
||||
<a href="?controller=comment&action=confirm&project_id=<?= $task['project_id'] ?>&comment_id=<?= $comment['id'] ?>"><?= t('remove') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=comment&action=edit&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'] ?>#comment-<?= $comment['id'] ?>"><?= t('edit') ?></a>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (isset($display_edit_form) && $display_edit_form === true): ?>
|
||||
<form method="post" action="?controller=comment&action=update&task_id=<?= $task['id'] ?>&comment_id=<?= $comment['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_textarea('comment', $values, $errors, array('required', 'placeholder="'.t('Leave a comment').'"')) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Update this comment') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?>
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<div class="markdown">
|
||||
<?= Helper\markdown($comment['comment']) ?>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
120
app/Templates/config_index.php
Normal file
120
app/Templates/config_index.php
Normal file
@@ -0,0 +1,120 @@
|
||||
<section id="main">
|
||||
|
||||
<?php if ($user['is_admin']): ?>
|
||||
<div class="page-header">
|
||||
<h2><?= t('Application settings') ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=config&action=save" autocomplete="off">
|
||||
|
||||
<?= Helper\form_label(t('Language'), 'language') ?>
|
||||
<?= Helper\form_select('language', $languages, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Timezone'), 'timezone') ?>
|
||||
<?= Helper\form_select('timezone', $timezones, $values, $errors) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="page-header">
|
||||
<h2><?= t('User settings') ?></h2>
|
||||
</div>
|
||||
<section class="settings">
|
||||
<ul>
|
||||
<li>
|
||||
<strong><?= t('My default project:') ?> </strong>
|
||||
<?= (isset($user['default_project_id']) && isset($projects[$user['default_project_id']])) ? Helper\escape($projects[$user['default_project_id']]) : t('None') ?>,
|
||||
<a href="?controller=user&action=edit&user_id=<?= $user['id'] ?>"><?= t('edit') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<?php if ($user['is_admin']): ?>
|
||||
<div class="page-header">
|
||||
<h2><?= t('More information') ?></h2>
|
||||
</div>
|
||||
<section class="settings">
|
||||
<ul>
|
||||
<li><a href="?controller=config&action=tokens"><?= t('Reset all tokens') ?></a></li>
|
||||
<li>
|
||||
<?= t('Webhooks token:') ?>
|
||||
<strong><?= Helper\escape($values['webhooks_token']) ?></strong>
|
||||
</li>
|
||||
<?php if (DB_DRIVER === 'sqlite'): ?>
|
||||
<li>
|
||||
<?= t('Database size:') ?>
|
||||
<strong><?= Helper\format_bytes($db_size) ?></strong>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=downloadDb"><?= t('Download the database') ?></a>
|
||||
<?= t('(Gzip compressed Sqlite file)') ?>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=config&action=optimizeDb"><?= t('Optimize the database') ?></a>
|
||||
<?= t('(VACUUM command)') ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?= t('Official website:') ?>
|
||||
<a href="http://kanboard.net/" target="_blank" rel="noreferer">http://kanboard.net/</a>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Application version:') ?>
|
||||
<?= APP_VERSION ?>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="page-header" id="last-logins">
|
||||
<h2><?= t('Last logins') ?></h2>
|
||||
</div>
|
||||
<?php if (! empty($last_logins)): ?>
|
||||
<table class="table-small table-hover">
|
||||
<tr>
|
||||
<th><?= t('Login date') ?></th>
|
||||
<th><?= t('Authentication method') ?></th>
|
||||
<th><?= t('IP address') ?></th>
|
||||
<th><?= t('User agent') ?></th>
|
||||
</tr>
|
||||
<?php foreach($last_logins as $login): ?>
|
||||
<tr>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $login['date_creation']) ?></td>
|
||||
<td><?= Helper\escape($login['auth_type']) ?></td>
|
||||
<td><?= Helper\escape($login['ip']) ?></td>
|
||||
<td><?= Helper\escape($login['user_agent']) ?></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="page-header" id="remember-me">
|
||||
<h2><?= t('Persistent connections') ?></h2>
|
||||
</div>
|
||||
<?php if (empty($remember_me_sessions)): ?>
|
||||
<p class="alert alert-info"><?= t('No session') ?></p>
|
||||
<?php else: ?>
|
||||
<table class="table-small table-hover">
|
||||
<tr>
|
||||
<th><?= t('Creation date') ?></th>
|
||||
<th><?= t('Expiration date') ?></th>
|
||||
<th><?= t('IP address') ?></th>
|
||||
<th><?= t('User agent') ?></th>
|
||||
<th><?= t('Action') ?></th>
|
||||
</tr>
|
||||
<?php foreach($remember_me_sessions as $session): ?>
|
||||
<tr>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $session['date_creation']) ?></td>
|
||||
<td><?= dt('%B %e, %G at %k:%M %p', $session['expiration']) ?></td>
|
||||
<td><?= Helper\escape($session['ip']) ?></td>
|
||||
<td><?= Helper\escape($session['user_agent']) ?></td>
|
||||
<td><a href="?controller=config&action=removeRememberMeToken&id=<?= $session['id'] ?>"><?= t('Remove') ?></a></td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
61
app/Templates/layout.php
Normal file
61
app/Templates/layout.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<meta name="viewport" content="width=device-width">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
<script src="assets/js/jquery-1.11.1.min.js"></script>
|
||||
<script src="assets/js/jquery-ui-1.10.4.custom.min.js"></script>
|
||||
<script src="assets/js/jquery.ui.touch-punch.min.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="assets/css/app.css" media="screen">
|
||||
<link rel="stylesheet" href="assets/css/font-awesome.min.css" media="screen">
|
||||
|
||||
<link rel="icon" type="image/png" href="assets/img/favicon.png">
|
||||
<link rel="apple-touch-icon" href="assets/img/touch-icon-iphone.png">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="assets/img/touch-icon-ipad.png">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="assets/img/touch-icon-iphone-retina.png">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="assets/img/touch-icon-ipad-retina.png">
|
||||
|
||||
<title><?= isset($title) ? Helper\escape($title).' - Kanboard' : 'Kanboard' ?></title>
|
||||
<?php if (isset($auto_refresh)): ?>
|
||||
<meta http-equiv="refresh" content="<?= BOARD_PUBLIC_CHECK_INTERVAL ?>" >
|
||||
<?php endif ?>
|
||||
</head>
|
||||
<body>
|
||||
<?php if (isset($no_layout)): ?>
|
||||
<?= $content_for_layout ?>
|
||||
<?php else: ?>
|
||||
<header>
|
||||
<nav>
|
||||
<a class="logo" href="?">kan<span>board</span></a>
|
||||
<ul>
|
||||
<li <?= isset($menu) && $menu === 'boards' ? 'class="active"' : '' ?>>
|
||||
<a href="?controller=board"><?= t('Boards') ?></a>
|
||||
</li>
|
||||
<li <?= isset($menu) && $menu === 'projects' ? 'class="active"' : '' ?>>
|
||||
<a href="?controller=project"><?= t('Projects') ?></a>
|
||||
</li>
|
||||
<li <?= isset($menu) && $menu === 'users' ? 'class="active"' : '' ?>>
|
||||
<a href="?controller=user"><?= t('Users') ?></a>
|
||||
</li>
|
||||
<li <?= isset($menu) && $menu === 'config' ? 'class="active"' : '' ?>>
|
||||
<a href="?controller=config"><?= t('Settings') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=user&action=logout"><?= t('Logout') ?></a>
|
||||
(<?= Helper\escape(Helper\get_username()) ?>)
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</header>
|
||||
<section class="page">
|
||||
<?= Helper\flash('<div class="alert alert-success alert-fade-out">%s</div>') ?>
|
||||
<?= Helper\flash_error('<div class="alert alert-error">%s</div>') ?>
|
||||
<?= $content_for_layout ?>
|
||||
</section>
|
||||
<?php endif ?>
|
||||
</body>
|
||||
</html>
|
||||
24
app/Templates/project_edit.php
Normal file
24
app/Templates/project_edit.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit project') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=project&action=update&project_id=<?= $values['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('required')) ?>
|
||||
|
||||
<?= Helper\form_checkbox('is_active', t('Activated'), 1, isset($values['is_active']) && $values['is_active'] == 1 ? true : false) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
9
app/Templates/project_forbidden.php
Normal file
9
app/Templates/project_forbidden.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Forbidden') ?></h2>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-error">
|
||||
<?= t('You are not allowed to access to this project.') ?>
|
||||
</p>
|
||||
</section>
|
||||
98
app/Templates/project_index.php
Normal file
98
app/Templates/project_index.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Projects') ?><span id="page-counter"> (<?= $nb_projects ?>)</span></h2>
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<ul>
|
||||
<li><a href="?controller=project&action=create"><?= t('New project') ?></a></li>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<section>
|
||||
<?php if (empty($projects)): ?>
|
||||
<p class="alert"><?= t('No project') ?></p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Project') ?></th>
|
||||
<th><?= t('Status') ?></th>
|
||||
<th><?= t('Tasks') ?></th>
|
||||
<th><?= t('Board') ?></th>
|
||||
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<th><?= t('Actions') ?></th>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
<?php foreach ($projects as $project): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="?controller=board&action=show&project_id=<?= $project['id'] ?>" title="project_id=<?= $project['id'] ?>"><?= Helper\escape($project['name']) ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<?= $project['is_active'] ? t('Active') : t('Inactive') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($project['nb_tasks'] > 0): ?>
|
||||
|
||||
<?php if ($project['nb_active_tasks'] > 0): ?>
|
||||
<a href="?controller=board&action=show&project_id=<?= $project['id'] ?>"><?= t('%d tasks on the board', $project['nb_active_tasks']) ?></a>,
|
||||
<?php endif ?>
|
||||
|
||||
<?php if ($project['nb_inactive_tasks'] > 0): ?>
|
||||
<a href="?controller=project&action=tasks&project_id=<?= $project['id'] ?>"><?= t('%d closed tasks', $project['nb_inactive_tasks']) ?></a>,
|
||||
<?php endif ?>
|
||||
|
||||
<?= t('%d tasks in total', $project['nb_tasks']) ?>
|
||||
|
||||
<?php else: ?>
|
||||
<?= t('no task for this project') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td>
|
||||
<ul>
|
||||
<?php foreach ($project['columns'] as $column): ?>
|
||||
<li>
|
||||
<span title="column_id=<?= $column['id'] ?>"><?= Helper\escape($column['title']) ?></span> (<?= $column['nb_active_tasks'] ?>)
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
</td>
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<td>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="?controller=category&action=index&project_id=<?= $project['id'] ?>"><?= t('Categories') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=project&action=edit&project_id=<?= $project['id'] ?>"><?= t('Edit project') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=project&action=users&project_id=<?= $project['id'] ?>"><?= t('Edit users access') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=board&action=edit&project_id=<?= $project['id'] ?>"><?= t('Edit board') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('Automatic actions') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<?php if ($project['is_active']): ?>
|
||||
<a href="?controller=project&action=disable&project_id=<?= $project['id'] ?>"><?= t('Disable') ?></a>
|
||||
<?php else: ?>
|
||||
<a href="?controller=project&action=enable&project_id=<?= $project['id'] ?>"><?= t('Enable') ?></a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=project&action=confirm&project_id=<?= $project['id'] ?>"><?= t('Remove') ?></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="?controller=board&action=readonly&token=<?= $project['token'] ?>" target="_blank"><?= t('Public link') ?></a>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
<?php endif ?>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
</section>
|
||||
20
app/Templates/project_new.php
Normal file
20
app/Templates/project_new.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('New project') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=project&action=save" autocomplete="off">
|
||||
|
||||
<?= Helper\form_label(t('Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors, array('autofocus', 'required')) ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
16
app/Templates/project_remove.php
Normal file
16
app/Templates/project_remove.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove project') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this project: "%s"?', $project['name']) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=project&action=remove&project_id=<?= $project['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
93
app/Templates/project_search.php
Normal file
93
app/Templates/project_search.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2>
|
||||
<?= t('Search in the project "%s"', $project['name']) ?>
|
||||
<?php if (! empty($nb_tasks)): ?>
|
||||
<span id="page-counter"> (<?= $nb_tasks ?>)</span>
|
||||
<?php endif ?>
|
||||
</h2>
|
||||
<ul>
|
||||
<li><a href="?controller=board&action=show&project_id=<?= $project['id'] ?>"><?= t('Back to the board') ?></a></li>
|
||||
<li><a href="?controller=project&action=tasks&project_id=<?= $project['id'] ?>"><?= t('Completed tasks') ?></a></li>
|
||||
<li><a href="?controller=project&action=index"><?= t('List of projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<form method="get" action="?" autocomplete="off">
|
||||
<?= Helper\form_hidden('controller', $values) ?>
|
||||
<?= Helper\form_hidden('action', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
<?= Helper\form_text('search', $values, array(), array('autofocus', 'required', 'placeholder="'.t('Search').'"')) ?>
|
||||
<input type="submit" value="<?= t('Search') ?>" class="btn btn-blue"/>
|
||||
</form>
|
||||
|
||||
<?php if (empty($tasks) && ! empty($values['search'])): ?>
|
||||
<p class="alert"><?= t('Nothing found.') ?></p>
|
||||
<?php elseif (! empty($tasks)): ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Id') ?></th>
|
||||
<th><?= t('Column') ?></th>
|
||||
<th><?= t('Category') ?></th>
|
||||
<th><?= t('Title') ?></th>
|
||||
<th><?= t('Assignee') ?></th>
|
||||
<th><?= t('Due date') ?></th>
|
||||
<th><?= t('Date created') ?></th>
|
||||
<th><?= t('Date completed') ?></th>
|
||||
<th><?= t('Status') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
<tr>
|
||||
<td class="task task-<?= $task['color_id'] ?>">
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['id']) ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\in_list($task['column_id'], $columns) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\in_list($task['category_id'], $categories, '') ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
|
||||
<div class="task-table-icons">
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<i class="fa fa-file-text-o" title="<?= t('Description') ?>"></i>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($task['username']): ?>
|
||||
<?= Helper\escape($task['username']) ?>
|
||||
<?php else: ?>
|
||||
<?= t('Unassigned') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= dt('%B %e, %G', $task['date_due']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= dt('%B %e, %G at %k:%M %p', $task['date_creation']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($task['date_completed']): ?>
|
||||
<?= dt('%B %e, %G at %k:%M %p', $task['date_completed']) ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($task['is_active'] == \Model\Task::STATUS_OPEN): ?>
|
||||
<?= t('Open') ?>
|
||||
<?php else: ?>
|
||||
<?= t('Closed') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
71
app/Templates/project_tasks.php
Normal file
71
app/Templates/project_tasks.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Completed tasks for "%s"', $project['name']) ?><span id="page-counter"> (<?= $nb_tasks ?>)</span></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=board&action=show&project_id=<?= $project['id'] ?>"><?= t('Back to the board') ?></a></li>
|
||||
<li><a href="?controller=project&action=search&project_id=<?= $project['id'] ?>"><?= t('Search') ?></a></li>
|
||||
<li><a href="?controller=project&action=index"><?= t('List of projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<?php if (empty($tasks)): ?>
|
||||
<p class="alert"><?= t('No task') ?></p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Id') ?></th>
|
||||
<th><?= t('Column') ?></th>
|
||||
<th><?= t('Category') ?></th>
|
||||
<th><?= t('Title') ?></th>
|
||||
<th><?= t('Assignee') ?></th>
|
||||
<th><?= t('Due date') ?></th>
|
||||
<th><?= t('Date created') ?></th>
|
||||
<th><?= t('Date completed') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($tasks as $task): ?>
|
||||
<tr>
|
||||
<td class="task task-<?= $task['color_id'] ?>">
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['id']) ?></a>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\in_list($task['column_id'], $columns) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\in_list($task['category_id'], $categories, '') ?>
|
||||
</td>
|
||||
<td>
|
||||
<a href="?controller=task&action=show&task_id=<?= $task['id'] ?>" title="<?= t('View this task') ?>"><?= Helper\escape($task['title']) ?></a>
|
||||
<div class="task-table-icons">
|
||||
<?php if (! empty($task['nb_comments'])): ?>
|
||||
<?= $task['nb_comments'] ?> <i class="fa fa-comment-o" title="<?= p($task['nb_comments'], t('%d comment', $task['nb_comments']), t('%d comments', $task['nb_comments'])) ?>"></i>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! empty($task['description'])): ?>
|
||||
<i class="fa fa-file-text-o" title="<?= t('Description') ?>"></i>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($task['username']): ?>
|
||||
<?= Helper\escape($task['username']) ?>
|
||||
<?php else: ?>
|
||||
<?= t('Unassigned') ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= dt('%B %e, %G', $task['date_due']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= dt('%B %e, %G at %k:%M %p', $task['date_creation']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if ($task['date_completed']): ?>
|
||||
<?= dt('%B %e, %G at %k:%M %p', $task['date_completed']) ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
</section>
|
||||
44
app/Templates/project_users.php
Normal file
44
app/Templates/project_users.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Project access list for "%s"', $project['name']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=project"><?= t('All projects') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
|
||||
<?php if (! empty($users['not_allowed'])): ?>
|
||||
<form method="post" action="?controller=project&action=allow&project_id=<?= $project['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('project_id', array('project_id' => $project['id'])) ?>
|
||||
|
||||
<?= Helper\form_label(t('User'), 'user_id') ?>
|
||||
<?= Helper\form_select('user_id', $users['not_allowed']) ?><br/>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Allow this user') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
|
||||
<h3><?= t('List of authorized users') ?></h3>
|
||||
<?php if (empty($users['allowed'])): ?>
|
||||
<div class="alert alert-info"><?= t('Everybody have access to this project.') ?></div>
|
||||
<?php else: ?>
|
||||
<div class="listing">
|
||||
<p><?= t('Only those users have access to this project:') ?></p>
|
||||
<ul>
|
||||
<?php foreach ($users['allowed'] as $user_id => $username): ?>
|
||||
<li>
|
||||
<strong><?= Helper\escape($username) ?></strong>
|
||||
(<a href="?controller=project&action=revoke&project_id=<?= $project['id'] ?>&user_id=<?= $user_id ?>"><?= t('revoke') ?></a>)
|
||||
</li>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<p><?= t('Don\'t forget that administrators have access to everything.') ?></p>
|
||||
</div>
|
||||
<?php endif ?>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
10
app/Templates/task_close.php
Normal file
10
app/Templates/task_close.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to close this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=close&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
51
app/Templates/task_edit.php
Normal file
51
app/Templates/task_edit.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit a task') ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=task&action=update" autocomplete="off">
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_label(t('Title'), 'title') ?>
|
||||
<?= Helper\form_text('title', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Description'), 'description') ?>
|
||||
<?= Helper\form_textarea('description', $values, $errors) ?><br/>
|
||||
<div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Assignee'), 'owner_id') ?>
|
||||
<?= Helper\form_select('owner_id', $users_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Category'), 'category_id') ?>
|
||||
<?= Helper\form_select('category_id', $categories_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Column'), 'column_id') ?>
|
||||
<?= Helper\form_select('column_id', $columns_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Color'), 'color_id') ?>
|
||||
<?= Helper\form_select('color_id', $colors_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Story Points'), 'score') ?>
|
||||
<?= Helper\form_number('score', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Due Date'), 'date_due') ?>
|
||||
<?= Helper\form_text('date_due', $values, $errors, array('placeholder="'.t('month/day/year').'"'), 'form-date') ?><br/>
|
||||
<div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=board&action=show&project_id=<?= $values['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
16
app/Templates/task_layout.php
Normal file
16
app/Templates/task_layout.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= Helper\escape($task['project_name']) ?> > <?= t('Task #%d', $task['id']) ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=board&action=show&project_id=<?= $task['project_id'] ?>"><?= t('Back to the board') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section class="task-show">
|
||||
|
||||
<?= Helper\template('task_sidebar', array('task' => $task)) ?>
|
||||
|
||||
<div class="task-show-main">
|
||||
<?= $task_content_for_layout ?>
|
||||
</div>
|
||||
</section>
|
||||
</section>
|
||||
51
app/Templates/task_new.php
Normal file
51
app/Templates/task_new.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('New task') ?></h2>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=task&action=save" autocomplete="off">
|
||||
|
||||
<div class="form-column">
|
||||
<?= Helper\form_label(t('Title'), 'title') ?>
|
||||
<?= Helper\form_text('title', $values, $errors, array('autofocus', 'required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Description'), 'description') ?>
|
||||
<?= Helper\form_textarea('description', $values, $errors) ?><br/>
|
||||
<div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div>
|
||||
|
||||
<?php if (! isset($duplicate)): ?>
|
||||
<?= Helper\form_checkbox('another_task', t('Create another task'), 1, isset($values['another_task']) && $values['another_task'] == 1) ?>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_hidden('project_id', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Assignee'), 'owner_id') ?>
|
||||
<?= Helper\form_select('owner_id', $users_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Category'), 'category_id') ?>
|
||||
<?= Helper\form_select('category_id', $categories_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Column'), 'column_id') ?>
|
||||
<?= Helper\form_select('column_id', $columns_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Color'), 'color_id') ?>
|
||||
<?= Helper\form_select('color_id', $colors_list, $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Story Points'), 'score') ?>
|
||||
<?= Helper\form_number('score', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Due Date'), 'date_due') ?>
|
||||
<?= Helper\form_text('date_due', $values, $errors, array('placeholder="'.t('month/day/year').'"'), 'form-date') ?><br/>
|
||||
<div class="form-help"><?= t('Others formats accepted: %s and %s', date('Y-m-d'), date('Y_m_d')) ?></div>
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=board&action=show&project_id=<?= $values['project_id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
16
app/Templates/task_open.php
Normal file
16
app/Templates/task_open.php
Normal file
@@ -0,0 +1,16 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Open a task') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to open this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=open&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
10
app/Templates/task_remove.php
Normal file
10
app/Templates/task_remove.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info">
|
||||
<?= t('Do you really want to remove this task: "%s"?', Helper\escape($task['title'])) ?>
|
||||
</p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=task&action=remove&task_id=<?= $task['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=task&action=show&task_id=<?= $task['id'] ?>"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
94
app/Templates/task_show.php
Normal file
94
app/Templates/task_show.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<article class="task task-<?= $task['color_id'] ?> task-show-details">
|
||||
<h2><?= Helper\escape($task['title']) ?></h2>
|
||||
<?php if ($task['score']): ?>
|
||||
<span class="task-score"><?= Helper\escape($task['score']) ?></span>
|
||||
<?php endif ?>
|
||||
<ul>
|
||||
<li>
|
||||
<?= dt('Created on %B %e, %G at %k:%M %p', $task['date_creation']) ?>
|
||||
</li>
|
||||
<?php if ($task['date_completed']): ?>
|
||||
<li>
|
||||
<?= dt('Completed on %B %e, %G at %k:%M %p', $task['date_completed']) ?>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<?php if ($task['date_due']): ?>
|
||||
<li>
|
||||
<strong><?= dt('Must be done before %B %e, %G', $task['date_due']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<strong>
|
||||
<?php if ($task['username']): ?>
|
||||
<?= t('Assigned to %s', $task['username']) ?>
|
||||
<?php else: ?>
|
||||
<?= t('There is nobody assigned') ?>
|
||||
<?php endif ?>
|
||||
</strong>
|
||||
</li>
|
||||
<li>
|
||||
<?= t('Column on the board:') ?>
|
||||
<strong><?= Helper\escape($task['column_title']) ?></strong>
|
||||
(<?= Helper\escape($task['project_name']) ?>)
|
||||
</li>
|
||||
<?php if ($task['category_name']): ?>
|
||||
<li>
|
||||
<?= t('Category:') ?> <strong><?= Helper\escape($task['category_name']) ?></strong>
|
||||
</li>
|
||||
<?php endif ?>
|
||||
<li>
|
||||
<?php if ($task['is_active'] == 1): ?>
|
||||
<?= t('Status is open') ?>
|
||||
<?php else: ?>
|
||||
<?= t('Status is closed') ?>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
</ul>
|
||||
</article>
|
||||
|
||||
<h2><?= t('Description') ?></h2>
|
||||
<?php if ($task['description']): ?>
|
||||
<article class="markdown task-show-description">
|
||||
<?= Helper\parse($task['description']) ?: t('There is no description.') ?>
|
||||
</article>
|
||||
<?php else: ?>
|
||||
<form method="post" action="?controller=task&action=description&task_id=<?= $task['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('id', $description_form['values']) ?>
|
||||
<?= Helper\form_textarea('description', $description_form['values'], $description_form['errors'], array('required', 'placeholder="'.t('Leave a description').'"')) ?><br/>
|
||||
<div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
|
||||
<h2><?= t('Comments') ?></h2>
|
||||
<?php if ($comments): ?>
|
||||
<ul id="comments">
|
||||
<?php foreach ($comments as $comment): ?>
|
||||
<?= Helper\template('comment_show', array(
|
||||
'comment' => $comment,
|
||||
'task' => $task,
|
||||
'display_edit_form' => $comment['id'] == $comment_edit_form['values']['id'],
|
||||
'values' => $comment_edit_form['values'] + array('comment' => $comment['comment']),
|
||||
'errors' => $comment_edit_form['errors']
|
||||
)) ?>
|
||||
<?php endforeach ?>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (! isset($hide_comment_form) || $hide_comment_form === false): ?>
|
||||
<form method="post" action="?controller=comment&action=save&task_id=<?= $task['id'] ?>" autocomplete="off">
|
||||
|
||||
<?= Helper\form_hidden('task_id', $comment_form['values']) ?>
|
||||
<?= Helper\form_hidden('user_id', $comment_form['values']) ?>
|
||||
<?= Helper\form_textarea('comment', $comment_form['values'], $comment_form['errors'], array('required', 'placeholder="'.t('Leave a comment').'"'), 'comment-textarea') ?><br/>
|
||||
<div class="form-help"><a href="http://kanboard.net/documentation/syntax-guide" target="_blank" rel="noreferrer"><?= t('Write your text in Markdown') ?></a></div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Post comment') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
<?php endif ?>
|
||||
17
app/Templates/task_sidebar.php
Normal file
17
app/Templates/task_sidebar.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<div class="task-show-sidebar">
|
||||
<h2><?= t('Actions') ?></h2>
|
||||
<div class="task-show-actions">
|
||||
<ul>
|
||||
<li><a href="?controller=task&action=duplicate&project_id=<?= $task['project_id'] ?>&task_id=<?= $task['id'] ?>"><?= t('Duplicate') ?></a></li>
|
||||
<li><a href="?controller=task&action=edit&task_id=<?= $task['id'] ?>"><?= t('Edit') ?></a></li>
|
||||
<li>
|
||||
<?php if ($task['is_active'] == 1): ?>
|
||||
<a href="?controller=task&action=confirmClose&task_id=<?= $task['id'] ?>"><?= t('Close this task') ?></a>
|
||||
<?php else: ?>
|
||||
<a href="?controller=task&action=confirmOpen&task_id=<?= $task['id'] ?>"><?= t('Open this task') ?></a>
|
||||
<?php endif ?>
|
||||
</li>
|
||||
<li><a href="?controller=task&action=confirmRemove&task_id=<?= $task['id'] ?>"><?= t('Remove') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
64
app/Templates/user_edit.php
Normal file
64
app/Templates/user_edit.php
Normal file
@@ -0,0 +1,64 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Edit user') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=user"><?= t('All users') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=user&action=update" autocomplete="off">
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_hidden('id', $values) ?>
|
||||
<?= Helper\form_hidden('is_ldap_user', $values) ?>
|
||||
|
||||
<?= Helper\form_label(t('Username'), 'username') ?>
|
||||
<?= Helper\form_text('username', $values, $errors, array('required', $values['is_ldap_user'] == 1 ? 'readonly' : '')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Email'), 'email') ?>
|
||||
<?= Helper\form_email('email', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Default Project'), 'default_project_id') ?>
|
||||
<?= Helper\form_select('default_project_id', $projects, $values, $errors) ?><br/>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?php if ($values['is_ldap_user'] == 0): ?>
|
||||
|
||||
<?= Helper\form_label(t('Current password for the user "%s"', Helper\get_username()), 'current_password') ?>
|
||||
<?= Helper\form_password('current_password', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Password'), 'password') ?>
|
||||
<?= Helper\form_password('password', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?= Helper\form_password('confirmation', $values, $errors) ?><br/>
|
||||
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<?= Helper\form_checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1 ? true : false) ?><br/>
|
||||
<?php endif ?>
|
||||
|
||||
<?php if (GOOGLE_AUTH && Helper\is_current_user($values['id'])): ?>
|
||||
<?php if (empty($values['google_id'])): ?>
|
||||
<a href="?controller=user&action=google"><?= t('Link my Google Account') ?></a>
|
||||
<?php else: ?>
|
||||
<a href="?controller=user&action=unlinkGoogle"><?= t('Unlink my Google Account') ?></a>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> <?= t('or') ?> <a href="?controller=user"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
9
app/Templates/user_forbidden.php
Normal file
9
app/Templates/user_forbidden.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Forbidden') ?></h2>
|
||||
</div>
|
||||
|
||||
<p class="alert alert-error">
|
||||
<?= t('Only administrators can access to this page.') ?>
|
||||
</p>
|
||||
</section>
|
||||
56
app/Templates/user_index.php
Normal file
56
app/Templates/user_index.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Users') ?><span id="page-counter"> (<?= $nb_users ?>)</span></h2>
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<ul>
|
||||
<li><a href="?controller=user&action=create"><?= t('New user') ?></a></li>
|
||||
</ul>
|
||||
<?php endif ?>
|
||||
</div>
|
||||
<section>
|
||||
<?php if (empty($users)): ?>
|
||||
<p class="alert"><?= t('No user') ?></p>
|
||||
<?php else: ?>
|
||||
<table>
|
||||
<tr>
|
||||
<th><?= t('Username') ?></th>
|
||||
<th><?= t('Name') ?></th>
|
||||
<th><?= t('Email') ?></th>
|
||||
<th><?= t('Administrator') ?></th>
|
||||
<th><?= t('Default Project') ?></th>
|
||||
<th><?= t('Actions') ?></th>
|
||||
</tr>
|
||||
<?php foreach ($users as $user): ?>
|
||||
<tr>
|
||||
<td>
|
||||
<span title="user_id=<?= $user['id'] ?>"><?= Helper\escape($user['username']) ?></span>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\escape($user['name']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= Helper\escape($user['email']) ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= $user['is_admin'] ? t('Yes') : t('No') ?>
|
||||
</td>
|
||||
<td>
|
||||
<?= (isset($user['default_project_id']) && isset($projects[$user['default_project_id']])) ? Helper\escape($projects[$user['default_project_id']]) : t('None'); ?>
|
||||
</td>
|
||||
<td>
|
||||
<?php if (Helper\is_admin() || Helper\is_current_user($user['id'])): ?>
|
||||
<a href="?controller=user&action=edit&user_id=<?= $user['id'] ?>"><?= t('edit') ?></a>
|
||||
<?php endif ?>
|
||||
<?php if (Helper\is_admin()): ?>
|
||||
<?php if (count($users) > 1): ?>
|
||||
<?= t('or') ?>
|
||||
<a href="?controller=user&action=confirm&user_id=<?= $user['id'] ?>"><?= t('remove') ?></a>
|
||||
<?php endif ?>
|
||||
<?php endif ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach ?>
|
||||
</table>
|
||||
<?php endif ?>
|
||||
</section>
|
||||
</section>
|
||||
28
app/Templates/user_login.php
Normal file
28
app/Templates/user_login.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<div class="page-header">
|
||||
<h1><?= t('Sign in') ?></h1>
|
||||
</div>
|
||||
|
||||
<?php if (isset($errors['login'])): ?>
|
||||
<p class="alert alert-error"><?= Helper\escape($errors['login']) ?></p>
|
||||
<?php endif ?>
|
||||
|
||||
<form method="post" action="?controller=user&action=check" class="form-login">
|
||||
|
||||
<?= Helper\form_label(t('Username'), 'username') ?>
|
||||
<?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Password'), 'password') ?>
|
||||
<?= Helper\form_password('password', $values, $errors, array('required')) ?>
|
||||
|
||||
<?= Helper\form_checkbox('remember_me', t('Remember Me'), 1) ?><br/>
|
||||
|
||||
<?php if (GOOGLE_AUTH): ?>
|
||||
<p>
|
||||
<a href="?controller=user&action=google"><?= t('Login with my Google Account') ?></a>
|
||||
</p>
|
||||
<?php endif ?>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Sign in') ?>" class="btn btn-blue"/>
|
||||
</div>
|
||||
</form>
|
||||
45
app/Templates/user_new.php
Normal file
45
app/Templates/user_new.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('New user') ?></h2>
|
||||
<ul>
|
||||
<li><a href="?controller=user"><?= t('All users') ?></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<section>
|
||||
<form method="post" action="?controller=user&action=save" autocomplete="off">
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_label(t('Username'), 'username') ?>
|
||||
<?= Helper\form_text('username', $values, $errors, array('autofocus', 'required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Name'), 'name') ?>
|
||||
<?= Helper\form_text('name', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Email'), 'email') ?>
|
||||
<?= Helper\form_email('email', $values, $errors) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Default Project'), 'default_project_id') ?>
|
||||
<?= Helper\form_select('default_project_id', $projects, $values, $errors) ?><br/>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-column">
|
||||
|
||||
<?= Helper\form_label(t('Password'), 'password') ?>
|
||||
<?= Helper\form_password('password', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?= Helper\form_label(t('Confirmation'), 'confirmation') ?>
|
||||
<?= Helper\form_password('confirmation', $values, $errors, array('required')) ?><br/>
|
||||
|
||||
<?= Helper\form_checkbox('is_admin', t('Administrator'), 1, isset($values['is_admin']) && $values['is_admin'] == 1 ? true : false) ?>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-actions">
|
||||
<input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/>
|
||||
<?= t('or') ?> <a href="?controller=user"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</section>
|
||||
14
app/Templates/user_remove.php
Normal file
14
app/Templates/user_remove.php
Normal file
@@ -0,0 +1,14 @@
|
||||
<section id="main">
|
||||
<div class="page-header">
|
||||
<h2><?= t('Remove user') ?></h2>
|
||||
</div>
|
||||
|
||||
<div class="confirm">
|
||||
<p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['username']) ?></p>
|
||||
|
||||
<div class="form-actions">
|
||||
<a href="?controller=user&action=remove&user_id=<?= $user['id'] ?>" class="btn btn-red"><?= t('Yes') ?></a>
|
||||
<?= t('or') ?> <a href="?controller=user"><?= t('cancel') ?></a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
40
app/check_setup.php
Normal file
40
app/check_setup.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
// PHP 5.3.3 minimum
|
||||
if (version_compare(PHP_VERSION, '5.3.3', '<')) {
|
||||
die('This software require PHP 5.3.3 minimum');
|
||||
}
|
||||
|
||||
// Checks for PHP < 5.4
|
||||
if (version_compare(PHP_VERSION, '5.4.0', '<')) {
|
||||
|
||||
// Short tags must be enabled for PHP < 5.4
|
||||
if (! ini_get('short_open_tag')) {
|
||||
die('This software require to have short tags enabled if you have PHP < 5.4 ("short_open_tag = On")');
|
||||
}
|
||||
|
||||
// Magic quotes are deprecated since PHP 5.4
|
||||
if (get_magic_quotes_gpc()) {
|
||||
die('This software require to have "Magic quotes" disabled, it\'s deprecated since PHP 5.4 ("magic_quotes_gpc = Off")');
|
||||
}
|
||||
}
|
||||
|
||||
// Check extension: PDO
|
||||
if (! extension_loaded('pdo_sqlite') && ! extension_loaded('pdo_mysql')) {
|
||||
die('PHP extension required: pdo_sqlite or pdo_mysql');
|
||||
}
|
||||
|
||||
// Check extension: mbstring
|
||||
if (! extension_loaded('mbstring')) {
|
||||
die('PHP extension required: mbstring');
|
||||
}
|
||||
|
||||
// Check if /data is writeable
|
||||
if (! is_writable('data')) {
|
||||
die('The directory "data" must be writeable by your web server user');
|
||||
}
|
||||
|
||||
// Include password_compat for PHP < 5.5
|
||||
if (version_compare(PHP_VERSION, '5.5.0', '<')) {
|
||||
require __DIR__.'/../vendor/password.php';
|
||||
}
|
||||
98
app/common.php
Normal file
98
app/common.php
Normal file
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
require __DIR__.'/Core/Loader.php';
|
||||
require __DIR__.'/helpers.php';
|
||||
require __DIR__.'/translator.php';
|
||||
|
||||
use Core\Event;
|
||||
use Core\Loader;
|
||||
use Core\Registry;
|
||||
|
||||
// Include custom config file
|
||||
if (file_exists('config.php')) {
|
||||
require 'config.php';
|
||||
}
|
||||
|
||||
// Board refresh frequency in seconds for the public board view
|
||||
defined('BOARD_PUBLIC_CHECK_INTERVAL') or define('BOARD_PUBLIC_CHECK_INTERVAL', 60);
|
||||
|
||||
// Board refresh frequency in seconds (the value 0 disable this feature)
|
||||
defined('BOARD_CHECK_INTERVAL') or define('BOARD_CHECK_INTERVAL', 10);
|
||||
|
||||
// Custom session save path
|
||||
defined('SESSION_SAVE_PATH') or define('SESSION_SAVE_PATH', '');
|
||||
|
||||
// Application version
|
||||
defined('APP_VERSION') or define('APP_VERSION', 'master');
|
||||
|
||||
// Base directory
|
||||
define('BASE_URL_DIRECTORY', dirname($_SERVER['PHP_SELF']));
|
||||
|
||||
// Database driver: sqlite or mysql
|
||||
defined('DB_DRIVER') or define('DB_DRIVER', 'sqlite');
|
||||
|
||||
// Sqlite configuration
|
||||
defined('DB_FILENAME') or define('DB_FILENAME', 'data/db.sqlite');
|
||||
|
||||
// Mysql configuration
|
||||
defined('DB_USERNAME') or define('DB_USERNAME', 'root');
|
||||
defined('DB_PASSWORD') or define('DB_PASSWORD', '');
|
||||
defined('DB_HOSTNAME') or define('DB_HOSTNAME', 'localhost');
|
||||
defined('DB_NAME') or define('DB_NAME', 'kanboard');
|
||||
|
||||
// LDAP configuration
|
||||
defined('LDAP_AUTH') or define('LDAP_AUTH', false);
|
||||
defined('LDAP_SERVER') or define('LDAP_SERVER', '');
|
||||
defined('LDAP_PORT') or define('LDAP_PORT', 389);
|
||||
defined('LDAP_USER_DN') or define('LDAP_USER_DN', '%s');
|
||||
|
||||
// Google authentication
|
||||
defined('GOOGLE_AUTH') or define('GOOGLE_AUTH', false);
|
||||
defined('GOOGLE_CLIENT_ID') or define('GOOGLE_CLIENT_ID', '');
|
||||
defined('GOOGLE_CLIENT_SECRET') or define('GOOGLE_CLIENT_SECRET', '');
|
||||
|
||||
$loader = new Loader;
|
||||
$loader->execute();
|
||||
|
||||
$registry = new Registry;
|
||||
|
||||
$registry->db = function() use ($registry) {
|
||||
require __DIR__.'/../vendor/PicoDb/Database.php';
|
||||
|
||||
if (DB_DRIVER === 'sqlite') {
|
||||
|
||||
require __DIR__.'/Schema/Sqlite.php';
|
||||
|
||||
$db = new \PicoDb\Database(array(
|
||||
'driver' => 'sqlite',
|
||||
'filename' => DB_FILENAME
|
||||
));
|
||||
}
|
||||
elseif (DB_DRIVER === 'mysql') {
|
||||
|
||||
require __DIR__.'/Schema/Mysql.php';
|
||||
|
||||
$db = new \PicoDb\Database(array(
|
||||
'driver' => 'mysql',
|
||||
'hostname' => DB_HOSTNAME,
|
||||
'username' => DB_USERNAME,
|
||||
'password' => DB_PASSWORD,
|
||||
'database' => DB_NAME,
|
||||
'charset' => 'utf8',
|
||||
));
|
||||
}
|
||||
else {
|
||||
die('Database driver not supported');
|
||||
}
|
||||
|
||||
if ($db->schema()->check(Schema\VERSION)) {
|
||||
return $db;
|
||||
}
|
||||
else {
|
||||
die('Unable to migrate database schema!');
|
||||
}
|
||||
};
|
||||
|
||||
$registry->event = function() use ($registry) {
|
||||
return new Event;
|
||||
};
|
||||
262
app/helpers.php
Normal file
262
app/helpers.php
Normal file
@@ -0,0 +1,262 @@
|
||||
<?php
|
||||
|
||||
namespace Helper;
|
||||
|
||||
function template($name, array $args = array())
|
||||
{
|
||||
$tpl = new \Core\Template;
|
||||
return $tpl->load($name, $args);
|
||||
}
|
||||
|
||||
function is_current_user($user_id)
|
||||
{
|
||||
return $_SESSION['user']['id'] == $user_id;
|
||||
}
|
||||
|
||||
function is_admin()
|
||||
{
|
||||
return $_SESSION['user']['is_admin'] == 1;
|
||||
}
|
||||
|
||||
function get_username()
|
||||
{
|
||||
return $_SESSION['user']['username'];
|
||||
}
|
||||
|
||||
function parse($text)
|
||||
{
|
||||
$text = markdown($text);
|
||||
$text = preg_replace('!#(\d+)!i', '<a href="?controller=task&action=show&task_id=$1">$0</a>', $text);
|
||||
return $text;
|
||||
}
|
||||
|
||||
function markdown($text)
|
||||
{
|
||||
require_once __DIR__.'/../vendor/Michelf/MarkdownExtra.inc.php';
|
||||
|
||||
$parser = new \Michelf\MarkdownExtra;
|
||||
$parser->no_markup = true;
|
||||
$parser->no_entities = true;
|
||||
|
||||
return $parser->transform($text);
|
||||
}
|
||||
|
||||
function get_current_base_url()
|
||||
{
|
||||
$url = isset($_SERVER['HTTPS']) ? 'https://' : 'http://';
|
||||
$url .= $_SERVER['SERVER_NAME'];
|
||||
$url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 ? '' : ':'.$_SERVER['SERVER_PORT'];
|
||||
$url .= dirname($_SERVER['PHP_SELF']) !== '/' ? dirname($_SERVER['PHP_SELF']).'/' : '/';
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
function escape($value)
|
||||
{
|
||||
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8', false);
|
||||
}
|
||||
|
||||
function flash($html)
|
||||
{
|
||||
$data = '';
|
||||
|
||||
if (isset($_SESSION['flash_message'])) {
|
||||
$data = sprintf($html, escape($_SESSION['flash_message']));
|
||||
unset($_SESSION['flash_message']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function flash_error($html)
|
||||
{
|
||||
$data = '';
|
||||
|
||||
if (isset($_SESSION['flash_error_message'])) {
|
||||
$data = sprintf($html, escape($_SESSION['flash_error_message']));
|
||||
unset($_SESSION['flash_error_message']);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
function format_bytes($size, $precision = 2)
|
||||
{
|
||||
$base = log($size) / log(1024);
|
||||
$suffixes = array('', 'k', 'M', 'G', 'T');
|
||||
|
||||
return round(pow(1024, $base - floor($base)), $precision).$suffixes[floor($base)];
|
||||
}
|
||||
|
||||
function get_host_from_url($url)
|
||||
{
|
||||
return escape(parse_url($url, PHP_URL_HOST)) ?: $url;
|
||||
}
|
||||
|
||||
function summary($value, $min_length = 5, $max_length = 120, $end = '[...]')
|
||||
{
|
||||
$length = strlen($value);
|
||||
|
||||
if ($length > $max_length) {
|
||||
return substr($value, 0, strpos($value, ' ', $max_length)).' '.$end;
|
||||
}
|
||||
else if ($length < $min_length) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
function contains($haystack, $needle)
|
||||
{
|
||||
return strpos($haystack, $needle) !== false;
|
||||
}
|
||||
|
||||
function in_list($id, array $listing, $default_value = '?')
|
||||
{
|
||||
if (isset($listing[$id])) {
|
||||
return escape($listing[$id]);
|
||||
}
|
||||
|
||||
return $default_value;
|
||||
}
|
||||
|
||||
function error_class(array $errors, $name)
|
||||
{
|
||||
return ! isset($errors[$name]) ? '' : ' form-error';
|
||||
}
|
||||
|
||||
function error_list(array $errors, $name)
|
||||
{
|
||||
$html = '';
|
||||
|
||||
if (isset($errors[$name])) {
|
||||
|
||||
$html .= '<ul class="form-errors">';
|
||||
|
||||
foreach ($errors[$name] as $error) {
|
||||
$html .= '<li>'.escape($error).'</li>';
|
||||
}
|
||||
|
||||
$html .= '</ul>';
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function form_value($values, $name)
|
||||
{
|
||||
if (isset($values->$name)) {
|
||||
return 'value="'.escape($values->$name).'"';
|
||||
}
|
||||
|
||||
return isset($values[$name]) ? 'value="'.escape($values[$name]).'"' : '';
|
||||
}
|
||||
|
||||
function form_hidden($name, $values = array())
|
||||
{
|
||||
return '<input type="hidden" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).'/>';
|
||||
}
|
||||
|
||||
function form_default_select($name, array $options, $values = array(), array $errors = array(), $class = '')
|
||||
{
|
||||
$options = array('' => '?') + $options;
|
||||
return form_select($name, $options, $values, $errors, $class);
|
||||
}
|
||||
|
||||
function form_select($name, array $options, $values = array(), array $errors = array(), $class = '')
|
||||
{
|
||||
$html = '<select name="'.$name.'" id="form-'.$name.'" class="'.$class.'">';
|
||||
|
||||
foreach ($options as $id => $value) {
|
||||
|
||||
$html .= '<option value="'.escape($id).'"';
|
||||
|
||||
if (isset($values->$name) && $id == $values->$name) $html .= ' selected="selected"';
|
||||
if (isset($values[$name]) && $id == $values[$name]) $html .= ' selected="selected"';
|
||||
|
||||
$html .= '>'.escape($value).'</option>';
|
||||
}
|
||||
|
||||
$html .= '</select>';
|
||||
$html .= error_list($errors, $name);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function form_radios($name, array $options, array $values = array())
|
||||
{
|
||||
$html = '';
|
||||
|
||||
foreach ($options as $value => $label) {
|
||||
$html .= form_radio($name, $label, $value, isset($values[$name]) && $values[$name] == $value);
|
||||
}
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function form_radio($name, $label, $value, $selected = false, $class = '')
|
||||
{
|
||||
return '<label><input type="radio" name="'.$name.'" class="'.$class.'" value="'.escape($value).'" '.($selected ? 'selected="selected"' : '').'>'.escape($label).'</label>';
|
||||
}
|
||||
|
||||
function form_checkbox($name, $label, $value, $checked = false, $class = '')
|
||||
{
|
||||
return '<label><input type="checkbox" name="'.$name.'" class="'.$class.'" value="'.escape($value).'" '.($checked ? 'checked="checked"' : '').'> '.escape($label).'</label>';
|
||||
}
|
||||
|
||||
function form_label($label, $name, array $attributes = array())
|
||||
{
|
||||
return '<label for="form-'.$name.'" '.implode(' ', $attributes).'>'.escape($label).'</label>';
|
||||
}
|
||||
|
||||
function form_textarea($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
$class .= error_class($errors, $name);
|
||||
|
||||
$html = '<textarea name="'.$name.'" id="form-'.$name.'" class="'.$class.'" ';
|
||||
$html .= implode(' ', $attributes).'>';
|
||||
$html .= isset($values->$name) ? escape($values->$name) : isset($values[$name]) ? $values[$name] : '';
|
||||
$html .= '</textarea>';
|
||||
if (in_array('required', $attributes)) $html .= '<span class="form-required">*</span>';
|
||||
$html .= error_list($errors, $name);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function form_input($type, $name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
$class .= error_class($errors, $name);
|
||||
|
||||
$html = '<input type="'.$type.'" name="'.$name.'" id="form-'.$name.'" '.form_value($values, $name).' class="'.$class.'" ';
|
||||
$html .= implode(' ', $attributes).'/>';
|
||||
if (in_array('required', $attributes)) $html .= '<span class="form-required">*</span>';
|
||||
$html .= error_list($errors, $name);
|
||||
|
||||
return $html;
|
||||
}
|
||||
|
||||
function form_text($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
return form_input('text', $name, $values, $errors, $attributes, $class);
|
||||
}
|
||||
|
||||
function form_password($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
return form_input('password', $name, $values, $errors, $attributes, $class);
|
||||
}
|
||||
|
||||
function form_email($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
return form_input('email', $name, $values, $errors, $attributes, $class);
|
||||
}
|
||||
|
||||
function form_date($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
return form_input('date', $name, $values, $errors, $attributes, $class);
|
||||
}
|
||||
|
||||
function form_number($name, $values = array(), array $errors = array(), array $attributes = array(), $class = '')
|
||||
{
|
||||
return form_input('number', $name, $values, $errors, $attributes, $class);
|
||||
}
|
||||
36
app/translator.php
Normal file
36
app/translator.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
use Core\Translator;
|
||||
|
||||
// Get a translation
|
||||
function t()
|
||||
{
|
||||
$t = new Translator;
|
||||
return call_user_func_array(array($t, 'translate'), func_get_args());
|
||||
}
|
||||
|
||||
// Get a locale currency
|
||||
function c($value)
|
||||
{
|
||||
$t = new Translator;
|
||||
return $t->currency($value);
|
||||
}
|
||||
|
||||
// Get a formatted number
|
||||
function n($value)
|
||||
{
|
||||
$t = new Translator;
|
||||
return $t->number($value);
|
||||
}
|
||||
|
||||
// Get a locale date
|
||||
function dt($format, $timestamp)
|
||||
{
|
||||
$t = new Translator;
|
||||
return $t->datetime($format, $timestamp);
|
||||
}
|
||||
|
||||
// Plurals, return $t2 if $value > 1
|
||||
function p($value, $t1, $t2) {
|
||||
return $value > 1 ? $t2 : $t1;
|
||||
}
|
||||
Reference in New Issue
Block a user