diff options
Diffstat (limited to 'app')
63 files changed, 1266 insertions, 605 deletions
diff --git a/app/Action/Base.php b/app/Action/Base.php index be9c3d48..a438ce04 100644 --- a/app/Action/Base.php +++ b/app/Action/Base.php @@ -2,8 +2,8 @@ namespace Action; +use Pimple\Container; use Core\Listener; -use Core\Registry; use Core\Tool; /** @@ -44,12 +44,12 @@ abstract class Base implements Listener protected $event_name = ''; /** - * Registry instance + * Container instance * * @access protected - * @var \Core\Registry + * @var Pimple\Container */ - protected $registry; + protected $container; /** * Execute the action @@ -101,13 +101,13 @@ abstract class Base implements Listener * Constructor * * @access public - * @param \Core\Registry $registry Regsitry instance - * @param integer $project_id Project id - * @param string $event_name Attached event name + * @param Pimple\Container $container Container + * @param integer $project_id Project id + * @param string $event_name Attached event name */ - public function __construct(Registry $registry, $project_id, $event_name) + public function __construct(Container $container, $project_id, $event_name) { - $this->registry = $registry; + $this->container = $container; $this->project_id = $project_id; $this->event_name = $event_name; } @@ -132,7 +132,7 @@ abstract class Base implements Listener */ public function __get($name) { - return Tool::loadModel($this->registry, $name); + return Tool::loadModel($this->container, $name); } /** diff --git a/app/Auth/Base.php b/app/Auth/Base.php index e174ff8f..e3a1c88c 100644 --- a/app/Auth/Base.php +++ b/app/Auth/Base.php @@ -3,7 +3,7 @@ namespace Auth; use Core\Tool; -use Core\Registry; +use Pimple\Container; /** * Base auth class @@ -26,34 +26,34 @@ abstract class Base protected $db; /** - * Registry instance + * Container instance * * @access protected - * @var \Core\Registry + * @var Pimple\Container */ - protected $registry; + protected $container; /** * Constructor * * @access public - * @param \Core\Registry $registry Registry instance + * @param Pimple\Container $container */ - public function __construct(Registry $registry) + public function __construct(Container $container) { - $this->registry = $registry; - $this->db = $this->registry->shared('db'); + $this->container = $container; + $this->db = $this->container['db']; } /** * Load automatically models * * @access public - * @param string $name Model name + * @param string $name Model name * @return mixed */ public function __get($name) { - return Tool::loadModel($this->registry, $name); + return Tool::loadModel($this->container, $name); } } diff --git a/app/Auth/RememberMe.php b/app/Auth/RememberMe.php index 380abbed..2585e96c 100644 --- a/app/Auth/RememberMe.php +++ b/app/Auth/RememberMe.php @@ -4,7 +4,6 @@ namespace Auth; use Core\Request; use Core\Security; -use Core\Tool; /** * RememberMe model @@ -311,7 +310,7 @@ class RememberMe extends Base $expiration, BASE_URL_DIRECTORY, null, - Tool::isHTTPS(), + Request::isHTTPS(), true ); } @@ -344,7 +343,7 @@ class RememberMe extends Base time() - 3600, BASE_URL_DIRECTORY, null, - Tool::isHTTPS(), + Request::isHTTPS(), true ); } diff --git a/app/Controller/Analytic.php b/app/Controller/Analytic.php index 68177c83..7d112e6a 100644 --- a/app/Controller/Analytic.php +++ b/app/Controller/Analytic.php @@ -27,11 +27,11 @@ class Analytic extends Base } /** - * Show task distribution graph + * Show tasks distribution graph * * @access public */ - public function repartition() + public function tasks() { $project = $this->getProject(); $metrics = $this->projectAnalytic->getTaskRepartition($project['id']); @@ -46,11 +46,39 @@ class Analytic extends Base )); } else { - $this->response->html($this->layout('analytic/repartition', array( + $this->response->html($this->layout('analytic/tasks', array( 'project' => $project, 'metrics' => $metrics, 'title' => t('Task repartition for "%s"', $project['name']), ))); } } + + /** + * Show users repartition + * + * @access public + */ + public function users() + { + $project = $this->getProject(); + $metrics = $this->projectAnalytic->getUserRepartition($project['id']); + + if ($this->request->isAjax()) { + $this->response->json(array( + 'metrics' => $metrics, + 'labels' => array( + 'user' => t('User'), + 'nb_tasks' => t('Number of tasks'), + ) + )); + } + else { + $this->response->html($this->layout('analytic/users', array( + 'project' => $project, + 'metrics' => $metrics, + 'title' => t('User repartition for "%s"', $project['name']), + ))); + } + } } diff --git a/app/Controller/App.php b/app/Controller/App.php index 9f8ded19..2458800a 100644 --- a/app/Controller/App.php +++ b/app/Controller/App.php @@ -3,6 +3,7 @@ namespace Controller; use Model\Project as ProjectModel; +use Model\SubTask; /** * Application controller @@ -19,16 +20,137 @@ class App extends Base */ public function index() { + $paginate = $this->request->getStringParam('paginate', 'userTasks'); + $offset = $this->request->getIntegerParam('offset', 0); + $direction = $this->request->getStringParam('direction'); + $order = $this->request->getStringParam('order'); + $user_id = $this->acl->getUserId(); $projects = $this->projectPermission->getMemberProjects($user_id); $project_ids = array_keys($projects); - $this->response->html($this->template->layout('app/index', array( + $params = array( + 'title' => t('Dashboard'), 'board_selector' => $this->projectPermission->getAllowedProjects($user_id), 'events' => $this->projectActivity->getProjects($project_ids, 10), - 'tasks' => $this->taskFinder->getAllTasksByUser($user_id), - 'projects' => $this->project->getSummary($project_ids), - 'title' => t('Dashboard'), - ))); + ); + + $params += $this->getTaskPagination($user_id, $paginate, $offset, $order, $direction); + $params += $this->getSubtaskPagination($user_id, $paginate, $offset, $order, $direction); + $params += $this->getProjectPagination($project_ids, $paginate, $offset, $order, $direction); + + $this->response->html($this->template->layout('app/dashboard', $params)); + } + + /** + * Get tasks pagination + * + * @access public + */ + private function getTaskPagination($user_id, $paginate, $offset, $order, $direction) + { + $limit = 10; + + if (! in_array($order, array('tasks.id', 'project_name', 'title'))) { + $order = 'tasks.id'; + $direction = 'ASC'; + } + + if ($paginate === 'userTasks') { + $tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit, $order, $direction); + } + else { + $offset = 0; + $tasks = $this->taskPaginator->userTasks($user_id, $offset, $limit); + } + + return array( + 'tasks' => $tasks, + 'task_pagination' => array( + 'controller' => 'app', + 'action' => 'index', + 'params' => array('paginate' => 'userTasks'), + 'direction' => $direction, + 'order' => $order, + 'total' => $this->taskPaginator->countUserTasks($user_id), + 'offset' => $offset, + 'limit' => $limit, + ) + ); + } + + /** + * Get subtasks pagination + * + * @access public + */ + private function getSubtaskPagination($user_id, $paginate, $offset, $order, $direction) + { + $status = array(SubTask::STATUS_TODO, SubTask::STATUS_INPROGRESS); + $limit = 10; + + if (! in_array($order, array('tasks.id', 'project_name', 'status', 'title'))) { + $order = 'tasks.id'; + $direction = 'ASC'; + } + + if ($paginate === 'userSubtasks') { + $subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit, $order, $direction); + } + else { + $offset = 0; + $subtasks = $this->subtaskPaginator->userSubtasks($user_id, $status, $offset, $limit); + } + + return array( + 'subtasks' => $subtasks, + 'subtask_pagination' => array( + 'controller' => 'app', + 'action' => 'index', + 'params' => array('paginate' => 'userSubtasks'), + 'direction' => $direction, + 'order' => $order, + 'total' => $this->subtaskPaginator->countUserSubtasks($user_id, $status), + 'offset' => $offset, + 'limit' => $limit, + ) + ); + } + + /** + * Get projects pagination + * + * @access public + */ + private function getProjectPagination($project_ids, $paginate, $offset, $order, $direction) + { + $limit = 5; + + if (! in_array($order, array('id', 'name'))) { + $order = 'name'; + $direction = 'ASC'; + } + + if ($paginate === 'projectSummaries') { + $projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit, $order, $direction); + } + else { + $offset = 0; + $projects = $this->projectPaginator->projectSummaries($project_ids, $offset, $limit); + } + + return array( + 'projects' => $projects, + 'project_pagination' => array( + 'controller' => 'app', + 'action' => 'index', + 'params' => array('paginate' => 'projectSummaries'), + 'direction' => $direction, + 'order' => $order, + 'total' => count($project_ids), + 'offset' => $offset, + 'limit' => $limit, + ) + ); } } diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 2c8fb221..b0252ecb 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -2,9 +2,13 @@ namespace Controller; +use Pimple\Container; use Core\Tool; -use Core\Registry; use Core\Security; +use Core\Request; +use Core\Response; +use Core\Template; +use Core\Session; use Model\LastLogin; /** @@ -45,64 +49,78 @@ abstract class Base /** * Request instance * - * @accesss public - * @var \Core\Request + * @accesss protected + * @var Core\Request */ - public $request; + protected $request; /** * Response instance * - * @accesss public - * @var \Core\Response + * @accesss protected + * @var Core\Response */ - public $response; + protected $response; /** * Template instance * - * @accesss public - * @var \Core\Template + * @accesss protected + * @var Core\Template */ - public $template; + protected $template; /** * Session instance * * @accesss public - * @var \Core\Session + * @var Core\Session */ - public $session; + protected $session; /** - * Registry instance + * Container instance * * @access private - * @var \Core\Registry + * @var Pimple\Container */ - private $registry; + private $container; /** * Constructor * * @access public - * @param \Core\Registry $registry Registry instance + * @param Pimple\Container $container */ - public function __construct(Registry $registry) + public function __construct(Container $container) { - $this->registry = $registry; + $this->container = $container; + $this->request = new Request; + $this->response = new Response; + $this->session = new Session; + $this->template = new Template; + } + + /** + * Destructor + * + * @access public + */ + public function __destruct() + { + // $this->container['logger']->addDebug(var_export($this->container['db']->getLogMessages(), true)); } /** * Load automatically models * * @access public - * @param string $name Model name + * @param string $name Model name * @return mixed */ public function __get($name) { - return Tool::loadModel($this->registry, $name); + return Tool::loadModel($this->container, $name); } /** @@ -143,7 +161,9 @@ abstract class Base } // Attach events - $this->attachEvents(); + if ($controller === 'board') { + $this->attachEvents(); + } } /** diff --git a/app/Controller/Category.php b/app/Controller/Category.php index 4a0128f3..d69ed115 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -38,7 +38,7 @@ class Category extends Base { $project = $this->getProjectManagement(); - $this->response->html($this->projectLayout('category_index', array( + $this->response->html($this->projectLayout('category/index', array( 'categories' => $this->category->getList($project['id'], false), 'values' => array('project_id' => $project['id']), 'errors' => array(), @@ -70,7 +70,7 @@ class Category extends Base } } - $this->response->html($this->projectLayout('category_index', array( + $this->response->html($this->projectLayout('category/index', array( 'categories' => $this->category->getList($project['id'], false), 'values' => $values, 'errors' => $errors, @@ -89,7 +89,7 @@ class Category extends Base $project = $this->getProjectManagement(); $category = $this->getCategory($project['id']); - $this->response->html($this->projectLayout('category_edit', array( + $this->response->html($this->projectLayout('category/edit', array( 'values' => $category, 'errors' => array(), 'project' => $project, @@ -120,7 +120,7 @@ class Category extends Base } } - $this->response->html($this->projectLayout('category_edit', array( + $this->response->html($this->projectLayout('category/edit', array( 'values' => $values, 'errors' => $errors, 'project' => $project, @@ -138,7 +138,7 @@ class Category extends Base $project = $this->getProjectManagement(); $category = $this->getCategory($project['id']); - $this->response->html($this->projectLayout('category_remove', array( + $this->response->html($this->projectLayout('category/remove', array( 'project' => $project, 'category' => $category, 'title' => t('Remove a category') diff --git a/app/Controller/Project.php b/app/Controller/Project.php index a479b1d6..cac5e0b8 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -428,8 +428,8 @@ class Project extends Base $limit = 25; if ($search !== '') { - $tasks = $this->taskFinder->search($project['id'], $search, $offset, $limit, $order, $direction); - $nb_tasks = $this->taskFinder->countSearch($project['id'], $search); + $tasks = $this->taskPaginator->searchTasks($project['id'], $search, $offset, $limit, $order, $direction); + $nb_tasks = $this->taskPaginator->countSearchTasks($project['id'], $search); } $this->response->html($this->template->layout('project_search', array( @@ -472,8 +472,8 @@ class Project extends Base $offset = $this->request->getIntegerParam('offset', 0); $limit = 25; - $tasks = $this->taskFinder->getClosedTasks($project['id'], $offset, $limit, $order, $direction); - $nb_tasks = $this->taskFinder->countByProjectId($project['id'], array(TaskModel::STATUS_CLOSED)); + $tasks = $this->taskPaginator->closedTasks($project['id'], $offset, $limit, $order, $direction); + $nb_tasks = $this->taskPaginator->countClosedTasks($project['id']); $this->response->html($this->template->layout('project_tasks', array( 'board_selector' => $this->projectPermission->getAllowedProjects($this->acl->getUserId()), diff --git a/app/Core/Registry.php b/app/Core/Registry.php deleted file mode 100644 index d8b9063e..00000000 --- a/app/Core/Registry.php +++ /dev/null @@ -1,83 +0,0 @@ -<?php - -namespace Core; - -use RuntimeException; - -/** - * The registry class is a dependency injection container - * - * @property mixed db - * @property mixed event - * @package core - * @author Frederic Guillot - */ -class Registry -{ - /** - * Contains all dependencies - * - * @access private - * @var array - */ - private $container = array(); - - /** - * Contains all instances - * - * @access private - * @var array - */ - private $instances = array(); - - /** - * Set a dependency - * - * @access public - * @param string $name Unique identifier for the service/parameter - * @param mixed $value The value of the parameter or a closure to define an object - */ - public function __set($name, $value) - { - $this->container[$name] = $value; - } - - /** - * Get a dependency - * - * @access public - * @param string $name Unique identifier for the service/parameter - * @return mixed The value of the parameter or an object - * @throws RuntimeException If the identifier is not found - */ - public function __get($name) - { - if (isset($this->container[$name])) { - - if (is_callable($this->container[$name])) { - return $this->container[$name](); - } - else { - return $this->container[$name]; - } - } - - throw new \RuntimeException('Identifier not found in the registry: '.$name); - } - - /** - * Return a shared instance of a dependency - * - * @access public - * @param string $name Unique identifier for the service/parameter - * @return mixed Same object instance of the dependency - */ - public function shared($name) - { - if (! isset($this->instances[$name])) { - $this->instances[$name] = $this->$name; - } - - return $this->instances[$name]; - } -} diff --git a/app/Core/Request.php b/app/Core/Request.php index 1b643208..c7ca3184 100644 --- a/app/Core/Request.php +++ b/app/Core/Request.php @@ -125,6 +125,20 @@ class Request } /** + * Check if the page is requested through HTTPS + * + * Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS + * + * @static + * @access public + * @return boolean + */ + public static function isHTTPS() + { + return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off'; + } + + /** * Return a HTTP header value * * @access public diff --git a/app/Core/Response.php b/app/Core/Response.php index 347cdde7..6534d64f 100644 --- a/app/Core/Response.php +++ b/app/Core/Response.php @@ -246,7 +246,7 @@ class Response */ public function hsts() { - if (Tool::isHTTPS()) { + if (Request::isHTTPS()) { header('Strict-Transport-Security: max-age=31536000'); } } diff --git a/app/Core/Router.php b/app/Core/Router.php index c9af6e2c..e01c16f6 100644 --- a/app/Core/Router.php +++ b/app/Core/Router.php @@ -2,6 +2,8 @@ namespace Core; +use Pimple\Container; + /** * Router class * @@ -27,24 +29,24 @@ class Router private $action = ''; /** - * Registry instance + * Container instance * * @access private - * @var \Core\Registry + * @var Pimple\Container */ - private $registry; + private $container; /** * Constructor * * @access public - * @param Registry $registry Registry instance - * @param string $controller Controller name - * @param string $action Action name + * @param Pimple\Container $container Container instance + * @param string $controller Controller name + * @param string $action Action name */ - public function __construct(Registry $registry, $controller = '', $action = '') + public function __construct(Container $container, $controller = '', $action = '') { - $this->registry = $registry; + $this->container = $container; $this->controller = empty($_GET['controller']) ? $controller : $_GET['controller']; $this->action = empty($_GET['action']) ? $action : $_GET['action']; } @@ -81,11 +83,7 @@ class Router return false; } - $instance = new $class($this->registry); - $instance->request = new Request; - $instance->response = new Response; - $instance->session = new Session; - $instance->template = new Template; + $instance = new $class($this->container); $instance->beforeAction($this->controller, $this->action); $instance->$method(); diff --git a/app/Core/Session.php b/app/Core/Session.php index 6028f0b9..e50c36b3 100644 --- a/app/Core/Session.php +++ b/app/Core/Session.php @@ -49,7 +49,7 @@ class Session self::SESSION_LIFETIME, $base_path ?: '/', null, - Tool::isHTTPS(), + Request::isHTTPS(), true ); diff --git a/app/Core/Tool.php b/app/Core/Tool.php index e54a0d3b..c010d932 100644 --- a/app/Core/Tool.php +++ b/app/Core/Tool.php @@ -2,6 +2,8 @@ namespace Core; +use Pimple\Container; + /** * Tool class * @@ -37,31 +39,17 @@ class Tool * * @static * @access public - * @param Core\Registry $registry DPI container - * @param string $name Model name + * @param Pimple\Container $container Container instance + * @param string $name Model name * @return mixed */ - public static function loadModel(Registry $registry, $name) + public static function loadModel(Container $container, $name) { - if (! isset($registry->$name)) { + if (! isset($container[$name])) { $class = '\Model\\'.ucfirst($name); - $registry->$name = new $class($registry); + $container[$name] = new $class($container); } - return $registry->shared($name); - } - - /** - * Check if the page is requested through HTTPS - * - * Note: IIS return the value 'off' and other web servers an empty value when it's not HTTPS - * - * @static - * @access public - * @return boolean - */ - public static function isHTTPS() - { - return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== '' && $_SERVER['HTTPS'] !== 'off'; + return $container[$name]; } } diff --git a/app/Event/Base.php b/app/Event/Base.php index 745871a5..dfeeab55 100644 --- a/app/Event/Base.php +++ b/app/Event/Base.php @@ -2,8 +2,8 @@ namespace Event; +use Pimple\Container; use Core\Listener; -use Core\Registry; use Core\Tool; /** @@ -22,22 +22,22 @@ use Core\Tool; abstract class Base implements Listener { /** - * Registry instance + * Container instance * * @access protected - * @var \Core\Registry + * @var Pimple\Container */ - protected $registry; + protected $container; /** * Constructor * * @access public - * @param \Core\Registry $registry Regsitry instance + * @param Pimple\Container $container */ - public function __construct(Registry $registry) + public function __construct(Container $container) { - $this->registry = $registry; + $this->container = $container; } /** @@ -60,7 +60,7 @@ abstract class Base implements Listener */ public function __get($name) { - return Tool::loadModel($this->registry, $name); + return Tool::loadModel($this->container, $name); } /** @@ -73,7 +73,7 @@ abstract class Base implements Listener */ public function getEventNamespace() { - $event_name = $this->registry->event->getLastTriggeredEvent(); + $event_name = $this->container['event']->getLastTriggeredEvent(); return substr($event_name, 0, strpos($event_name, '.')); } } diff --git a/app/Event/ProjectActivityListener.php b/app/Event/ProjectActivityListener.php index 8958bd2b..75efe65d 100644 --- a/app/Event/ProjectActivityListener.php +++ b/app/Event/ProjectActivityListener.php @@ -27,7 +27,7 @@ class ProjectActivityListener extends Base $values['task']['project_id'], $values['task']['id'], $this->acl->getUserId(), - $this->registry->event->getLastTriggeredEvent(), + $this->container['event']->getLastTriggeredEvent(), $values ); } diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 590f2512..bc002699 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index 807118d8..b2b02477 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 25df8acf..a54ccbfb 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index 517430ea..5ac8a047 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 3afa53ce..4c181917 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -571,4 +571,25 @@ return array( 'Reportings' => 'Rapports', 'Task repartition for "%s"' => 'Répartition des tâches pour « %s »', 'Analytics' => 'Analytique', + 'Subtask' => 'Sous-tâche', + 'My subtasks' => 'Mes sous-tâches', + 'User repartition' => 'Répartition des utilisateurs', + 'User repartition for "%s"' => 'Répartition des utilisateurs pour « %s »', + 'Clone this project' => 'Cloner ce projet', + 'Column removed successfully.' => 'Colonne supprimée avec succès.', + 'Edit Project' => 'Modifier le projet', + 'Github Issue' => 'Ticket Github', + 'Not enough data to show the graph.' => 'Pas assez de données pour afficher le graphique', + 'Previous' => 'Précédent', + 'The id must be an integer' => 'L\'id doit être un entier', + 'The project id must be an integer' => 'L\'id du projet doit être un entier', + 'The status must be an integer' => 'Le status doit être un entier', + 'The subtask id is required' => 'L\'id de la sous-tâche est obligatoire', + 'The subtask id must be an integer' => 'L\'id de la sous-tâche doit être en entier', + 'The task id is required' => 'L\'id de la tâche est obligatoire', + 'The task id must be an integer' => 'L\'id de la tâche doit être en entier', + 'The user id must be an integer' => 'L\'id de l\'utilisateur doit être en entier', + 'This value is required' => 'Cette valeur est obligatoire', + 'This value must be numeric' => 'Cette valeur doit être numérique', + 'Unable to create this task.' => 'Impossible de créer cette tâche', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 81ccbd86..7b042e3b 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 2fdfb3d6..2022a184 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index 45253fe1..26982848 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index 778bfd3a..75f39c09 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index f2eaca63..a120d2a9 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index 4f9d815a..93ada787 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 8a1c8b35..b06f2ac1 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Locale/zh_CN/translations.php b/app/Locale/zh_CN/translations.php index 3d45532a..a95ac1bc 100644 --- a/app/Locale/zh_CN/translations.php +++ b/app/Locale/zh_CN/translations.php @@ -571,4 +571,25 @@ return array( // 'Reportings' => '', // 'Task repartition for "%s"' => '', // 'Analytics' => '', + // 'Subtask' => '', + // 'My subtasks' => '', + // 'User repartition' => '', + // 'User repartition for "%s"' => '', + // 'Clone this project' => '', + // 'Column removed successfully.' => '', + // 'Edit Project' => '', + // 'Github Issue' => '', + // 'Not enough data to show the graph.' => '', + // 'Previous' => '', + // 'The id must be an integer' => '', + // 'The project id must be an integer' => '', + // 'The status must be an integer' => '', + // 'The subtask id is required' => '', + // 'The subtask id must be an integer' => '', + // 'The task id is required' => '', + // 'The task id must be an integer' => '', + // 'The user id must be an integer' => '', + // 'This value is required' => '', + // 'This value must be numeric' => '', + // 'Unable to create this task.' => '', ); diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 52957130..25e98850 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -41,7 +41,7 @@ class Acl extends Base 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy', 'time'), 'category' => array('index', 'save', 'edit', 'update', 'confirm', 'remove'), 'action' => array('index', 'event', 'params', 'create', 'confirm', 'remove'), - 'analytic' => array('repartition'), + 'analytic' => array('tasks', 'users'), ); /** diff --git a/app/Model/Action.php b/app/Model/Action.php index c3acdc5b..f8dbd88e 100644 --- a/app/Model/Action.php +++ b/app/Model/Action.php @@ -264,7 +264,7 @@ class Action extends Base public function load($name, $project_id, $event) { $className = '\Action\\'.$name; - return new $className($this->registry, $project_id, $event); + return new $className($this->container, $project_id, $event); } /** diff --git a/app/Model/Authentication.php b/app/Model/Authentication.php index b9ebcfe2..a0e9684f 100644 --- a/app/Model/Authentication.php +++ b/app/Model/Authentication.php @@ -24,12 +24,12 @@ class Authentication extends Base */ public function backend($name) { - if (! isset($this->registry->$name)) { + if (! isset($this->container[$name])) { $class = '\Auth\\'.ucfirst($name); - $this->registry->$name = new $class($this->registry); + $this->container[$name] = new $class($this->container); } - return $this->registry->shared($name); + return $this->container[$name]; } /** diff --git a/app/Model/Base.php b/app/Model/Base.php index 72d91c3c..5a8d8f1c 100644 --- a/app/Model/Base.php +++ b/app/Model/Base.php @@ -4,7 +4,7 @@ namespace Model; use Core\Event; use Core\Tool; -use Core\Registry; +use Pimple\Container; use PicoDb\Database; /** @@ -58,24 +58,24 @@ abstract class Base public $event; /** - * Registry instance + * Container instance * * @access protected - * @var \Core\Registry + * @var Pimple\Container */ - protected $registry; + protected $container; /** * Constructor * * @access public - * @param \Core\Registry $registry Registry instance + * @param Pimple\Container $container */ - public function __construct(Registry $registry) + public function __construct(Container $container) { - $this->registry = $registry; - $this->db = $this->registry->shared('db'); - $this->event = $this->registry->shared('event'); + $this->container = $container; + $this->db = $this->container['db']; + $this->event = $this->container['event']; } /** @@ -87,7 +87,7 @@ abstract class Base */ public function __get($name) { - return Tool::loadModel($this->registry, $name); + return Tool::loadModel($this->container, $name); } /** diff --git a/app/Model/Notification.php b/app/Model/Notification.php index d2fcf525..32765041 100644 --- a/app/Model/Notification.php +++ b/app/Model/Notification.php @@ -117,7 +117,7 @@ class Notification extends Base foreach ($events as $event_name => $template_name) { - $listener = new NotificationListener($this->registry); + $listener = new NotificationListener($this->container); $listener->setTemplate($template_name); $this->event->attach($event_name, $listener); @@ -135,8 +135,7 @@ class Notification extends Base public function sendEmails($template, array $users, array $data) { try { - $transport = $this->registry->shared('mailer'); - $mailer = Swift_Mailer::newInstance($transport); + $mailer = Swift_Mailer::newInstance($this->container['mailer']); $message = Swift_Message::newInstance() ->setSubject($this->getMailSubject($template, $data)) @@ -149,7 +148,7 @@ class Notification extends Base } } catch (Swift_TransportException $e) { - debug($e->getMessage()); + $this->container['logger']->addError($e->getMessage()); } } diff --git a/app/Model/Project.php b/app/Model/Project.php index 305e3f1e..8b842519 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -96,40 +96,6 @@ class Project extends Base } /** - * Get project summary for a list of project (number of tasks for each column) - * - * @access public - * @param array $project_ids List of project id - * @param integer $status Project status - * @param string $order Sort on this column - * @param string $direction Sorting direction - * @return array Project properties - */ - public function getSummary(array $project_ids, $status = self::ACTIVE, $order = 'name', $direction = 'asc') - { - if (empty($project_ids)) { - return array(); - } - - $projects = $this->db->table(self::TABLE) - ->in('id', $project_ids) - ->eq('is_active', $status) - ->orderby($order, $direction) - ->findAll(); - - foreach ($projects as &$project) { - - $project['columns'] = $this->board->getColumns($project['id']); - - foreach ($project['columns'] as &$column) { - $column['nb_tasks'] = $this->taskFinder->countByColumnId($project['id'], $column['id']); - } - } - - return $projects; - } - - /** * Get all projects, optionaly fetch stats for each project and can check users permissions * * @access public @@ -546,7 +512,7 @@ class Project extends Base GithubWebhook::EVENT_COMMIT, ); - $listener = new ProjectModificationDateListener($this->registry); + $listener = new ProjectModificationDateListener($this->container); foreach ($events as $event_name) { $this->event->attach($event_name, $listener); diff --git a/app/Model/ProjectActivity.php b/app/Model/ProjectActivity.php index 6d6ef454..9d7ecfac 100644 --- a/app/Model/ProjectActivity.php +++ b/app/Model/ProjectActivity.php @@ -147,7 +147,7 @@ class ProjectActivity extends Base SubTask::EVENT_CREATE, ); - $listener = new ProjectActivityListener($this->registry); + $listener = new ProjectActivityListener($this->container); foreach ($events as $event_name) { $this->event->attach($event_name, $listener); diff --git a/app/Model/ProjectAnalytic.php b/app/Model/ProjectAnalytic.php index ccd2c4c9..46f2242d 100644 --- a/app/Model/ProjectAnalytic.php +++ b/app/Model/ProjectAnalytic.php @@ -11,7 +11,7 @@ namespace Model; class ProjectAnalytic extends Base { /** - * Get task repartition + * Get tasks repartition * * @access public * @param integer $project_id Project id @@ -34,10 +34,55 @@ class ProjectAnalytic extends Base ); } + if ($total === 0) { + return array(); + } + foreach ($metrics as &$metric) { $metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2); } return $metrics; } + + /** + * Get users repartition + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getUserRepartition($project_id) + { + $metrics = array(); + $total = 0; + $tasks = $this->taskFinder->getAll($project_id); + $users = $this->projectPermission->getMemberList($project_id); + + foreach ($tasks as $task) { + + $user = isset($users[$task['owner_id']]) ? $users[$task['owner_id']] : $users[0]; + $total++; + + if (! isset($metrics[$user])) { + $metrics[$user] = array( + 'nb_tasks' => 0, + 'percentage' => 0, + 'user' => $user, + ); + } + + $metrics[$user]['nb_tasks']++; + } + + if ($total === 0) { + return array(); + } + + foreach ($metrics as &$metric) { + $metric['percentage'] = round(($metric['nb_tasks'] * 100) / $total, 2); + } + + return array_values($metrics); + } } diff --git a/app/Model/ProjectPaginator.php b/app/Model/ProjectPaginator.php new file mode 100644 index 00000000..9f1c39f0 --- /dev/null +++ b/app/Model/ProjectPaginator.php @@ -0,0 +1,49 @@ +<?php + +namespace Model; + +/** + * Project Paginator + * + * @package model + * @author Frederic Guillot + */ +class ProjectPaginator extends Base +{ + /** + * Get project summary for a list of project (number of tasks for each column) + * + * @access public + * @param array $project_ids List of project id + * @param integer $offset Offset + * @param integer $limit Limit + * @param string $column Sorting column + * @param string $direction Sorting direction + * @return array + */ + public function projectSummaries(array $project_ids, $offset = 0, $limit = 25, $column = 'name', $direction = 'asc') + { + if (empty($project_ids)) { + return array(); + } + + $projects = $this->db + ->table(Project::TABLE) + ->in('id', $project_ids) + ->offset($offset) + ->limit($limit) + ->orderBy($column, $direction) + ->findAll(); + + foreach ($projects as &$project) { + + $project['columns'] = $this->board->getColumns($project['id']); + + foreach ($project['columns'] as &$column) { + $column['nb_tasks'] = $this->taskFinder->countByColumnId($project['id'], $column['id']); + } + } + + return $projects; + } +} diff --git a/app/Model/SubtaskPaginator.php b/app/Model/SubtaskPaginator.php new file mode 100644 index 00000000..8ccbd696 --- /dev/null +++ b/app/Model/SubtaskPaginator.php @@ -0,0 +1,68 @@ +<?php + +namespace Model; + +/** + * Subtask Paginator + * + * @package model + * @author Frederic Guillot + */ +class SubtaskPaginator extends Base +{ + /** + * Get all subtasks assigned to a user + * + * @access public + * @param integer $user_id User id + * @param array $status List of status + * @param integer $offset Offset + * @param integer $limit Limit + * @param string $column Sorting column + * @param string $direction Sorting direction + * @return array + */ + public function userSubtasks($user_id, array $status, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'asc') + { + $status_list = $this->subTask->getStatusList(); + + $subtasks = $this->db->table(SubTask::TABLE) + ->columns( + SubTask::TABLE.'.*', + Task::TABLE.'.project_id', + Task::TABLE.'.color_id', + Project::TABLE.'.name AS project_name' + ) + ->eq('user_id', $user_id) + ->in(SubTask::TABLE.'.status', $status) + ->join(Task::TABLE, 'id', 'task_id') + ->join(Project::TABLE, 'id', 'project_id', Task::TABLE) + ->offset($offset) + ->limit($limit) + ->orderBy($column, $direction) + ->findAll(); + + foreach ($subtasks as &$subtask) { + $subtask['status_name'] = $status_list[$subtask['status']]; + } + + return $subtasks; + } + + /** + * Count all subtasks assigned to the user + * + * @access public + * @param integer $user_id User id + * @param array $status List of status + * @return integer + */ + public function countUserSubtasks($user_id, array $status) + { + return $this->db + ->table(SubTask::TABLE) + ->eq('user_id', $user_id) + ->in('status', $status) + ->count(); + } +} diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php index 56795152..0e581025 100644 --- a/app/Model/TaskFinder.php +++ b/app/Model/TaskFinder.php @@ -15,10 +15,10 @@ class TaskFinder extends Base /** * Common request to fetch a list of tasks * - * @access private + * @access public * @return \PicoDb\Table */ - private function prepareRequestList() + public function getQuery() { return $this->db ->table(Task::TABLE) @@ -51,51 +51,6 @@ class TaskFinder extends Base } /** - * Task search with pagination - * - * @access public - * @param integer $project_id Project id - * @param string $search Search terms - * @param integer $offset Offset - * @param integer $limit Limit - * @param string $column Sorting column - * @param string $direction Sorting direction - * @return array - */ - public function search($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC') - { - return $this->prepareRequestList() - ->eq('project_id', $project_id) - ->like('title', '%'.$search.'%') - ->offset($offset) - ->limit($limit) - ->orderBy($column, $direction) - ->findAll(); - } - - /** - * Get all completed tasks with pagination - * - * @access public - * @param integer $project_id Project id - * @param integer $offset Offset - * @param integer $limit Limit - * @param string $column Sorting column - * @param string $direction Sorting direction - * @return array - */ - public function getClosedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC') - { - return $this->prepareRequestList() - ->eq('project_id', $project_id) - ->eq('is_active', Task::STATUS_CLOSED) - ->offset($offset) - ->limit($limit) - ->orderBy($column, $direction) - ->findAll(); - } - - /** * Get all tasks shown on the board (sorted by position) * * @access public @@ -104,7 +59,7 @@ class TaskFinder extends Base */ public function getTasksOnBoard($project_id) { - return $this->prepareRequestList() + return $this->getQuery() ->eq('project_id', $project_id) ->eq('is_active', Task::STATUS_OPEN) ->asc('tasks.position') @@ -112,33 +67,6 @@ class TaskFinder extends Base } /** - * Get all open tasks for a given user - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function getAllTasksByUser($user_id) - { - return $this->db - ->table(Task::TABLE) - ->columns( - 'tasks.id', - 'tasks.title', - 'tasks.date_due', - 'tasks.date_creation', - 'tasks.project_id', - 'tasks.color_id', - 'projects.name AS project_name' - ) - ->join(Project::TABLE, 'id', 'project_id') - ->eq('tasks.owner_id', $user_id) - ->eq('tasks.is_active', Task::STATUS_OPEN) - ->asc('tasks.id') - ->findAll(); - } - - /** * Get all tasks for a given project and status * * @access public @@ -296,22 +224,6 @@ class TaskFinder extends Base } /** - * Count the number of tasks for a custom search - * - * @access public - * @param integer $project_id Project id - * @param string $search Search terms - * @return integer - */ - public function countSearch($project_id, $search) - { - return $this->db->table(Task::TABLE) - ->eq('project_id', $project_id) - ->like('title', '%'.$search.'%') - ->count(); - } - - /** * Return true if the task exists * * @access public diff --git a/app/Model/TaskPaginator.php b/app/Model/TaskPaginator.php new file mode 100644 index 00000000..4ae3566c --- /dev/null +++ b/app/Model/TaskPaginator.php @@ -0,0 +1,139 @@ +<?php + +namespace Model; + +/** + * Task Paginator model + * + * @package model + * @author Frederic Guillot + */ +class TaskPaginator extends Base +{ + /** + * Task search with pagination + * + * @access public + * @param integer $project_id Project id + * @param string $search Search terms + * @param integer $offset Offset + * @param integer $limit Limit + * @param string $column Sorting column + * @param string $direction Sorting direction + * @return array + */ + public function searchTasks($project_id, $search, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'DESC') + { + return $this->taskFinder->getQuery() + ->eq('project_id', $project_id) + ->like('title', '%'.$search.'%') + ->offset($offset) + ->limit($limit) + ->orderBy($column, $direction) + ->findAll(); + } + + /** + * Count the number of tasks for a custom search + * + * @access public + * @param integer $project_id Project id + * @param string $search Search terms + * @return integer + */ + public function countSearchTasks($project_id, $search) + { + return $this->db->table(Task::TABLE) + ->eq('project_id', $project_id) + ->like('title', '%'.$search.'%') + ->count(); + } + + /** + * Get all completed tasks with pagination + * + * @access public + * @param integer $project_id Project id + * @param integer $offset Offset + * @param integer $limit Limit + * @param string $column Sorting column + * @param string $direction Sorting direction + * @return array + */ + public function closedTasks($project_id, $offset = 0, $limit = 25, $column = 'tasks.date_completed', $direction = 'DESC') + { + return $this->taskFinder->getQuery() + ->eq('project_id', $project_id) + ->eq('is_active', Task::STATUS_CLOSED) + ->offset($offset) + ->limit($limit) + ->orderBy($column, $direction) + ->findAll(); + } + + /** + * Count all closed tasks + * + * @access public + * @param integer $project_id Project id + * @param array $status List of status id + * @return integer + */ + public function countClosedTasks($project_id) + { + return $this->db + ->table(Task::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', Task::STATUS_CLOSED) + ->count(); + } + + /** + * Get all open tasks for a given user + * + * @access public + * @param integer $user_id User id + * @param integer $offset Offset + * @param integer $limit Limit + * @param string $column Sorting column + * @param string $direction Sorting direction + * @return array + */ + public function userTasks($user_id, $offset = 0, $limit = 25, $column = 'tasks.id', $direction = 'ASC') + { + return $this->db + ->table(Task::TABLE) + ->columns( + 'tasks.id', + 'tasks.title', + 'tasks.date_due', + 'tasks.date_creation', + 'tasks.project_id', + 'tasks.color_id', + 'projects.name AS project_name' + ) + ->join(Project::TABLE, 'id', 'project_id') + ->eq('tasks.owner_id', $user_id) + ->eq('tasks.is_active', Task::STATUS_OPEN) + ->offset($offset) + ->limit($limit) + ->orderBy($column, $direction) + ->findAll(); + } + + /** + * Count all tasks assigned to the user + * + * @access public + * @param integer $user_id User id + * @return integer + */ + public function countUserTasks($user_id) + { + return $this->db + ->table(Task::TABLE) + ->eq('owner_id', $user_id) + ->eq('is_active', Task::STATUS_OPEN) + ->count(); + } +} diff --git a/app/Model/Webhook.php b/app/Model/Webhook.php index b84728cf..14d50684 100644 --- a/app/Model/Webhook.php +++ b/app/Model/Webhook.php @@ -93,7 +93,7 @@ class Webhook extends Base Task::EVENT_ASSIGNEE_CHANGE, ); - $listener = new WebhookListener($this->registry); + $listener = new WebhookListener($this->container); $listener->setUrl($this->url_task_modification); foreach ($events as $event_name) { @@ -108,7 +108,7 @@ class Webhook extends Base */ public function attachCreateEvents() { - $listener = new WebhookListener($this->registry); + $listener = new WebhookListener($this->container); $listener->setUrl($this->url_task_creation); $this->event->attach(Task::EVENT_CREATE, $listener); diff --git a/app/ServiceProvider/Database.php b/app/ServiceProvider/Database.php new file mode 100644 index 00000000..75e1f73e --- /dev/null +++ b/app/ServiceProvider/Database.php @@ -0,0 +1,100 @@ +<?php + +namespace ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use PicoDb\Database as Dbal; + +class Database implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['db'] = $this->getInstance(); + } + + /** + * Setup the database driver and execute schema migration + * + * @return PicoDb\Database + */ + public function getInstance() + { + switch (DB_DRIVER) { + case 'sqlite': + $db = $this->getSqliteInstance(); + break; + + case 'mysql': + $db = $this->getMysqlInstance(); + break; + + case 'postgres': + $db = $this->getPostgresInstance(); + break; + + default: + die('Database driver not supported'); + } + + if ($db->schema()->check(\Schema\VERSION)) { + return $db; + } + else { + $errors = $db->getLogMessages(); + die('Unable to migrate database schema: <br/><br/><strong>'.(isset($errors[0]) ? $errors[0] : 'Unknown error').'</strong>'); + } + } + + /** + * Setup the Sqlite database driver + * + * @return PicoDb\Database + */ + function getSqliteInstance() + { + require_once __DIR__.'/../Schema/Sqlite.php'; + + return new Dbal(array( + 'driver' => 'sqlite', + 'filename' => DB_FILENAME + )); + } + + /** + * Setup the Mysql database driver + * + * @return PicoDb\Database + */ + function getMysqlInstance() + { + require_once __DIR__.'/../Schema/Mysql.php'; + + return new Dbal(array( + 'driver' => 'mysql', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + 'charset' => 'utf8', + )); + } + + /** + * Setup the Postgres database driver + * + * @return PicoDb\Database + */ + public function getPostgresInstance() + { + require_once __DIR__.'/../Schema/Postgres.php'; + + return new Dbal(array( + 'driver' => 'postgres', + 'hostname' => DB_HOSTNAME, + 'username' => DB_USERNAME, + 'password' => DB_PASSWORD, + 'database' => DB_NAME, + )); + } +} diff --git a/app/ServiceProvider/Event.php b/app/ServiceProvider/Event.php new file mode 100644 index 00000000..0436aa7b --- /dev/null +++ b/app/ServiceProvider/Event.php @@ -0,0 +1,15 @@ +<?php + +namespace ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Core\Event as EventDispatcher; + +class Event implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['event'] = new EventDispatcher; + } +} diff --git a/app/ServiceProvider/Logging.php b/app/ServiceProvider/Logging.php new file mode 100644 index 00000000..9737cadc --- /dev/null +++ b/app/ServiceProvider/Logging.php @@ -0,0 +1,21 @@ +<?php + +namespace ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Monolog\Logger; +use Monolog\Handler\StreamHandler; +use Monolog\Handler\SyslogHandler; + +class Logging implements ServiceProviderInterface +{ + public function register(Container $container) + { + $logger = new Logger('app'); + $logger->pushHandler(new StreamHandler(__DIR__.'/../../data/debug.log', Logger::DEBUG)); + $logger->pushHandler(new SyslogHandler('kanboard', LOG_USER, Logger::DEBUG)); + + $container['logger'] = $logger; + } +} diff --git a/app/ServiceProvider/Mailer.php b/app/ServiceProvider/Mailer.php new file mode 100644 index 00000000..c82c16f6 --- /dev/null +++ b/app/ServiceProvider/Mailer.php @@ -0,0 +1,36 @@ +<?php + +namespace ServiceProvider; + +use Pimple\Container; +use Pimple\ServiceProviderInterface; +use Swift_SmtpTransport; +use Swift_SendmailTransport; +use Swift_MailTransport; + +class Mailer implements ServiceProviderInterface +{ + public function register(Container $container) + { + $container['mailer'] = $this->getInstance(); + } + + public function getInstance() + { + switch (MAIL_TRANSPORT) { + case 'smtp': + $transport = Swift_SmtpTransport::newInstance(MAIL_SMTP_HOSTNAME, MAIL_SMTP_PORT); + $transport->setUsername(MAIL_SMTP_USERNAME); + $transport->setPassword(MAIL_SMTP_PASSWORD); + $transport->setEncryption(MAIL_SMTP_ENCRYPTION); + break; + case 'sendmail': + $transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND); + break; + default: + $transport = Swift_MailTransport::newInstance(); + } + + return $transport; + } +} diff --git a/app/Template/analytic/repartition.php b/app/Template/analytic/repartition.php deleted file mode 100644 index f20b6a0c..00000000 --- a/app/Template/analytic/repartition.php +++ /dev/null @@ -1,29 +0,0 @@ -<div class="page-header"> - <h2><?= t('Task distribution') ?></h2> -</div> -<section id="analytic-repartition"> - -<div id="chart" data-url="<?= Helper\u('analytic', 'repartition', array('project_id' => $project['id'])) ?>"></div> - -<table> - <tr> - <th><?= t('Column') ?></th> - <th><?= t('Number of tasks') ?></th> - <th><?= t('Percentage') ?></th> - </tr> - <?php foreach ($metrics as $metric): ?> - <tr> - <td> - <?= Helper\escape($metric['column_title']) ?> - </td> - <td> - <?= $metric['nb_tasks'] ?> - </td> - <td> - <?= n($metric['percentage']) ?>% - </td> - </tr> - <?php endforeach ?> -</table> - -</section> diff --git a/app/Template/analytic/sidebar.php b/app/Template/analytic/sidebar.php index df6835ee..dded245a 100644 --- a/app/Template/analytic/sidebar.php +++ b/app/Template/analytic/sidebar.php @@ -2,7 +2,10 @@ <h2><?= t('Reportings') ?></h2> <ul> <li> - <?= Helper\a(t('Task distribution'), 'analytic', 'repartition', array('project_id' => $project['id'])) ?> + <?= Helper\a(t('Task distribution'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?> + </li> + <li> + <?= Helper\a(t('User repartition'), 'analytic', 'users', array('project_id' => $project['id'])) ?> </li> </ul> </div>
\ No newline at end of file diff --git a/app/Template/analytic/tasks.php b/app/Template/analytic/tasks.php new file mode 100644 index 00000000..b8b630f9 --- /dev/null +++ b/app/Template/analytic/tasks.php @@ -0,0 +1,34 @@ +<div class="page-header"> + <h2><?= t('Task distribution') ?></h2> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <section id="analytic-task-repartition"> + + <div id="chart" data-url="<?= Helper\u('analytic', 'tasks', array('project_id' => $project['id'])) ?>"></div> + + <table> + <tr> + <th><?= t('Column') ?></th> + <th><?= t('Number of tasks') ?></th> + <th><?= t('Percentage') ?></th> + </tr> + <?php foreach ($metrics as $metric): ?> + <tr> + <td> + <?= Helper\escape($metric['column_title']) ?> + </td> + <td> + <?= $metric['nb_tasks'] ?> + </td> + <td> + <?= n($metric['percentage']) ?>% + </td> + </tr> + <?php endforeach ?> + </table> + + </section> +<?php endif ?> diff --git a/app/Template/analytic/users.php b/app/Template/analytic/users.php new file mode 100644 index 00000000..007f7bc6 --- /dev/null +++ b/app/Template/analytic/users.php @@ -0,0 +1,34 @@ +<div class="page-header"> + <h2><?= t('User repartition') ?></h2> +</div> + +<?php if (empty($metrics)): ?> + <p class="alert"><?= t('Not enough data to show the graph.') ?></p> +<?php else: ?> + <section id="analytic-user-repartition"> + + <div id="chart" data-url="<?= Helper\u('analytic', 'users', array('project_id' => $project['id'])) ?>"></div> + + <table> + <tr> + <th><?= t('User') ?></th> + <th><?= t('Number of tasks') ?></th> + <th><?= t('Percentage') ?></th> + </tr> + <?php foreach ($metrics as $metric): ?> + <tr> + <td> + <?= Helper\escape($metric['user']) ?> + </td> + <td> + <?= $metric['nb_tasks'] ?> + </td> + <td> + <?= n($metric['percentage']) ?>% + </td> + </tr> + <?php endforeach ?> + </table> + + </section> +<?php endif ?> diff --git a/app/Template/app/dashboard.php b/app/Template/app/dashboard.php new file mode 100644 index 00000000..9bcd69f6 --- /dev/null +++ b/app/Template/app/dashboard.php @@ -0,0 +1,26 @@ +<section id="main"> + <div class="page-header"> + <ul> + <?php if (Helper\is_admin()): ?> + <li><i class="fa fa-plus fa-fw"></i><?= Helper\a(t('New project'), 'project', 'create') ?></li> + <?php endif ?> + <li><i class="fa fa-lock fa-fw"></i><?= Helper\a(t('New private project'), 'project', 'create', array('private' => 1)) ?></li> + <li><i class="fa fa-folder fa-fw"></i><?= Helper\a(t('Project management'), 'project', 'index') ?></li> + <?php if (Helper\is_admin()): ?> + <li><i class="fa fa-user fa-fw"></i><?= Helper\a(t('User management'), 'user', 'index') ?></li> + <li><i class="fa fa-cog fa-fw"></i><?= Helper\a(t('Settings'), 'config', 'index') ?></li> + <?php endif ?> + </ul> + </div> + <section id="dashboard"> + <div class="dashboard-left-column"> + <?= Helper\Template('app/projects', array('projects' => $projects, 'pagination' => $project_pagination)) ?> + <?= Helper\Template('app/tasks', array('tasks' => $tasks, 'pagination' => $task_pagination)) ?> + <?= Helper\Template('app/subtasks', array('subtasks' => $subtasks, 'pagination' => $subtask_pagination)) ?> + </div> + <div class="dashboard-right-column"> + <h2><?= t('Activity stream') ?></h2> + <?= Helper\template('project_events', array('events' => $events)) ?> + </div> + </section> +</section>
\ No newline at end of file diff --git a/app/Template/app/index.php b/app/Template/app/index.php deleted file mode 100644 index 89550e05..00000000 --- a/app/Template/app/index.php +++ /dev/null @@ -1,83 +0,0 @@ -<section id="main"> - <div class="page-header"> - <ul> - <?php if (Helper\is_admin()): ?> - <li><i class="fa fa-plus fa-fw"></i><?= Helper\a(t('New project'), 'project', 'create') ?></li> - <?php endif ?> - <li><i class="fa fa-lock fa-fw"></i><?= Helper\a(t('New private project'), 'project', 'create', array('private' => 1)) ?></li> - <li><i class="fa fa-folder fa-fw"></i><?= Helper\a(t('Project management'), 'project', 'index') ?></li> - <?php if (Helper\is_admin()): ?> - <li><i class="fa fa-user fa-fw"></i><?= Helper\a(t('User management'), 'user', 'index') ?></li> - <li><i class="fa fa-cog fa-fw"></i><?= Helper\a(t('Settings'), 'config', 'index') ?></li> - <?php endif ?> - </ul> - </div> - <section id="dashboard"> - <div class="dashboard-left-column"> - <h2><?= t('My projects') ?></h2> - <?php if (empty($projects)): ?> - <p class="alert"><?= t('Your are not member of any project.') ?></p> - <?php else: ?> - <table class="table-fixed"> - <tr> - <th class="column-8"> </th> - <th class="column-20"><?= t('Project') ?></th> - <th><?= t('Columns') ?></th> - </tr> - <?php foreach ($projects as $project): ?> - <tr> - <td> - <?= Helper\a('#'.$project['id'], 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link') ?> - </td> - <td> - <?php if (Helper\is_project_admin($project)): ?> - <?= Helper\a('<i class="fa fa-cog"></i>', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?> - <?php endif ?> - <?= Helper\a(Helper\escape($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?> - </td> - <td class="dashboard-project-stats"> - <?php foreach ($project['columns'] as $column): ?> - <strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong> - <span><?= Helper\escape($column['title']) ?></span> - <?php endforeach ?> - </td> - </tr> - <?php endforeach ?> - </table> - <?php endif ?> - - <h2><?= t('My tasks') ?></h2> - <?php if (empty($tasks)): ?> - <p class="alert"><?= t('There is nothing assigned to you.') ?></p> - <?php else: ?> - <table class="table-fixed"> - <tr> - <th class="column-8"> </th> - <th class="column-20"><?= t('Project') ?></th> - <th><?= t('Task') ?></th> - <th class="column-20"><?= t('Due date') ?></th> - </tr> - <?php foreach ($tasks as $task): ?> - <tr> - <td class="task-table task-<?= $task['color_id'] ?>"> - <?= Helper\a('#'.$task['id'], 'task', 'show', array('task_id' => $task['id'])) ?> - </td> - <td> - <?= Helper\a(Helper\escape($task['project_name']), 'board', 'show', array('project_id' => $task['project_id'])) ?> - </td> - <td> - <?= Helper\a(Helper\escape($task['title']), 'task', 'show', array('task_id' => $task['id'])) ?> - </td> - <td> - <?= dt('%B %e, %Y', $task['date_due']) ?> - </td> - </tr> - <?php endforeach ?> - </table> - <?php endif ?> - </div> - <div class="dashboard-right-column"> - <h2><?= t('Activity stream') ?></h2> - <?= Helper\template('project_events', array('events' => $events)) ?> - </section> -</section>
\ No newline at end of file diff --git a/app/Template/app/projects.php b/app/Template/app/projects.php new file mode 100644 index 00000000..2c13a052 --- /dev/null +++ b/app/Template/app/projects.php @@ -0,0 +1,33 @@ +<h2><?= t('My projects') ?></h2> +<?php if (empty($projects)): ?> + <p class="alert"><?= t('Your are not member of any project.') ?></p> +<?php else: ?> + <table class="table-fixed"> + <tr> + <th class="column-8"><?= Helper\order('Id', 'id', $pagination) ?></th> + <th class="column-20"><?= Helper\order(t('Project'), 'name', $pagination) ?></th> + <th><?= t('Columns') ?></th> + </tr> + <?php foreach ($projects as $project): ?> + <tr> + <td> + <?= Helper\a('#'.$project['id'], 'board', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link') ?> + </td> + <td> + <?php if (Helper\is_project_admin($project)): ?> + <?= Helper\a('<i class="fa fa-cog"></i>', 'project', 'show', array('project_id' => $project['id']), false, 'dashboard-table-link', t('Settings')) ?> + <?php endif ?> + <?= Helper\a(Helper\escape($project['name']), 'board', 'show', array('project_id' => $project['id'])) ?> + </td> + <td class="dashboard-project-stats"> + <?php foreach ($project['columns'] as $column): ?> + <strong title="<?= t('Task count') ?>"><?= $column['nb_tasks'] ?></strong> + <span><?= Helper\escape($column['title']) ?></span> + <?php endforeach ?> + </td> + </tr> + <?php endforeach ?> + </table> + + <?= Helper\paginate($pagination) ?> +<?php endif ?>
\ No newline at end of file diff --git a/app/Template/app/subtasks.php b/app/Template/app/subtasks.php new file mode 100644 index 00000000..f51ef1db --- /dev/null +++ b/app/Template/app/subtasks.php @@ -0,0 +1,31 @@ +<h2><?= t('My subtasks') ?></h2> +<?php if (empty($subtasks)): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <table class="table-fixed"> + <tr> + <th class="column-10"><?= Helper\order(t('Task Id'), 'tasks.id', $pagination) ?></th> + <th class="column-20"><?= Helper\order(t('Project'), 'project_name', $pagination) ?></th> + <th class="column-15"><?= Helper\order(t('Status'), 'status', $pagination) ?></th> + <th><?= Helper\order(t('Subtask'), 'title', $pagination) ?></th> + </tr> + <?php foreach ($subtasks as $subtask): ?> + <tr> + <td class="task-table task-<?= $subtask['color_id'] ?>"> + <?= Helper\a('#'.$subtask['task_id'], 'task', 'show', array('task_id' => $subtask['task_id'])) ?> + </td> + <td> + <?= Helper\a(Helper\escape($subtask['project_name']), 'board', 'show', array('project_id' => $subtask['project_id'])) ?> + </td> + <td> + <?= Helper\escape($subtask['status_name']) ?> + </td> + <td> + <?= Helper\a(Helper\escape($subtask['title']), 'task', 'show', array('task_id' => $subtask['task_id'])) ?> + </td> + </tr> + <?php endforeach ?> + </table> + + <?= Helper\paginate($pagination) ?> +<?php endif ?>
\ No newline at end of file diff --git a/app/Template/app/tasks.php b/app/Template/app/tasks.php new file mode 100644 index 00000000..d1e1ed4e --- /dev/null +++ b/app/Template/app/tasks.php @@ -0,0 +1,31 @@ +<h2><?= t('My tasks') ?></h2> +<?php if (empty($tasks)): ?> + <p class="alert"><?= t('There is nothing assigned to you.') ?></p> +<?php else: ?> + <table class="table-fixed"> + <tr> + <th class="column-8"><?= Helper\order('Id', 'tasks.id', $pagination) ?></th> + <th class="column-20"><?= Helper\order(t('Project'), 'project_name', $pagination) ?></th> + <th><?= Helper\order(t('Task'), 'title', $pagination) ?></th> + <th class="column-20"><?= Helper\order(t('Due date'), 'date_due', $pagination) ?></th> + </tr> + <?php foreach ($tasks as $task): ?> + <tr> + <td class="task-table task-<?= $task['color_id'] ?>"> + <?= Helper\a('#'.$task['id'], 'task', 'show', array('task_id' => $task['id'])) ?> + </td> + <td> + <?= Helper\a(Helper\escape($task['project_name']), 'board', 'show', array('project_id' => $task['project_id'])) ?> + </td> + <td> + <?= Helper\a(Helper\escape($task['title']), 'task', 'show', array('task_id' => $task['id'])) ?> + </td> + <td> + <?= dt('%B %e, %Y', $task['date_due']) ?> + </td> + </tr> + <?php endforeach ?> + </table> + + <?= Helper\paginate($pagination) ?> +<?php endif ?>
\ No newline at end of file diff --git a/app/Template/board/filters.php b/app/Template/board/filters.php index 21e51d98..76cbca2d 100644 --- a/app/Template/board/filters.php +++ b/app/Template/board/filters.php @@ -25,7 +25,7 @@ </li> <li> <i class="fa fa-line-chart fa-fw"></i> - <?= Helper\a(t('Analytics'), 'analytic', 'repartition', array('project_id' => $project['id'])) ?> + <?= Helper\a(t('Analytics'), 'analytic', 'tasks', array('project_id' => $project['id'])) ?> </li> <?php if (Helper\is_admin()): ?> <li><i class="fa fa-cog fa-fw"></i> diff --git a/app/Template/board/rename.php b/app/Template/board/public.php index 6514b0e5..090d617b 100644 --- a/app/Template/board/rename.php +++ b/app/Template/board/public.php @@ -21,7 +21,12 @@ <?php foreach ($column['tasks'] as $task): ?> <div class="task-board task-<?= $task['color_id'] ?>"> - <?= Helper\template('board/task', array('task' => $task, 'categories' => $categories, 'not_editable' => true, 'project' => $project)) ?> + <?= Helper\template('board/task', array( + 'task' => $task, + 'categories' => $categories, + 'not_editable' => true, + 'project' => $project + )) ?> </div> <?php endforeach ?> diff --git a/app/Template/category_edit.php b/app/Template/category/edit.php index 278d7e12..05fe288a 100644 --- a/app/Template/category_edit.php +++ b/app/Template/category/edit.php @@ -2,8 +2,10 @@ <h2><?= t('Category modification for the project "%s"', $project['name']) ?></h2> </div> -<form method="post" action="?controller=category&action=update&project_id=<?= $project['id'] ?>" autocomplete="off"> +<form method="post" action="<?= Helper\u('category', 'update', array('project_id' => $project['id'])) ?>" autocomplete="off"> + <?= Helper\form_csrf() ?> + <?= Helper\form_hidden('id', $values) ?> <?= Helper\form_hidden('project_id', $values) ?> diff --git a/app/Template/category_index.php b/app/Template/category/index.php index 4635406e..8a3eb4f1 100644 --- a/app/Template/category_index.php +++ b/app/Template/category/index.php @@ -14,10 +14,10 @@ <td> <ul> <li> - <a href="?controller=category&action=edit&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Edit') ?></a> + <?= Helper\a(t('Edit'), 'category', 'edit', array('project_id' => $project['id'], 'category_id' => $category_id)) ?> </li> <li> - <a href="?controller=category&action=confirm&project_id=<?= $project['id'] ?>&category_id=<?= $category_id ?>"><?= t('Remove') ?></a> + <?= Helper\a(t('Remove'), 'category', 'confirm', array('project_id' => $project['id'], 'category_id' => $category_id)) ?> </li> </ul> </td> @@ -27,7 +27,7 @@ <?php endif ?> <h3><?= t('Add a new category') ?></h3> -<form method="post" action="?controller=category&action=save&project_id=<?= $project['id'] ?>" autocomplete="off"> +<form method="post" action="<?= Helper\u('category', 'save', array('project_id' => $project['id'])) ?>" autocomplete="off"> <?= Helper\form_csrf() ?> <?= Helper\form_hidden('project_id', $values) ?> diff --git a/app/Template/category_remove.php b/app/Template/category/remove.php index cfc23e07..39e5c1db 100644 --- a/app/Template/category_remove.php +++ b/app/Template/category/remove.php @@ -9,8 +9,9 @@ </p> <div class="form-actions"> - <a href="?controller=category&action=remove&project_id=<?= $project['id'] ?>&category_id=<?= $category['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=category&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'category', 'remove', array('project_id' => $project['id'], 'category_id' => $category['id']), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= Helper\a(t('cancel'), 'category', 'index', array('project_id' => $project['id'])) ?> </div> </div> </section>
\ No newline at end of file diff --git a/app/common.php b/app/common.php index 613d4501..addfe874 100644 --- a/app/common.php +++ b/app/common.php @@ -1,7 +1,5 @@ <?php -// Common file between cli and web interface - require 'vendor/autoload.php'; // Include custom config file @@ -11,7 +9,8 @@ if (file_exists('config.php')) { require __DIR__.'/constants.php'; -$registry = new Core\Registry; -$registry->db = setup_db(); -$registry->event = setup_events(); -$registry->mailer = function() { return setup_mailer(); }; +$container = new Pimple\Container; +$container->register(new ServiceProvider\Logging); +$container->register(new ServiceProvider\Database); +$container->register(new ServiceProvider\Event); +$container->register(new ServiceProvider\Mailer); diff --git a/app/functions.php b/app/functions.php index 5fe218ce..d45e78e7 100644 --- a/app/functions.php +++ b/app/functions.php @@ -1,141 +1,6 @@ <?php -use Core\Event; use Core\Translator; -use PicoDb\Database; - -/** - * Send a debug message to the log files - * - * @param mixed $message Variable or string - */ -function debug($message) -{ - if (! is_string($message)) { - $message = var_export($message, true); - } - - error_log($message.PHP_EOL, 3, 'data/debug.log'); -} - -/** - * Setup events - * - * @return Core\Event - */ -function setup_events() -{ - return new Event; -} - -/** - * Setup the mailer according to the configuration - * - * @return Swift_SmtpTransport - */ -function setup_mailer() -{ - switch (MAIL_TRANSPORT) { - case 'smtp': - $transport = Swift_SmtpTransport::newInstance(MAIL_SMTP_HOSTNAME, MAIL_SMTP_PORT); - $transport->setUsername(MAIL_SMTP_USERNAME); - $transport->setPassword(MAIL_SMTP_PASSWORD); - $transport->setEncryption(MAIL_SMTP_ENCRYPTION); - break; - case 'sendmail': - $transport = Swift_SendmailTransport::newInstance(MAIL_SENDMAIL_COMMAND); - break; - default: - $transport = Swift_MailTransport::newInstance(); - } - - return $transport; -} - -/** - * Setup the database driver and execute schema migration - * - * @return PicoDb\Database - */ -function setup_db() -{ - switch (DB_DRIVER) { - case 'sqlite': - $db = setup_sqlite(); - break; - - case 'mysql': - $db = setup_mysql(); - break; - - case 'postgres': - $db = setup_postgres(); - break; - - default: - die('Database driver not supported'); - } - - if ($db->schema()->check(Schema\VERSION)) { - return $db; - } - else { - $errors = $db->getLogMessages(); - die('Unable to migrate database schema: <br/><br/><strong>'.(isset($errors[0]) ? $errors[0] : 'Unknown error').'</strong>'); - } -} - -/** - * Setup the Sqlite database driver - * - * @return PicoDb\Database - */ -function setup_sqlite() -{ - require_once __DIR__.'/Schema/Sqlite.php'; - - return new Database(array( - 'driver' => 'sqlite', - 'filename' => DB_FILENAME - )); -} - -/** - * Setup the Mysql database driver - * - * @return PicoDb\Database - */ -function setup_mysql() -{ - require_once __DIR__.'/Schema/Mysql.php'; - - return new Database(array( - 'driver' => 'mysql', - 'hostname' => DB_HOSTNAME, - 'username' => DB_USERNAME, - 'password' => DB_PASSWORD, - 'database' => DB_NAME, - 'charset' => 'utf8', - )); -} - -/** - * Setup the Postgres database driver - * - * @return PicoDb\Database - */ -function setup_postgres() -{ - require_once __DIR__.'/Schema/Postgres.php'; - - return new Database(array( - 'driver' => 'postgres', - 'hostname' => DB_HOSTNAME, - 'username' => DB_USERNAME, - 'password' => DB_PASSWORD, - 'database' => DB_NAME, - )); -} /** * Translate a string diff --git a/app/helpers.php b/app/helpers.php index f25db0f0..b53e59e7 100644 --- a/app/helpers.php +++ b/app/helpers.php @@ -8,7 +8,7 @@ namespace Helper; */ use Core\Security; use Core\Template; -use Core\Tool; +use Core\Request; use Parsedown; /** @@ -142,7 +142,7 @@ function markdown($text, array $link = array('controller' => 'task', 'action' => */ function get_current_base_url() { - $url = Tool::isHTTPS() ? 'https://' : 'http://'; + $url = Request::isHTTPS() ? 'https://' : 'http://'; $url .= $_SERVER['SERVER_NAME']; $url .= $_SERVER['SERVER_PORT'] == 80 || $_SERVER['SERVER_PORT'] == 443 ? '' : ':'.$_SERVER['SERVER_PORT']; $url .= dirname($_SERVER['PHP_SELF']) !== '/' ? dirname($_SERVER['PHP_SELF']).'/' : '/'; @@ -618,8 +618,8 @@ function paginate(array $pagination) { extract($pagination); - $html = '<div id="pagination">'; - $html .= '<span id="pagination-previous">'; + $html = '<div class="pagination">'; + $html .= '<span class="pagination-previous">'; if ($pagination['offset'] > 0) { $offset = $pagination['offset'] - $limit; @@ -630,7 +630,7 @@ function paginate(array $pagination) } $html .= '</span>'; - $html .= '<span id="pagination-next">'; + $html .= '<span class="pagination-next">'; if (($total - $pagination['offset']) > $limit) { $offset = $pagination['offset'] + $limit; |