Code refactoring (add autoloader and change files organization)
This commit is contained in:
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');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user