From 14713b0ec7ed93ca45578da069ad4e19a7d8addf Mon Sep 17 00:00:00 2001 From: Frederic Guillot Date: Sat, 28 May 2016 19:48:22 -0400 Subject: Rename all models --- app/Model/Action.php | 190 ----------- app/Model/ActionModel.php | 190 +++++++++++ app/Model/ActionParameter.php | 164 --------- app/Model/ActionParameterModel.php | 164 +++++++++ app/Model/AvatarFile.php | 139 -------- app/Model/AvatarFileModel.php | 139 ++++++++ app/Model/Board.php | 175 ---------- app/Model/BoardModel.php | 175 ++++++++++ app/Model/Category.php | 218 ------------ app/Model/CategoryModel.php | 218 ++++++++++++ app/Model/Color.php | 228 ------------- app/Model/ColorModel.php | 228 +++++++++++++ app/Model/Column.php | 211 ------------ app/Model/ColumnModel.php | 211 ++++++++++++ app/Model/Comment.php | 158 --------- app/Model/CommentModel.php | 158 +++++++++ app/Model/Config.php | 89 ----- app/Model/ConfigModel.php | 89 +++++ app/Model/Currency.php | 111 ------ app/Model/CurrencyModel.php | 111 ++++++ app/Model/CustomFilter.php | 104 ------ app/Model/CustomFilterModel.php | 104 ++++++ app/Model/File.php | 341 ------------------- app/Model/FileModel.php | 341 +++++++++++++++++++ app/Model/Group.php | 119 ------- app/Model/GroupMember.php | 130 ------- app/Model/GroupMemberModel.php | 130 +++++++ app/Model/GroupModel.php | 119 +++++++ app/Model/Language.php | 179 ---------- app/Model/LanguageModel.php | 179 ++++++++++ app/Model/LastLogin.php | 92 ----- app/Model/LastLoginModel.php | 92 +++++ app/Model/Link.php | 178 ---------- app/Model/LinkModel.php | 178 ++++++++++ app/Model/Metadata.php | 139 -------- app/Model/MetadataModel.php | 139 ++++++++ app/Model/Notification.php | 136 -------- app/Model/NotificationModel.php | 136 ++++++++ app/Model/NotificationType.php | 128 ------- app/Model/NotificationTypeModel.php | 128 +++++++ app/Model/PasswordReset.php | 95 ------ app/Model/PasswordResetModel.php | 95 ++++++ app/Model/Project.php | 526 ----------------------------- app/Model/ProjectActivity.php | 94 ------ app/Model/ProjectActivityModel.php | 94 ++++++ app/Model/ProjectDailyColumnStats.php | 254 -------------- app/Model/ProjectDailyColumnStatsModel.php | 254 ++++++++++++++ app/Model/ProjectDailyStats.php | 76 ----- app/Model/ProjectDailyStatsModel.php | 76 +++++ app/Model/ProjectDuplication.php | 161 --------- app/Model/ProjectDuplicationModel.php | 161 +++++++++ app/Model/ProjectFile.php | 42 --- app/Model/ProjectFileModel.php | 40 +++ app/Model/ProjectGroupRole.php | 191 ----------- app/Model/ProjectGroupRoleModel.php | 191 +++++++++++ app/Model/ProjectMetadata.php | 56 --- app/Model/ProjectMetadataModel.php | 54 +++ app/Model/ProjectModel.php | 526 +++++++++++++++++++++++++++++ app/Model/ProjectNotification.php | 67 ---- app/Model/ProjectNotificationModel.php | 67 ++++ app/Model/ProjectNotificationType.php | 59 ---- app/Model/ProjectNotificationTypeModel.php | 57 ++++ app/Model/ProjectPermission.php | 166 --------- app/Model/ProjectPermissionModel.php | 166 +++++++++ app/Model/ProjectUserRole.php | 282 ---------------- app/Model/ProjectUserRoleModel.php | 282 ++++++++++++++++ app/Model/RememberMeSession.php | 152 --------- app/Model/RememberMeSessionModel.php | 152 +++++++++ app/Model/Setting.php | 113 ------- app/Model/SettingModel.php | 113 +++++++ app/Model/Subtask.php | 428 ----------------------- app/Model/SubtaskModel.php | 428 +++++++++++++++++++++++ app/Model/SubtaskTimeTracking.php | 298 ---------------- app/Model/SubtaskTimeTrackingModel.php | 298 ++++++++++++++++ app/Model/Swimlane.php | 490 --------------------------- app/Model/SwimlaneModel.php | 490 +++++++++++++++++++++++++++ app/Model/Task.php | 225 ------------ app/Model/TaskAnalytic.php | 72 ---- app/Model/TaskAnalyticModel.php | 72 ++++ app/Model/TaskCreation.php | 103 ------ app/Model/TaskCreationModel.php | 103 ++++++ app/Model/TaskDuplication.php | 270 --------------- app/Model/TaskDuplicationModel.php | 270 +++++++++++++++ app/Model/TaskExternalLink.php | 101 ------ app/Model/TaskExternalLinkModel.php | 101 ++++++ app/Model/TaskFile.php | 54 --- app/Model/TaskFileModel.php | 54 +++ app/Model/TaskFinder.php | 476 -------------------------- app/Model/TaskFinderModel.php | 476 ++++++++++++++++++++++++++ app/Model/TaskLink.php | 265 --------------- app/Model/TaskLinkModel.php | 265 +++++++++++++++ app/Model/TaskMetadata.php | 35 -- app/Model/TaskMetadataModel.php | 35 ++ app/Model/TaskModel.php | 225 ++++++++++++ app/Model/TaskModification.php | 97 ------ app/Model/TaskModificationModel.php | 97 ++++++ app/Model/TaskPosition.php | 239 ------------- app/Model/TaskPositionModel.php | 239 +++++++++++++ app/Model/TaskStatus.php | 146 -------- app/Model/TaskStatusModel.php | 146 ++++++++ app/Model/Timezone.php | 58 ---- app/Model/TimezoneModel.php | 58 ++++ app/Model/Transition.php | 130 ------- app/Model/TransitionModel.php | 130 +++++++ app/Model/User.php | 390 --------------------- app/Model/UserLocking.php | 105 ------ app/Model/UserLockingModel.php | 105 ++++++ app/Model/UserMention.php | 62 ---- app/Model/UserMentionModel.php | 62 ++++ app/Model/UserMetadata.php | 37 -- app/Model/UserMetadataModel.php | 35 ++ app/Model/UserModel.php | 390 +++++++++++++++++++++ app/Model/UserNotification.php | 204 ----------- app/Model/UserNotificationFilter.php | 206 ----------- app/Model/UserNotificationFilterModel.php | 206 +++++++++++ app/Model/UserNotificationModel.php | 204 +++++++++++ app/Model/UserNotificationType.php | 54 --- app/Model/UserNotificationTypeModel.php | 52 +++ app/Model/UserUnreadNotification.php | 117 ------- app/Model/UserUnreadNotificationModel.php | 117 +++++++ 120 files changed, 10215 insertions(+), 10225 deletions(-) delete mode 100644 app/Model/Action.php create mode 100644 app/Model/ActionModel.php delete mode 100644 app/Model/ActionParameter.php create mode 100644 app/Model/ActionParameterModel.php delete mode 100644 app/Model/AvatarFile.php create mode 100644 app/Model/AvatarFileModel.php delete mode 100644 app/Model/Board.php create mode 100644 app/Model/BoardModel.php delete mode 100644 app/Model/Category.php create mode 100644 app/Model/CategoryModel.php delete mode 100644 app/Model/Color.php create mode 100644 app/Model/ColorModel.php delete mode 100644 app/Model/Column.php create mode 100644 app/Model/ColumnModel.php delete mode 100644 app/Model/Comment.php create mode 100644 app/Model/CommentModel.php delete mode 100644 app/Model/Config.php create mode 100644 app/Model/ConfigModel.php delete mode 100644 app/Model/Currency.php create mode 100644 app/Model/CurrencyModel.php delete mode 100644 app/Model/CustomFilter.php create mode 100644 app/Model/CustomFilterModel.php delete mode 100644 app/Model/File.php create mode 100644 app/Model/FileModel.php delete mode 100644 app/Model/Group.php delete mode 100644 app/Model/GroupMember.php create mode 100644 app/Model/GroupMemberModel.php create mode 100644 app/Model/GroupModel.php delete mode 100644 app/Model/Language.php create mode 100644 app/Model/LanguageModel.php delete mode 100644 app/Model/LastLogin.php create mode 100644 app/Model/LastLoginModel.php delete mode 100644 app/Model/Link.php create mode 100644 app/Model/LinkModel.php delete mode 100644 app/Model/Metadata.php create mode 100644 app/Model/MetadataModel.php delete mode 100644 app/Model/Notification.php create mode 100644 app/Model/NotificationModel.php delete mode 100644 app/Model/NotificationType.php create mode 100644 app/Model/NotificationTypeModel.php delete mode 100644 app/Model/PasswordReset.php create mode 100644 app/Model/PasswordResetModel.php delete mode 100644 app/Model/Project.php delete mode 100644 app/Model/ProjectActivity.php create mode 100644 app/Model/ProjectActivityModel.php delete mode 100644 app/Model/ProjectDailyColumnStats.php create mode 100644 app/Model/ProjectDailyColumnStatsModel.php delete mode 100644 app/Model/ProjectDailyStats.php create mode 100644 app/Model/ProjectDailyStatsModel.php delete mode 100644 app/Model/ProjectDuplication.php create mode 100644 app/Model/ProjectDuplicationModel.php delete mode 100644 app/Model/ProjectFile.php create mode 100644 app/Model/ProjectFileModel.php delete mode 100644 app/Model/ProjectGroupRole.php create mode 100644 app/Model/ProjectGroupRoleModel.php delete mode 100644 app/Model/ProjectMetadata.php create mode 100644 app/Model/ProjectMetadataModel.php create mode 100644 app/Model/ProjectModel.php delete mode 100644 app/Model/ProjectNotification.php create mode 100644 app/Model/ProjectNotificationModel.php delete mode 100644 app/Model/ProjectNotificationType.php create mode 100644 app/Model/ProjectNotificationTypeModel.php delete mode 100644 app/Model/ProjectPermission.php create mode 100644 app/Model/ProjectPermissionModel.php delete mode 100644 app/Model/ProjectUserRole.php create mode 100644 app/Model/ProjectUserRoleModel.php delete mode 100644 app/Model/RememberMeSession.php create mode 100644 app/Model/RememberMeSessionModel.php delete mode 100644 app/Model/Setting.php create mode 100644 app/Model/SettingModel.php delete mode 100644 app/Model/Subtask.php create mode 100644 app/Model/SubtaskModel.php delete mode 100644 app/Model/SubtaskTimeTracking.php create mode 100644 app/Model/SubtaskTimeTrackingModel.php delete mode 100644 app/Model/Swimlane.php create mode 100644 app/Model/SwimlaneModel.php delete mode 100644 app/Model/Task.php delete mode 100644 app/Model/TaskAnalytic.php create mode 100644 app/Model/TaskAnalyticModel.php delete mode 100644 app/Model/TaskCreation.php create mode 100644 app/Model/TaskCreationModel.php delete mode 100644 app/Model/TaskDuplication.php create mode 100644 app/Model/TaskDuplicationModel.php delete mode 100644 app/Model/TaskExternalLink.php create mode 100644 app/Model/TaskExternalLinkModel.php delete mode 100644 app/Model/TaskFile.php create mode 100644 app/Model/TaskFileModel.php delete mode 100644 app/Model/TaskFinder.php create mode 100644 app/Model/TaskFinderModel.php delete mode 100644 app/Model/TaskLink.php create mode 100644 app/Model/TaskLinkModel.php delete mode 100644 app/Model/TaskMetadata.php create mode 100644 app/Model/TaskMetadataModel.php create mode 100644 app/Model/TaskModel.php delete mode 100644 app/Model/TaskModification.php create mode 100644 app/Model/TaskModificationModel.php delete mode 100644 app/Model/TaskPosition.php create mode 100644 app/Model/TaskPositionModel.php delete mode 100644 app/Model/TaskStatus.php create mode 100644 app/Model/TaskStatusModel.php delete mode 100644 app/Model/Timezone.php create mode 100644 app/Model/TimezoneModel.php delete mode 100644 app/Model/Transition.php create mode 100644 app/Model/TransitionModel.php delete mode 100644 app/Model/User.php delete mode 100644 app/Model/UserLocking.php create mode 100644 app/Model/UserLockingModel.php delete mode 100644 app/Model/UserMention.php create mode 100644 app/Model/UserMentionModel.php delete mode 100644 app/Model/UserMetadata.php create mode 100644 app/Model/UserMetadataModel.php create mode 100644 app/Model/UserModel.php delete mode 100644 app/Model/UserNotification.php delete mode 100644 app/Model/UserNotificationFilter.php create mode 100644 app/Model/UserNotificationFilterModel.php create mode 100644 app/Model/UserNotificationModel.php delete mode 100644 app/Model/UserNotificationType.php create mode 100644 app/Model/UserNotificationTypeModel.php delete mode 100644 app/Model/UserUnreadNotification.php create mode 100644 app/Model/UserUnreadNotificationModel.php (limited to 'app/Model') diff --git a/app/Model/Action.php b/app/Model/Action.php deleted file mode 100644 index 568ac85a..00000000 --- a/app/Model/Action.php +++ /dev/null @@ -1,190 +0,0 @@ -projectPermission->getActiveProjectIds($user_id); - $actions = array(); - - if (! empty($project_ids)) { - $actions = $this->db->table(self::TABLE)->in('project_id', $project_ids)->findAll(); - $params = $this->actionParameter->getAllByActions(array_column($actions, 'id')); - $this->attachParamsToActions($actions, $params); - } - - return $actions; - } - - /** - * Return actions and parameters for a given project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAllByProject($project_id) - { - $actions = $this->db->table(self::TABLE)->eq('project_id', $project_id)->findAll(); - $params = $this->actionParameter->getAllByActions(array_column($actions, 'id')); - return $this->attachParamsToActions($actions, $params); - } - - /** - * Return all actions and parameters - * - * @access public - * @return array - */ - public function getAll() - { - $actions = $this->db->table(self::TABLE)->findAll(); - $params = $this->actionParameter->getAll(); - return $this->attachParamsToActions($actions, $params); - } - - /** - * Fetch an action - * - * @access public - * @param integer $action_id - * @return array - */ - public function getById($action_id) - { - $action = $this->db->table(self::TABLE)->eq('id', $action_id)->findOne(); - - if (! empty($action)) { - $action['params'] = $this->actionParameter->getAllByAction($action_id); - } - - return $action; - } - - /** - * Attach parameters to actions - * - * @access private - * @param array &$actions - * @param array &$params - * @return array - */ - private function attachParamsToActions(array &$actions, array &$params) - { - foreach ($actions as &$action) { - $action['params'] = isset($params[$action['id']]) ? $params[$action['id']] : array(); - } - - return $actions; - } - - /** - * Remove an action - * - * @access public - * @param integer $action_id - * @return bool - */ - 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 boolean|integer - */ - 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)->insert($action)) { - $this->db->cancelTransaction(); - return false; - } - - $action_id = $this->db->getLastId(); - - if (! $this->actionParameter->create($action_id, $values)) { - $this->db->cancelTransaction(); - return false; - } - - $this->db->closeTransaction(); - - return $action_id; - } - - /** - * Copy actions from a project to another one (skip actions that cannot resolve parameters) - * - * @author Antonio Rabelo - * @param integer $src_project_id Source project id - * @param integer $dst_project_id Destination project id - * @return boolean - */ - public function duplicate($src_project_id, $dst_project_id) - { - $actions = $this->action->getAllByProject($src_project_id); - - foreach ($actions as $action) { - $this->db->startTransaction(); - - $values = array( - 'project_id' => $dst_project_id, - 'event_name' => $action['event_name'], - 'action_name' => $action['action_name'], - ); - - if (! $this->db->table(self::TABLE)->insert($values)) { - $this->db->cancelTransaction(); - continue; - } - - $action_id = $this->db->getLastId(); - - if (! $this->actionParameter->duplicateParameters($dst_project_id, $action_id, $action['params'])) { - $this->logger->error('Action::duplicate => skip action '.$action['action_name'].' '.$action['id']); - $this->db->cancelTransaction(); - continue; - } - - $this->db->closeTransaction(); - } - - return true; - } -} diff --git a/app/Model/ActionModel.php b/app/Model/ActionModel.php new file mode 100644 index 00000000..53393ed5 --- /dev/null +++ b/app/Model/ActionModel.php @@ -0,0 +1,190 @@ +projectPermissionModel->getActiveProjectIds($user_id); + $actions = array(); + + if (! empty($project_ids)) { + $actions = $this->db->table(self::TABLE)->in('project_id', $project_ids)->findAll(); + $params = $this->actionParameterModel->getAllByActions(array_column($actions, 'id')); + $this->attachParamsToActions($actions, $params); + } + + return $actions; + } + + /** + * Return actions and parameters for a given project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAllByProject($project_id) + { + $actions = $this->db->table(self::TABLE)->eq('project_id', $project_id)->findAll(); + $params = $this->actionParameterModel->getAllByActions(array_column($actions, 'id')); + return $this->attachParamsToActions($actions, $params); + } + + /** + * Return all actions and parameters + * + * @access public + * @return array + */ + public function getAll() + { + $actions = $this->db->table(self::TABLE)->findAll(); + $params = $this->actionParameterModel->getAll(); + return $this->attachParamsToActions($actions, $params); + } + + /** + * Fetch an action + * + * @access public + * @param integer $action_id + * @return array + */ + public function getById($action_id) + { + $action = $this->db->table(self::TABLE)->eq('id', $action_id)->findOne(); + + if (! empty($action)) { + $action['params'] = $this->actionParameterModel->getAllByAction($action_id); + } + + return $action; + } + + /** + * Attach parameters to actions + * + * @access private + * @param array &$actions + * @param array &$params + * @return array + */ + private function attachParamsToActions(array &$actions, array &$params) + { + foreach ($actions as &$action) { + $action['params'] = isset($params[$action['id']]) ? $params[$action['id']] : array(); + } + + return $actions; + } + + /** + * Remove an action + * + * @access public + * @param integer $action_id + * @return bool + */ + 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 boolean|integer + */ + 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)->insert($action)) { + $this->db->cancelTransaction(); + return false; + } + + $action_id = $this->db->getLastId(); + + if (! $this->actionParameterModel->create($action_id, $values)) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return $action_id; + } + + /** + * Copy actions from a project to another one (skip actions that cannot resolve parameters) + * + * @author Antonio Rabelo + * @param integer $src_project_id Source project id + * @param integer $dst_project_id Destination project id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $actions = $this->actionModel->getAllByProject($src_project_id); + + foreach ($actions as $action) { + $this->db->startTransaction(); + + $values = array( + 'project_id' => $dst_project_id, + 'event_name' => $action['event_name'], + 'action_name' => $action['action_name'], + ); + + if (! $this->db->table(self::TABLE)->insert($values)) { + $this->db->cancelTransaction(); + continue; + } + + $action_id = $this->db->getLastId(); + + if (! $this->actionParameterModel->duplicateParameters($dst_project_id, $action_id, $action['params'])) { + $this->logger->error('Action::duplicate => skip action '.$action['action_name'].' '.$action['id']); + $this->db->cancelTransaction(); + continue; + } + + $this->db->closeTransaction(); + } + + return true; + } +} diff --git a/app/Model/ActionParameter.php b/app/Model/ActionParameter.php deleted file mode 100644 index a2fe74e5..00000000 --- a/app/Model/ActionParameter.php +++ /dev/null @@ -1,164 +0,0 @@ -db->table(self::TABLE)->findAll(); - return $this->toDictionary($params); - } - - /** - * Get all params for a list of actions - * - * @access public - * @param array $action_ids - * @return array - */ - public function getAllByActions(array $action_ids) - { - $params = $this->db->table(self::TABLE)->in('action_id', $action_ids)->findAll(); - return $this->toDictionary($params); - } - - /** - * Build params dictionary - * - * @access private - * @param array $params - * @return array - */ - private function toDictionary(array $params) - { - $result = array(); - - foreach ($params as $param) { - $result[$param['action_id']][$param['name']] = $param['value']; - } - - return $result; - } - - /** - * Get all action params for a given action - * - * @access public - * @param integer $action_id - * @return array - */ - public function getAllByAction($action_id) - { - return $this->db->hashtable(self::TABLE)->eq('action_id', $action_id)->getAll('name', 'value'); - } - - /** - * Insert new parameters for an action - * - * @access public - * @param integer $action_id - * @param array $values - * @return boolean - */ - public function create($action_id, array $values) - { - foreach ($values['params'] as $name => $value) { - $param = array( - 'action_id' => $action_id, - 'name' => $name, - 'value' => $value, - ); - - if (! $this->db->table(self::TABLE)->save($param)) { - return false; - } - } - - return true; - } - - /** - * Duplicate action parameters - * - * @access public - * @param integer $project_id - * @param integer $action_id - * @param array $params - * @return boolean - */ - public function duplicateParameters($project_id, $action_id, array $params) - { - foreach ($params as $name => $value) { - $value = $this->resolveParameter($project_id, $name, $value); - - if ($value === false) { - $this->logger->error('ActionParameter::duplicateParameters => unable to resolve '.$name.'='.$value); - return false; - } - - $values = array( - 'action_id' => $action_id, - 'name' => $name, - 'value' => $value, - ); - - if (! $this->db->table(self::TABLE)->insert($values)) { - return false; - } - } - - return true; - } - - /** - * Resolve action parameter values according to another project - * - * @access private - * @param integer $project_id - * @param string $name - * @param string $value - * @return mixed - */ - private function resolveParameter($project_id, $name, $value) - { - switch ($name) { - case 'project_id': - return $value != $project_id ? $value : false; - case 'category_id': - return $this->category->getIdByName($project_id, $this->category->getNameById($value)) ?: false; - case 'src_column_id': - case 'dest_column_id': - case 'dst_column_id': - case 'column_id': - $column = $this->column->getById($value); - return empty($column) ? false : $this->column->getColumnIdByTitle($project_id, $column['title']) ?: false; - case 'user_id': - case 'owner_id': - return $this->projectPermission->isAssignable($project_id, $value) ? $value : false; - default: - return $value; - } - } -} diff --git a/app/Model/ActionParameterModel.php b/app/Model/ActionParameterModel.php new file mode 100644 index 00000000..9895da0f --- /dev/null +++ b/app/Model/ActionParameterModel.php @@ -0,0 +1,164 @@ +db->table(self::TABLE)->findAll(); + return $this->toDictionary($params); + } + + /** + * Get all params for a list of actions + * + * @access public + * @param array $action_ids + * @return array + */ + public function getAllByActions(array $action_ids) + { + $params = $this->db->table(self::TABLE)->in('action_id', $action_ids)->findAll(); + return $this->toDictionary($params); + } + + /** + * Build params dictionary + * + * @access private + * @param array $params + * @return array + */ + private function toDictionary(array $params) + { + $result = array(); + + foreach ($params as $param) { + $result[$param['action_id']][$param['name']] = $param['value']; + } + + return $result; + } + + /** + * Get all action params for a given action + * + * @access public + * @param integer $action_id + * @return array + */ + public function getAllByAction($action_id) + { + return $this->db->hashtable(self::TABLE)->eq('action_id', $action_id)->getAll('name', 'value'); + } + + /** + * Insert new parameters for an action + * + * @access public + * @param integer $action_id + * @param array $values + * @return boolean + */ + public function create($action_id, array $values) + { + foreach ($values['params'] as $name => $value) { + $param = array( + 'action_id' => $action_id, + 'name' => $name, + 'value' => $value, + ); + + if (! $this->db->table(self::TABLE)->save($param)) { + return false; + } + } + + return true; + } + + /** + * Duplicate action parameters + * + * @access public + * @param integer $project_id + * @param integer $action_id + * @param array $params + * @return boolean + */ + public function duplicateParameters($project_id, $action_id, array $params) + { + foreach ($params as $name => $value) { + $value = $this->resolveParameter($project_id, $name, $value); + + if ($value === false) { + $this->logger->error('ActionParameter::duplicateParameters => unable to resolve '.$name.'='.$value); + return false; + } + + $values = array( + 'action_id' => $action_id, + 'name' => $name, + 'value' => $value, + ); + + if (! $this->db->table(self::TABLE)->insert($values)) { + return false; + } + } + + return true; + } + + /** + * Resolve action parameter values according to another project + * + * @access private + * @param integer $project_id + * @param string $name + * @param string $value + * @return mixed + */ + private function resolveParameter($project_id, $name, $value) + { + switch ($name) { + case 'project_id': + return $value != $project_id ? $value : false; + case 'category_id': + return $this->categoryModel->getIdByName($project_id, $this->categoryModel->getNameById($value)) ?: false; + case 'src_column_id': + case 'dest_column_id': + case 'dst_column_id': + case 'column_id': + $column = $this->columnModel->getById($value); + return empty($column) ? false : $this->columnModel->getColumnIdByTitle($project_id, $column['title']) ?: false; + case 'user_id': + case 'owner_id': + return $this->projectPermissionModel->isAssignable($project_id, $value) ? $value : false; + default: + return $value; + } + } +} diff --git a/app/Model/AvatarFile.php b/app/Model/AvatarFile.php deleted file mode 100644 index cfb9e713..00000000 --- a/app/Model/AvatarFile.php +++ /dev/null @@ -1,139 +0,0 @@ -db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('avatar_path'); - } - - /** - * Add avatar in the user profile - * - * @access public - * @param integer $user_id Foreign key - * @param string $path Path on the disk - * @return bool - */ - public function create($user_id, $path) - { - $result = $this->db->table(User::TABLE)->eq('id', $user_id)->update(array( - 'avatar_path' => $path, - )); - - $this->userSession->refresh($user_id); - - return $result; - } - - /** - * Remove avatar from the user profile - * - * @access public - * @param integer $user_id Foreign key - * @return bool - */ - public function remove($user_id) - { - try { - $filename = $this->getFilename($user_id); - - if (! empty($filename)) { - $this->objectStorage->remove($filename); - return $this->db->table(User::TABLE)->eq('id', $user_id)->update(array('avatar_path' => '')); - } - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - return false; - } - - return true; - } - - /** - * Upload avatar image file - * - * @access public - * @param integer $user_id - * @param array $file - * @return boolean - */ - public function uploadImageFile($user_id, array $file) - { - try { - if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { - $destinationFilename = $this->generatePath($user_id, $file['name']); - $this->objectStorage->moveUploadedFile($file['tmp_name'], $destinationFilename); - $this->create($user_id, $destinationFilename); - } else { - throw new Exception('File not uploaded: '.var_export($file['error'], true)); - } - - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - return false; - } - - return true; - } - - /** - * Upload avatar image content - * - * @access public - * @param integer $user_id - * @param string $blob - * @return boolean - */ - public function uploadImageContent($user_id, &$blob) - { - try { - $destinationFilename = $this->generatePath($user_id, 'imageContent'); - $this->objectStorage->put($destinationFilename, $blob); - $this->create($user_id, $destinationFilename); - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - return false; - } - - return true; - } - - /** - * Generate the path for a new filename - * - * @access public - * @param integer $user_id - * @param string $filename - * @return string - */ - public function generatePath($user_id, $filename) - { - return implode(DIRECTORY_SEPARATOR, array(self::PATH_PREFIX, $user_id, hash('sha1', $filename.time()))); - } -} diff --git a/app/Model/AvatarFileModel.php b/app/Model/AvatarFileModel.php new file mode 100644 index 00000000..6e36d83f --- /dev/null +++ b/app/Model/AvatarFileModel.php @@ -0,0 +1,139 @@ +db->table(UserModel::TABLE)->eq('id', $user_id)->findOneColumn('avatar_path'); + } + + /** + * Add avatar in the user profile + * + * @access public + * @param integer $user_id Foreign key + * @param string $path Path on the disk + * @return bool + */ + public function create($user_id, $path) + { + $result = $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array( + 'avatar_path' => $path, + )); + + $this->userSession->refresh($user_id); + + return $result; + } + + /** + * Remove avatar from the user profile + * + * @access public + * @param integer $user_id Foreign key + * @return bool + */ + public function remove($user_id) + { + try { + $filename = $this->getFilename($user_id); + + if (! empty($filename)) { + $this->objectStorage->remove($filename); + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('avatar_path' => '')); + } + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Upload avatar image file + * + * @access public + * @param integer $user_id + * @param array $file + * @return boolean + */ + public function uploadImageFile($user_id, array $file) + { + try { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destinationFilename = $this->generatePath($user_id, $file['name']); + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destinationFilename); + $this->create($user_id, $destinationFilename); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Upload avatar image content + * + * @access public + * @param integer $user_id + * @param string $blob + * @return boolean + */ + public function uploadImageContent($user_id, &$blob) + { + try { + $destinationFilename = $this->generatePath($user_id, 'imageContent'); + $this->objectStorage->put($destinationFilename, $blob); + $this->create($user_id, $destinationFilename); + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + + return true; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $user_id + * @param string $filename + * @return string + */ + public function generatePath($user_id, $filename) + { + return implode(DIRECTORY_SEPARATOR, array(self::PATH_PREFIX, $user_id, hash('sha1', $filename.time()))); + } +} diff --git a/app/Model/Board.php b/app/Model/Board.php deleted file mode 100644 index 91acfb1b..00000000 --- a/app/Model/Board.php +++ /dev/null @@ -1,175 +0,0 @@ -config->get('board_columns', implode(',', $this->getDefaultColumns()))); - $columns = array(); - - foreach ($column_names as $column_name) { - $column_name = trim($column_name); - - if (! empty($column_name)) { - $columns[] = array('title' => $column_name, 'task_limit' => 0, 'description' => ''); - } - } - - return $columns; - } - - /** - * Create a board with default columns, must be executed inside a transaction - * - * @access public - * @param integer $project_id Project id - * @param array $columns Column parameters [ 'title' => 'boo', 'task_limit' => 2 ... ] - * @return boolean - */ - public function create($project_id, array $columns) - { - $position = 0; - - foreach ($columns as $column) { - $values = array( - 'title' => $column['title'], - 'position' => ++$position, - 'project_id' => $project_id, - 'task_limit' => $column['task_limit'], - 'description' => $column['description'], - ); - - if (! $this->db->table(Column::TABLE)->save($values)) { - return false; - } - } - - return true; - } - - /** - * Copy board columns from a project to another one - * - * @author Antonio Rabelo - * @param integer $project_from Project Template - * @param integer $project_to Project that receives the copy - * @return boolean - */ - public function duplicate($project_from, $project_to) - { - $columns = $this->db->table(Column::TABLE) - ->columns('title', 'task_limit', 'description') - ->eq('project_id', $project_from) - ->asc('position') - ->findAll(); - - return $this->board->create($project_to, $columns); - } - - /** - * Get all tasks sorted by columns and swimlanes - * - * @access public - * @param integer $project_id - * @param callable $callback - * @return array - */ - public function getBoard($project_id, $callback = null) - { - $swimlanes = $this->swimlane->getSwimlanes($project_id); - $columns = $this->column->getAll($project_id); - $nb_columns = count($columns); - - for ($i = 0, $ilen = count($swimlanes); $i < $ilen; $i++) { - $swimlanes[$i]['columns'] = $columns; - $swimlanes[$i]['nb_columns'] = $nb_columns; - $swimlanes[$i]['nb_tasks'] = 0; - $swimlanes[$i]['nb_swimlanes'] = $ilen; - - for ($j = 0; $j < $nb_columns; $j++) { - $column_id = $columns[$j]['id']; - $swimlane_id = $swimlanes[$i]['id']; - - if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) { - $swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0; - $swimlanes[0]['columns'][$j]['total_score'] = 0; - } - - $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinder->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id); - $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']); - $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score'); - $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; - $swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; - $swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score']; - } - } - - return $swimlanes; - } - - /** - * Calculate the sum of the defined field for a list of tasks - * - * @access public - * @param array $tasks - * @param string $field - * @return integer - */ - public function getColumnSum(array &$tasks, $field) - { - $sum = 0; - - foreach ($tasks as $task) { - $sum += $task[$field]; - } - - return $sum; - } - - /** - * Get the total of tasks per column - * - * @access public - * @param integer $project_id - * @param boolean $prepend Prepend default value - * @return array - */ - public function getColumnStats($project_id, $prepend = false) - { - $listing = $this->db - ->hashtable(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', 1) - ->groupBy('column_id') - ->getAll('column_id', 'COUNT(*) AS total'); - - return $prepend ? array(-1 => t('All columns')) + $listing : $listing; - } -} diff --git a/app/Model/BoardModel.php b/app/Model/BoardModel.php new file mode 100644 index 00000000..d2718b47 --- /dev/null +++ b/app/Model/BoardModel.php @@ -0,0 +1,175 @@ +configModel->get('board_columns', implode(',', $this->getDefaultColumns()))); + $columns = array(); + + foreach ($column_names as $column_name) { + $column_name = trim($column_name); + + if (! empty($column_name)) { + $columns[] = array('title' => $column_name, 'task_limit' => 0, 'description' => ''); + } + } + + return $columns; + } + + /** + * Create a board with default columns, must be executed inside a transaction + * + * @access public + * @param integer $project_id Project id + * @param array $columns Column parameters [ 'title' => 'boo', 'task_limit' => 2 ... ] + * @return boolean + */ + public function create($project_id, array $columns) + { + $position = 0; + + foreach ($columns as $column) { + $values = array( + 'title' => $column['title'], + 'position' => ++$position, + 'project_id' => $project_id, + 'task_limit' => $column['task_limit'], + 'description' => $column['description'], + ); + + if (! $this->db->table(ColumnModel::TABLE)->save($values)) { + return false; + } + } + + return true; + } + + /** + * Copy board columns from a project to another one + * + * @author Antonio Rabelo + * @param integer $project_from Project Template + * @param integer $project_to Project that receives the copy + * @return boolean + */ + public function duplicate($project_from, $project_to) + { + $columns = $this->db->table(ColumnModel::TABLE) + ->columns('title', 'task_limit', 'description') + ->eq('project_id', $project_from) + ->asc('position') + ->findAll(); + + return $this->boardModel->create($project_to, $columns); + } + + /** + * Get all tasks sorted by columns and swimlanes + * + * @access public + * @param integer $project_id + * @param callable $callback + * @return array + */ + public function getBoard($project_id, $callback = null) + { + $swimlanes = $this->swimlaneModel->getSwimlanes($project_id); + $columns = $this->columnModel->getAll($project_id); + $nb_columns = count($columns); + + for ($i = 0, $ilen = count($swimlanes); $i < $ilen; $i++) { + $swimlanes[$i]['columns'] = $columns; + $swimlanes[$i]['nb_columns'] = $nb_columns; + $swimlanes[$i]['nb_tasks'] = 0; + $swimlanes[$i]['nb_swimlanes'] = $ilen; + + for ($j = 0; $j < $nb_columns; $j++) { + $column_id = $columns[$j]['id']; + $swimlane_id = $swimlanes[$i]['id']; + + if (! isset($swimlanes[0]['columns'][$j]['nb_column_tasks'])) { + $swimlanes[0]['columns'][$j]['nb_column_tasks'] = 0; + $swimlanes[0]['columns'][$j]['total_score'] = 0; + } + + $swimlanes[$i]['columns'][$j]['tasks'] = $callback === null ? $this->taskFinderModel->getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id) : $callback($project_id, $column_id, $swimlane_id); + $swimlanes[$i]['columns'][$j]['nb_tasks'] = count($swimlanes[$i]['columns'][$j]['tasks']); + $swimlanes[$i]['columns'][$j]['score'] = $this->getColumnSum($swimlanes[$i]['columns'][$j]['tasks'], 'score'); + $swimlanes[$i]['nb_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; + $swimlanes[0]['columns'][$j]['nb_column_tasks'] += $swimlanes[$i]['columns'][$j]['nb_tasks']; + $swimlanes[0]['columns'][$j]['total_score'] += $swimlanes[$i]['columns'][$j]['score']; + } + } + + return $swimlanes; + } + + /** + * Calculate the sum of the defined field for a list of tasks + * + * @access public + * @param array $tasks + * @param string $field + * @return integer + */ + public function getColumnSum(array &$tasks, $field) + { + $sum = 0; + + foreach ($tasks as $task) { + $sum += $task[$field]; + } + + return $sum; + } + + /** + * Get the total of tasks per column + * + * @access public + * @param integer $project_id + * @param boolean $prepend Prepend default value + * @return array + */ + public function getColumnStats($project_id, $prepend = false) + { + $listing = $this->db + ->hashtable(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->groupBy('column_id') + ->getAll('column_id', 'COUNT(*) AS total'); + + return $prepend ? array(-1 => t('All columns')) + $listing : $listing; + } +} diff --git a/app/Model/Category.php b/app/Model/Category.php deleted file mode 100644 index 00690a41..00000000 --- a/app/Model/Category.php +++ /dev/null @@ -1,218 +0,0 @@ -db->table(self::TABLE)->eq('id', $category_id)->exists(); - } - - /** - * 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(); - } - - /** - * Get the category name by the id - * - * @access public - * @param integer $category_id Category id - * @return string - */ - public function getNameById($category_id) - { - return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: ''; - } - - /** - * Get a category id by the category name and project id - * - * @access public - * @param integer $project_id Project id - * @param string $category_name Category name - * @return integer - */ - public function getIdByName($project_id, $category_name) - { - return (int) $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('name', $category_name) - ->findOneColumn('id'); - } - - /** - * Return the list of all categories - * - * @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->hashtable(self::TABLE) - ->eq('project_id', $project_id) - ->asc('name') - ->getAll('id', 'name'); - - $prepend = array(); - - if ($prepend_all) { - $prepend[-1] = t('All categories'); - } - - if ($prepend_none) { - $prepend[0] = t('No category'); - } - - return $prepend + $listing; - } - - /** - * Return all categories for a given project - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getAll($project_id) - { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->asc('name') - ->findAll(); - } - - /** - * Create default categories during project creation (transaction already started in Project::create()) - * - * @access public - * @param integer $project_id - * @return boolean - */ - public function createDefaultCategories($project_id) - { - $results = array(); - $categories = explode(',', $this->config->get('project_categories')); - - foreach ($categories as $category) { - $category = trim($category); - - if (! empty($category)) { - $results[] = $this->db->table(self::TABLE)->insert(array( - 'project_id' => $project_id, - 'name' => $category, - )); - } - } - - return in_array(false, $results, true); - } - - /** - * Create a category (run inside a transaction) - * - * @access public - * @param array $values Form values - * @return bool|integer - */ - public function create(array $values) - { - return $this->db->table(self::TABLE)->persist($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(); - - $this->db->table(Task::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0)); - - if (! $this->db->table(self::TABLE)->eq('id', $category_id)->remove()) { - $this->db->cancelTransaction(); - return false; - } - - $this->db->closeTransaction(); - - return true; - } - - /** - * Duplicate categories from a project to another one, must be executed inside a transaction - * - * @author Antonio Rabelo - * @param integer $src_project_id Source project id - * @param integer $dst_project_id Destination project id - * @return boolean - */ - public function duplicate($src_project_id, $dst_project_id) - { - $categories = $this->db - ->table(self::TABLE) - ->columns('name', 'description') - ->eq('project_id', $src_project_id) - ->asc('name') - ->findAll(); - - foreach ($categories as $category) { - $category['project_id'] = $dst_project_id; - - if (! $this->db->table(self::TABLE)->save($category)) { - return false; - } - } - - return true; - } -} diff --git a/app/Model/CategoryModel.php b/app/Model/CategoryModel.php new file mode 100644 index 00000000..62fb5611 --- /dev/null +++ b/app/Model/CategoryModel.php @@ -0,0 +1,218 @@ +db->table(self::TABLE)->eq('id', $category_id)->exists(); + } + + /** + * 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(); + } + + /** + * Get the category name by the id + * + * @access public + * @param integer $category_id Category id + * @return string + */ + public function getNameById($category_id) + { + return $this->db->table(self::TABLE)->eq('id', $category_id)->findOneColumn('name') ?: ''; + } + + /** + * Get a category id by the category name and project id + * + * @access public + * @param integer $project_id Project id + * @param string $category_name Category name + * @return integer + */ + public function getIdByName($project_id, $category_name) + { + return (int) $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('name', $category_name) + ->findOneColumn('id'); + } + + /** + * Return the list of all categories + * + * @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->hashtable(self::TABLE) + ->eq('project_id', $project_id) + ->asc('name') + ->getAll('id', 'name'); + + $prepend = array(); + + if ($prepend_all) { + $prepend[-1] = t('All categories'); + } + + if ($prepend_none) { + $prepend[0] = t('No category'); + } + + return $prepend + $listing; + } + + /** + * Return all categories for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAll($project_id) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->asc('name') + ->findAll(); + } + + /** + * Create default categories during project creation (transaction already started in Project::create()) + * + * @access public + * @param integer $project_id + * @return boolean + */ + public function createDefaultCategories($project_id) + { + $results = array(); + $categories = explode(',', $this->configModel->get('project_categories')); + + foreach ($categories as $category) { + $category = trim($category); + + if (! empty($category)) { + $results[] = $this->db->table(self::TABLE)->insert(array( + 'project_id' => $project_id, + 'name' => $category, + )); + } + } + + return in_array(false, $results, true); + } + + /** + * Create a category (run inside a transaction) + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + return $this->db->table(self::TABLE)->persist($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(); + + $this->db->table(TaskModel::TABLE)->eq('category_id', $category_id)->update(array('category_id' => 0)); + + if (! $this->db->table(self::TABLE)->eq('id', $category_id)->remove()) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return true; + } + + /** + * Duplicate categories from a project to another one, must be executed inside a transaction + * + * @author Antonio Rabelo + * @param integer $src_project_id Source project id + * @param integer $dst_project_id Destination project id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $categories = $this->db + ->table(self::TABLE) + ->columns('name', 'description') + ->eq('project_id', $src_project_id) + ->asc('name') + ->findAll(); + + foreach ($categories as $category) { + $category['project_id'] = $dst_project_id; + + if (! $this->db->table(self::TABLE)->save($category)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/Color.php b/app/Model/Color.php deleted file mode 100644 index 025ca669..00000000 --- a/app/Model/Color.php +++ /dev/null @@ -1,228 +0,0 @@ - array( - 'name' => 'Yellow', - 'background' => 'rgb(245, 247, 196)', - 'border' => 'rgb(223, 227, 45)', - ), - 'blue' => array( - 'name' => 'Blue', - 'background' => 'rgb(219, 235, 255)', - 'border' => 'rgb(168, 207, 255)', - ), - 'green' => array( - 'name' => 'Green', - 'background' => 'rgb(189, 244, 203)', - 'border' => 'rgb(74, 227, 113)', - ), - 'purple' => array( - 'name' => 'Purple', - 'background' => 'rgb(223, 176, 255)', - 'border' => 'rgb(205, 133, 254)', - ), - 'red' => array( - 'name' => 'Red', - 'background' => 'rgb(255, 187, 187)', - 'border' => 'rgb(255, 151, 151)', - ), - 'orange' => array( - 'name' => 'Orange', - 'background' => 'rgb(255, 215, 179)', - 'border' => 'rgb(255, 172, 98)', - ), - 'grey' => array( - 'name' => 'Grey', - 'background' => 'rgb(238, 238, 238)', - 'border' => 'rgb(204, 204, 204)', - ), - 'brown' => array( - 'name' => 'Brown', - 'background' => '#d7ccc8', - 'border' => '#4e342e', - ), - 'deep_orange' => array( - 'name' => 'Deep Orange', - 'background' => '#ffab91', - 'border' => '#e64a19', - ), - 'dark_grey' => array( - 'name' => 'Dark Grey', - 'background' => '#cfd8dc', - 'border' => '#455a64', - ), - 'pink' => array( - 'name' => 'Pink', - 'background' => '#f48fb1', - 'border' => '#d81b60', - ), - 'teal' => array( - 'name' => 'Teal', - 'background' => '#80cbc4', - 'border' => '#00695c', - ), - 'cyan' => array( - 'name' => 'Cyan', - 'background' => '#b2ebf2', - 'border' => '#00bcd4', - ), - 'lime' => array( - 'name' => 'Lime', - 'background' => '#e6ee9c', - 'border' => '#afb42b', - ), - 'light_green' => array( - 'name' => 'Light Green', - 'background' => '#dcedc8', - 'border' => '#689f38', - ), - 'amber' => array( - 'name' => 'Amber', - 'background' => '#ffe082', - 'border' => '#ffa000', - ), - ); - - /** - * Find a color id from the name or the id - * - * @access public - * @param string $color - * @return string - */ - public function find($color) - { - $color = strtolower($color); - - foreach ($this->default_colors as $color_id => $params) { - if ($color_id === $color) { - return $color_id; - } elseif ($color === strtolower($params['name'])) { - return $color_id; - } - } - - return ''; - } - - /** - * Get color properties - * - * @access public - * @param string $color_id - * @return array - */ - public function getColorProperties($color_id) - { - if (isset($this->default_colors[$color_id])) { - return $this->default_colors[$color_id]; - } - - return $this->default_colors[$this->getDefaultColor()]; - } - - /** - * Get available colors - * - * @access public - * @param bool $prepend - * @return array - */ - public function getList($prepend = false) - { - $listing = $prepend ? array('' => t('All colors')) : array(); - - foreach ($this->default_colors as $color_id => $color) { - $listing[$color_id] = t($color['name']); - } - - return $listing; - } - - /** - * Get the default color - * - * @access public - * @return string - */ - public function getDefaultColor() - { - return $this->config->get('default_color', 'yellow'); - } - - /** - * Get the default colors - * - * @access public - * @return array - */ - public function getDefaultColors() - { - return $this->default_colors; - } - - /** - * Get border color from string - * - * @access public - * @param string $color_id Color id - * @return string - */ - public function getBorderColor($color_id) - { - $color = $this->getColorProperties($color_id); - return $color['border']; - } - - /** - * Get background color from the color_id - * - * @access public - * @param string $color_id Color id - * @return string - */ - public function getBackgroundColor($color_id) - { - $color = $this->getColorProperties($color_id); - return $color['background']; - } - - /** - * Get CSS stylesheet of all colors - * - * @access public - * @return string - */ - public function getCss() - { - $buffer = ''; - - foreach ($this->default_colors as $color => $values) { - $buffer .= 'div.color-'.$color.' {'; - $buffer .= 'background-color: '.$values['background'].';'; - $buffer .= 'border-color: '.$values['border']; - $buffer .= '}'; - $buffer .= 'td.color-'.$color.' { background-color: '.$values['background'].'}'; - } - - return $buffer; - } -} diff --git a/app/Model/ColorModel.php b/app/Model/ColorModel.php new file mode 100644 index 00000000..9e69dda2 --- /dev/null +++ b/app/Model/ColorModel.php @@ -0,0 +1,228 @@ + array( + 'name' => 'Yellow', + 'background' => 'rgb(245, 247, 196)', + 'border' => 'rgb(223, 227, 45)', + ), + 'blue' => array( + 'name' => 'Blue', + 'background' => 'rgb(219, 235, 255)', + 'border' => 'rgb(168, 207, 255)', + ), + 'green' => array( + 'name' => 'Green', + 'background' => 'rgb(189, 244, 203)', + 'border' => 'rgb(74, 227, 113)', + ), + 'purple' => array( + 'name' => 'Purple', + 'background' => 'rgb(223, 176, 255)', + 'border' => 'rgb(205, 133, 254)', + ), + 'red' => array( + 'name' => 'Red', + 'background' => 'rgb(255, 187, 187)', + 'border' => 'rgb(255, 151, 151)', + ), + 'orange' => array( + 'name' => 'Orange', + 'background' => 'rgb(255, 215, 179)', + 'border' => 'rgb(255, 172, 98)', + ), + 'grey' => array( + 'name' => 'Grey', + 'background' => 'rgb(238, 238, 238)', + 'border' => 'rgb(204, 204, 204)', + ), + 'brown' => array( + 'name' => 'Brown', + 'background' => '#d7ccc8', + 'border' => '#4e342e', + ), + 'deep_orange' => array( + 'name' => 'Deep Orange', + 'background' => '#ffab91', + 'border' => '#e64a19', + ), + 'dark_grey' => array( + 'name' => 'Dark Grey', + 'background' => '#cfd8dc', + 'border' => '#455a64', + ), + 'pink' => array( + 'name' => 'Pink', + 'background' => '#f48fb1', + 'border' => '#d81b60', + ), + 'teal' => array( + 'name' => 'Teal', + 'background' => '#80cbc4', + 'border' => '#00695c', + ), + 'cyan' => array( + 'name' => 'Cyan', + 'background' => '#b2ebf2', + 'border' => '#00bcd4', + ), + 'lime' => array( + 'name' => 'Lime', + 'background' => '#e6ee9c', + 'border' => '#afb42b', + ), + 'light_green' => array( + 'name' => 'Light Green', + 'background' => '#dcedc8', + 'border' => '#689f38', + ), + 'amber' => array( + 'name' => 'Amber', + 'background' => '#ffe082', + 'border' => '#ffa000', + ), + ); + + /** + * Find a color id from the name or the id + * + * @access public + * @param string $color + * @return string + */ + public function find($color) + { + $color = strtolower($color); + + foreach ($this->default_colors as $color_id => $params) { + if ($color_id === $color) { + return $color_id; + } elseif ($color === strtolower($params['name'])) { + return $color_id; + } + } + + return ''; + } + + /** + * Get color properties + * + * @access public + * @param string $color_id + * @return array + */ + public function getColorProperties($color_id) + { + if (isset($this->default_colors[$color_id])) { + return $this->default_colors[$color_id]; + } + + return $this->default_colors[$this->getDefaultColor()]; + } + + /** + * Get available colors + * + * @access public + * @param bool $prepend + * @return array + */ + public function getList($prepend = false) + { + $listing = $prepend ? array('' => t('All colors')) : array(); + + foreach ($this->default_colors as $color_id => $color) { + $listing[$color_id] = t($color['name']); + } + + return $listing; + } + + /** + * Get the default color + * + * @access public + * @return string + */ + public function getDefaultColor() + { + return $this->configModel->get('default_color', 'yellow'); + } + + /** + * Get the default colors + * + * @access public + * @return array + */ + public function getDefaultColors() + { + return $this->default_colors; + } + + /** + * Get border color from string + * + * @access public + * @param string $color_id Color id + * @return string + */ + public function getBorderColor($color_id) + { + $color = $this->getColorProperties($color_id); + return $color['border']; + } + + /** + * Get background color from the color_id + * + * @access public + * @param string $color_id Color id + * @return string + */ + public function getBackgroundColor($color_id) + { + $color = $this->getColorProperties($color_id); + return $color['background']; + } + + /** + * Get CSS stylesheet of all colors + * + * @access public + * @return string + */ + public function getCss() + { + $buffer = ''; + + foreach ($this->default_colors as $color => $values) { + $buffer .= 'div.color-'.$color.' {'; + $buffer .= 'background-color: '.$values['background'].';'; + $buffer .= 'border-color: '.$values['border']; + $buffer .= '}'; + $buffer .= 'td.color-'.$color.' { background-color: '.$values['background'].'}'; + } + + return $buffer; + } +} diff --git a/app/Model/Column.php b/app/Model/Column.php deleted file mode 100644 index 329ff2d0..00000000 --- a/app/Model/Column.php +++ /dev/null @@ -1,211 +0,0 @@ -db->table(self::TABLE)->eq('id', $column_id)->findOne(); - } - - /** - * Get the first column id for a given project - * - * @access public - * @param integer $project_id Project id - * @return integer - */ - public function getFirstColumnId($project_id) - { - return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findOneColumn('id'); - } - - /** - * Get the last column id for a given project - * - * @access public - * @param integer $project_id Project id - * @return integer - */ - public function getLastColumnId($project_id) - { - return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('position')->findOneColumn('id'); - } - - /** - * 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'); - } - - /** - * Get a column id by the name - * - * @access public - * @param integer $project_id - * @param string $title - * @return integer - */ - public function getColumnIdByTitle($project_id, $title) - { - return (int) $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('title', $title)->findOneColumn('id'); - } - - /** - * Get a column title by the id - * - * @access public - * @param integer $column_id - * @return integer - */ - public function getColumnTitleById($column_id) - { - return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('title'); - } - - /** - * Get all columns sorted by position for a given project - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getAll($project_id) - { - return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findAll(); - } - - /** - * Get the list of columns sorted by position [ column_id => title ] - * - * @access public - * @param integer $project_id Project id - * @param boolean $prepend Prepend a default value - * @return array - */ - public function getList($project_id, $prepend = false) - { - $listing = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'title'); - return $prepend ? array(-1 => t('All columns')) + $listing : $listing; - } - - /** - * Add a new column to the board - * - * @access public - * @param integer $project_id Project id - * @param string $title Column title - * @param integer $task_limit Task limit - * @param string $description Column description - * @return boolean|integer - */ - public function create($project_id, $title, $task_limit = 0, $description = '') - { - $values = array( - 'project_id' => $project_id, - 'title' => $title, - 'task_limit' => intval($task_limit), - 'position' => $this->getLastColumnPosition($project_id) + 1, - 'description' => $description, - ); - - return $this->db->table(self::TABLE)->persist($values); - } - - /** - * Update a column - * - * @access public - * @param integer $column_id Column id - * @param string $title Column title - * @param integer $task_limit Task limit - * @param string $description Optional description - * @return boolean - */ - public function update($column_id, $title, $task_limit = 0, $description = '') - { - return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array( - 'title' => $title, - 'task_limit' => intval($task_limit), - 'description' => $description, - )); - } - - /** - * Remove a column and all tasks associated to this column - * - * @access public - * @param integer $column_id Column id - * @return boolean - */ - public function remove($column_id) - { - return $this->db->table(self::TABLE)->eq('id', $column_id)->remove(); - } - - /** - * Change column position - * - * @access public - * @param integer $project_id - * @param integer $column_id - * @param integer $position - * @return boolean - */ - public function changePosition($project_id, $column_id, $position) - { - if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $project_id)->count()) { - return false; - } - - $column_ids = $this->db->table(self::TABLE)->eq('project_id', $project_id)->neq('id', $column_id)->asc('position')->findAllByColumn('id'); - $offset = 1; - $results = array(); - - foreach ($column_ids as $current_column_id) { - if ($offset == $position) { - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $current_column_id)->update(array('position' => $offset)); - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $column_id)->update(array('position' => $position)); - - return !in_array(false, $results, true); - } -} diff --git a/app/Model/ColumnModel.php b/app/Model/ColumnModel.php new file mode 100644 index 00000000..1adac0f2 --- /dev/null +++ b/app/Model/ColumnModel.php @@ -0,0 +1,211 @@ +db->table(self::TABLE)->eq('id', $column_id)->findOne(); + } + + /** + * Get the first column id for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getFirstColumnId($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findOneColumn('id'); + } + + /** + * Get the last column id for a given project + * + * @access public + * @param integer $project_id Project id + * @return integer + */ + public function getLastColumnId($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->desc('position')->findOneColumn('id'); + } + + /** + * 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'); + } + + /** + * Get a column id by the name + * + * @access public + * @param integer $project_id + * @param string $title + * @return integer + */ + public function getColumnIdByTitle($project_id, $title) + { + return (int) $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('title', $title)->findOneColumn('id'); + } + + /** + * Get a column title by the id + * + * @access public + * @param integer $column_id + * @return integer + */ + public function getColumnTitleById($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->findOneColumn('title'); + } + + /** + * Get all columns sorted by position for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAll($project_id) + { + return $this->db->table(self::TABLE)->eq('project_id', $project_id)->asc('position')->findAll(); + } + + /** + * Get the list of columns sorted by position [ column_id => title ] + * + * @access public + * @param integer $project_id Project id + * @param boolean $prepend Prepend a default value + * @return array + */ + public function getList($project_id, $prepend = false) + { + $listing = $this->db->hashtable(self::TABLE)->eq('project_id', $project_id)->asc('position')->getAll('id', 'title'); + return $prepend ? array(-1 => t('All columns')) + $listing : $listing; + } + + /** + * Add a new column to the board + * + * @access public + * @param integer $project_id Project id + * @param string $title Column title + * @param integer $task_limit Task limit + * @param string $description Column description + * @return boolean|integer + */ + public function create($project_id, $title, $task_limit = 0, $description = '') + { + $values = array( + 'project_id' => $project_id, + 'title' => $title, + 'task_limit' => intval($task_limit), + 'position' => $this->getLastColumnPosition($project_id) + 1, + 'description' => $description, + ); + + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a column + * + * @access public + * @param integer $column_id Column id + * @param string $title Column title + * @param integer $task_limit Task limit + * @param string $description Optional description + * @return boolean + */ + public function update($column_id, $title, $task_limit = 0, $description = '') + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->update(array( + 'title' => $title, + 'task_limit' => intval($task_limit), + 'description' => $description, + )); + } + + /** + * Remove a column and all tasks associated to this column + * + * @access public + * @param integer $column_id Column id + * @return boolean + */ + public function remove($column_id) + { + return $this->db->table(self::TABLE)->eq('id', $column_id)->remove(); + } + + /** + * Change column position + * + * @access public + * @param integer $project_id + * @param integer $column_id + * @param integer $position + * @return boolean + */ + public function changePosition($project_id, $column_id, $position) + { + if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $project_id)->count()) { + return false; + } + + $column_ids = $this->db->table(self::TABLE)->eq('project_id', $project_id)->neq('id', $column_id)->asc('position')->findAllByColumn('id'); + $offset = 1; + $results = array(); + + foreach ($column_ids as $current_column_id) { + if ($offset == $position) { + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $current_column_id)->update(array('position' => $offset)); + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $column_id)->update(array('position' => $position)); + + return !in_array(false, $results, true); + } +} diff --git a/app/Model/Comment.php b/app/Model/Comment.php deleted file mode 100644 index 7d3e1d6e..00000000 --- a/app/Model/Comment.php +++ /dev/null @@ -1,158 +0,0 @@ -db - ->table(self::TABLE) - ->columns( - self::TABLE.'.id', - self::TABLE.'.date_creation', - self::TABLE.'.task_id', - self::TABLE.'.user_id', - self::TABLE.'.comment', - User::TABLE.'.username', - User::TABLE.'.name', - User::TABLE.'.email', - User::TABLE.'.avatar_path' - ) - ->join(User::TABLE, 'id', 'user_id') - ->orderBy(self::TABLE.'.date_creation', $sorting) - ->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_creation', - self::TABLE.'.comment', - self::TABLE.'.reference', - User::TABLE.'.username', - User::TABLE.'.name', - User::TABLE.'.email', - User::TABLE.'.avatar_path' - ) - ->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(); - } - - /** - * Create a new comment - * - * @access public - * @param array $values Form values - * @return boolean|integer - */ - public function create(array $values) - { - $values['date_creation'] = time(); - $comment_id = $this->db->table(self::TABLE)->persist($values); - - if ($comment_id) { - $event = new CommentEvent(array('id' => $comment_id) + $values); - $this->dispatcher->dispatch(self::EVENT_CREATE, $event); - $this->userMention->fireEvents($values['comment'], self::EVENT_USER_MENTION, $event); - } - - return $comment_id; - } - - /** - * Update a comment in the database - * - * @access public - * @param array $values Form values - * @return boolean - */ - public function update(array $values) - { - $result = $this->db - ->table(self::TABLE) - ->eq('id', $values['id']) - ->update(array('comment' => $values['comment'])); - - if ($result) { - $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new CommentEvent($values)); - } - - return $result; - } - - /** - * 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(); - } -} diff --git a/app/Model/CommentModel.php b/app/Model/CommentModel.php new file mode 100644 index 00000000..6e5fd9d8 --- /dev/null +++ b/app/Model/CommentModel.php @@ -0,0 +1,158 @@ +db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.date_creation', + self::TABLE.'.task_id', + self::TABLE.'.user_id', + self::TABLE.'.comment', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + UserModel::TABLE.'.avatar_path' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->orderBy(self::TABLE.'.date_creation', $sorting) + ->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_creation', + self::TABLE.'.comment', + self::TABLE.'.reference', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + UserModel::TABLE.'.email', + UserModel::TABLE.'.avatar_path' + ) + ->join(UserModel::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(); + } + + /** + * Create a new comment + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + $values['date_creation'] = time(); + $comment_id = $this->db->table(self::TABLE)->persist($values); + + if ($comment_id) { + $event = new CommentEvent(array('id' => $comment_id) + $values); + $this->dispatcher->dispatch(self::EVENT_CREATE, $event); + $this->userMentionModel->fireEvents($values['comment'], self::EVENT_USER_MENTION, $event); + } + + return $comment_id; + } + + /** + * Update a comment in the database + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $result = $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update(array('comment' => $values['comment'])); + + if ($result) { + $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new CommentEvent($values)); + } + + return $result; + } + + /** + * 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(); + } +} diff --git a/app/Model/Config.php b/app/Model/Config.php deleted file mode 100644 index 1b14efa1..00000000 --- a/app/Model/Config.php +++ /dev/null @@ -1,89 +0,0 @@ -memoryCache->proxy($this, 'getAll'); - return isset($options[$name]) && $options[$name] !== '' ? $options[$name] : $default_value; - } - - /** - * 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 a token - * - * @access public - * @param string $option Parameter name - * @return boolean - */ - public function regenerateToken($option) - { - return $this->save(array($option => Token::getToken())); - } - - /** - * Prepare data before save - * - * @access public - * @param array $values - * @return array - */ - public function prepare(array $values) - { - if (! empty($values['application_url']) && substr($values['application_url'], -1) !== '/') { - $values['application_url'] = $values['application_url'].'/'; - } - - return $values; - } -} diff --git a/app/Model/ConfigModel.php b/app/Model/ConfigModel.php new file mode 100644 index 00000000..945c5e6f --- /dev/null +++ b/app/Model/ConfigModel.php @@ -0,0 +1,89 @@ +memoryCache->proxy($this, 'getAll'); + return isset($options[$name]) && $options[$name] !== '' ? $options[$name] : $default_value; + } + + /** + * 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 a token + * + * @access public + * @param string $option Parameter name + * @return boolean + */ + public function regenerateToken($option) + { + return $this->save(array($option => Token::getToken())); + } + + /** + * Prepare data before save + * + * @access public + * @param array $values + * @return array + */ + public function prepare(array $values) + { + if (! empty($values['application_url']) && substr($values['application_url'], -1) !== '/') { + $values['application_url'] = $values['application_url'].'/'; + } + + return $values; + } +} diff --git a/app/Model/Currency.php b/app/Model/Currency.php deleted file mode 100644 index 7c8cadd4..00000000 --- a/app/Model/Currency.php +++ /dev/null @@ -1,111 +0,0 @@ - t('USD - US Dollar'), - 'EUR' => t('EUR - Euro'), - 'GBP' => t('GBP - British Pound'), - 'CHF' => t('CHF - Swiss Francs'), - 'CAD' => t('CAD - Canadian Dollar'), - 'AUD' => t('AUD - Australian Dollar'), - 'NZD' => t('NZD - New Zealand Dollar'), - 'INR' => t('INR - Indian Rupee'), - 'JPY' => t('JPY - Japanese Yen'), - 'RSD' => t('RSD - Serbian dinar'), - 'SEK' => t('SEK - Swedish Krona'), - 'NOK' => t('NOK - Norwegian Krone'), - 'BAM' => t('BAM - Konvertible Mark'), - 'RUB' => t('RUB - Russian Ruble'), - ); - } - - /** - * Get all currency rates - * - * @access public - * @return array - */ - public function getAll() - { - return $this->db->table(self::TABLE)->findAll(); - } - - /** - * Calculate the price for the reference currency - * - * @access public - * @param string $currency - * @param double $price - * @return double - */ - public function getPrice($currency, $price) - { - static $rates = null; - $reference = $this->config->get('application_currency', 'USD'); - - if ($reference !== $currency) { - $rates = $rates === null ? $this->db->hashtable(self::TABLE)->getAll('currency', 'rate') : $rates; - $rate = isset($rates[$currency]) ? $rates[$currency] : 1; - - return $rate * $price; - } - - return $price; - } - - /** - * Add a new currency rate - * - * @access public - * @param string $currency - * @param float $rate - * @return boolean|integer - */ - public function create($currency, $rate) - { - if ($this->db->table(self::TABLE)->eq('currency', $currency)->exists()) { - return $this->update($currency, $rate); - } - - return $this->db->table(self::TABLE)->insert(array('currency' => $currency, 'rate' => $rate)); - } - - /** - * Update a currency rate - * - * @access public - * @param string $currency - * @param float $rate - * @return boolean - */ - public function update($currency, $rate) - { - return $this->db->table(self::TABLE)->eq('currency', $currency)->update(array('rate' => $rate)); - } -} diff --git a/app/Model/CurrencyModel.php b/app/Model/CurrencyModel.php new file mode 100644 index 00000000..bfd9697c --- /dev/null +++ b/app/Model/CurrencyModel.php @@ -0,0 +1,111 @@ + t('USD - US Dollar'), + 'EUR' => t('EUR - Euro'), + 'GBP' => t('GBP - British Pound'), + 'CHF' => t('CHF - Swiss Francs'), + 'CAD' => t('CAD - Canadian Dollar'), + 'AUD' => t('AUD - Australian Dollar'), + 'NZD' => t('NZD - New Zealand Dollar'), + 'INR' => t('INR - Indian Rupee'), + 'JPY' => t('JPY - Japanese Yen'), + 'RSD' => t('RSD - Serbian dinar'), + 'SEK' => t('SEK - Swedish Krona'), + 'NOK' => t('NOK - Norwegian Krone'), + 'BAM' => t('BAM - Konvertible Mark'), + 'RUB' => t('RUB - Russian Ruble'), + ); + } + + /** + * Get all currency rates + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->findAll(); + } + + /** + * Calculate the price for the reference currency + * + * @access public + * @param string $currency + * @param double $price + * @return double + */ + public function getPrice($currency, $price) + { + static $rates = null; + $reference = $this->configModel->get('application_currency', 'USD'); + + if ($reference !== $currency) { + $rates = $rates === null ? $this->db->hashtable(self::TABLE)->getAll('currency', 'rate') : $rates; + $rate = isset($rates[$currency]) ? $rates[$currency] : 1; + + return $rate * $price; + } + + return $price; + } + + /** + * Add a new currency rate + * + * @access public + * @param string $currency + * @param float $rate + * @return boolean|integer + */ + public function create($currency, $rate) + { + if ($this->db->table(self::TABLE)->eq('currency', $currency)->exists()) { + return $this->update($currency, $rate); + } + + return $this->db->table(self::TABLE)->insert(array('currency' => $currency, 'rate' => $rate)); + } + + /** + * Update a currency rate + * + * @access public + * @param string $currency + * @param float $rate + * @return boolean + */ + public function update($currency, $rate) + { + return $this->db->table(self::TABLE)->eq('currency', $currency)->update(array('rate' => $rate)); + } +} diff --git a/app/Model/CustomFilter.php b/app/Model/CustomFilter.php deleted file mode 100644 index 9d409bd4..00000000 --- a/app/Model/CustomFilter.php +++ /dev/null @@ -1,104 +0,0 @@ -db - ->table(self::TABLE) - ->columns( - User::TABLE.'.name as owner_name', - User::TABLE.'.username as owner_username', - self::TABLE.'.id', - self::TABLE.'.user_id', - self::TABLE.'.project_id', - self::TABLE.'.filter', - self::TABLE.'.name', - self::TABLE.'.is_shared', - self::TABLE.'.append' - ) - ->asc(self::TABLE.'.name') - ->join(User::TABLE, 'id', 'user_id') - ->beginOr() - ->eq('is_shared', 1) - ->eq('user_id', $user_id) - ->closeOr() - ->eq('project_id', $project_id) - ->findAll(); - } - - /** - * Get custom filter by id - * - * @access private - * @param integer $filter_id - * @return array - */ - public function getById($filter_id) - { - return $this->db->table(self::TABLE)->eq('id', $filter_id)->findOne(); - } - - /** - * Create a custom filter - * - * @access public - * @param array $values Form values - * @return bool|integer - */ - public function create(array $values) - { - return $this->db->table(self::TABLE)->persist($values); - } - - /** - * Update a custom filter - * - * @access public - * @param array $values Form values - * @return bool - */ - public function update(array $values) - { - return $this->db->table(self::TABLE) - ->eq('id', $values['id']) - ->update($values); - } - - /** - * Remove a custom filter - * - * @access public - * @param integer $filter_id - * @return bool - */ - public function remove($filter_id) - { - return $this->db->table(self::TABLE)->eq('id', $filter_id)->remove(); - } -} diff --git a/app/Model/CustomFilterModel.php b/app/Model/CustomFilterModel.php new file mode 100644 index 00000000..a4c23b5c --- /dev/null +++ b/app/Model/CustomFilterModel.php @@ -0,0 +1,104 @@ +db + ->table(self::TABLE) + ->columns( + UserModel::TABLE.'.name as owner_name', + UserModel::TABLE.'.username as owner_username', + self::TABLE.'.id', + self::TABLE.'.user_id', + self::TABLE.'.project_id', + self::TABLE.'.filter', + self::TABLE.'.name', + self::TABLE.'.is_shared', + self::TABLE.'.append' + ) + ->asc(self::TABLE.'.name') + ->join(UserModel::TABLE, 'id', 'user_id') + ->beginOr() + ->eq('is_shared', 1) + ->eq('user_id', $user_id) + ->closeOr() + ->eq('project_id', $project_id) + ->findAll(); + } + + /** + * Get custom filter by id + * + * @access private + * @param integer $filter_id + * @return array + */ + public function getById($filter_id) + { + return $this->db->table(self::TABLE)->eq('id', $filter_id)->findOne(); + } + + /** + * Create a custom filter + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a custom filter + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + return $this->db->table(self::TABLE) + ->eq('id', $values['id']) + ->update($values); + } + + /** + * Remove a custom filter + * + * @access public + * @param integer $filter_id + * @return bool + */ + public function remove($filter_id) + { + return $this->db->table(self::TABLE)->eq('id', $filter_id)->remove(); + } +} diff --git a/app/Model/File.php b/app/Model/File.php deleted file mode 100644 index 92032110..00000000 --- a/app/Model/File.php +++ /dev/null @@ -1,341 +0,0 @@ -db - ->table(static::TABLE) - ->columns( - static::TABLE.'.id', - static::TABLE.'.name', - static::TABLE.'.path', - static::TABLE.'.is_image', - static::TABLE.'.'.static::FOREIGN_KEY, - static::TABLE.'.date', - static::TABLE.'.user_id', - static::TABLE.'.size', - User::TABLE.'.username', - User::TABLE.'.name as user_name' - ) - ->join(User::TABLE, 'id', 'user_id') - ->asc(static::TABLE.'.name'); - } - - /** - * Get a file by the id - * - * @access public - * @param integer $file_id File id - * @return array - */ - public function getById($file_id) - { - return $this->db->table(static::TABLE)->eq('id', $file_id)->findOne(); - } - - /** - * Get all files - * - * @access public - * @param integer $id - * @return array - */ - public function getAll($id) - { - return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->findAll(); - } - - /** - * Get all images - * - * @access public - * @param integer $id - * @return array - */ - public function getAllImages($id) - { - return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->eq('is_image', 1)->findAll(); - } - - /** - * Get all files without images - * - * @access public - * @param integer $id - * @return array - */ - public function getAllDocuments($id) - { - return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->eq('is_image', 0)->findAll(); - } - - /** - * Create a file entry in the database - * - * @access public - * @param integer $id Foreign key - * @param string $name Filename - * @param string $path Path on the disk - * @param integer $size File size - * @return bool|integer - */ - public function create($id, $name, $path, $size) - { - $values = array( - static::FOREIGN_KEY => $id, - 'name' => substr($name, 0, 255), - 'path' => $path, - 'is_image' => $this->isImage($name) ? 1 : 0, - 'size' => $size, - 'user_id' => $this->userSession->getId() ?: 0, - 'date' => time(), - ); - - $result = $this->db->table(static::TABLE)->insert($values); - - if ($result) { - $file_id = (int) $this->db->getLastId(); - $event = new FileEvent($values + array('file_id' => $file_id)); - $this->dispatcher->dispatch(static::EVENT_CREATE, $event); - return $file_id; - } - - return false; - } - - /** - * Remove all files - * - * @access public - * @param integer $id - * @return bool - */ - public function removeAll($id) - { - $file_ids = $this->db->table(static::TABLE)->eq(static::FOREIGN_KEY, $id)->asc('id')->findAllByColumn('id'); - $results = array(); - - foreach ($file_ids as $file_id) { - $results[] = $this->remove($file_id); - } - - return ! in_array(false, $results, true); - } - - /** - * Remove a file - * - * @access public - * @param integer $file_id File id - * @return bool - */ - public function remove($file_id) - { - try { - $file = $this->getById($file_id); - $this->objectStorage->remove($file['path']); - - if ($file['is_image'] == 1) { - $this->objectStorage->remove($this->getThumbnailPath($file['path'])); - } - - return $this->db->table(static::TABLE)->eq('id', $file['id'])->remove(); - } catch (ObjectStorageException $e) { - $this->logger->error($e->getMessage()); - return false; - } - } - - /** - * Check if a filename is an image (file types that can be shown as thumbnail) - * - * @access public - * @param string $filename Filename - * @return bool - */ - public function isImage($filename) - { - $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); - - switch ($extension) { - case 'jpeg': - case 'jpg': - case 'png': - case 'gif': - return true; - } - - return false; - } - - /** - * Generate the path for a thumbnails - * - * @access public - * @param string $key Storage key - * @return string - */ - public function getThumbnailPath($key) - { - return 'thumbnails'.DIRECTORY_SEPARATOR.$key; - } - - /** - * Generate the path for a new filename - * - * @access public - * @param integer $id Foreign key - * @param string $filename Filename - * @return string - */ - public function generatePath($id, $filename) - { - return static::PATH_PREFIX.DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time()); - } - - /** - * Upload multiple files - * - * @access public - * @param integer $id - * @param array $files - * @return bool - */ - public function uploadFiles($id, array $files) - { - try { - if (empty($files)) { - return false; - } - - foreach (array_keys($files['error']) as $key) { - $file = array( - 'name' => $files['name'][$key], - 'tmp_name' => $files['tmp_name'][$key], - 'size' => $files['size'][$key], - 'error' => $files['error'][$key], - ); - - $this->uploadFile($id, $file); - } - - return true; - } catch (Exception $e) { - $this->logger->error($e->getMessage()); - return false; - } - } - - /** - * Upload a file - * - * @access public - * @param integer $id - * @param array $file - */ - public function uploadFile($id, array $file) - { - if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { - $destination_filename = $this->generatePath($id, $file['name']); - - if ($this->isImage($file['name'])) { - $this->generateThumbnailFromFile($file['tmp_name'], $destination_filename); - } - - $this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename); - $this->create($id, $file['name'], $destination_filename, $file['size']); - } else { - throw new Exception('File not uploaded: '.var_export($file['error'], true)); - } - } - - /** - * Handle file upload (base64 encoded content) - * - * @access public - * @param integer $id - * @param string $original_filename - * @param string $blob - * @return bool|integer - */ - public function uploadContent($id, $original_filename, $blob) - { - try { - $data = base64_decode($blob); - - if (empty($data)) { - return false; - } - - $destination_filename = $this->generatePath($id, $original_filename); - $this->objectStorage->put($destination_filename, $data); - - if ($this->isImage($original_filename)) { - $this->generateThumbnailFromData($destination_filename, $data); - } - - return $this->create( - $id, - $original_filename, - $destination_filename, - strlen($data) - ); - } catch (ObjectStorageException $e) { - $this->logger->error($e->getMessage()); - return false; - } - } - - /** - * Generate thumbnail from a blob - * - * @access public - * @param string $destination_filename - * @param string $data - */ - public function generateThumbnailFromData($destination_filename, &$data) - { - $blob = Thumbnail::createFromString($data) - ->resize() - ->toString(); - - $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); - } - - /** - * Generate thumbnail from a local file - * - * @access public - * @param string $uploaded_filename - * @param string $destination_filename - */ - public function generateThumbnailFromFile($uploaded_filename, $destination_filename) - { - $blob = Thumbnail::createFromFile($uploaded_filename) - ->resize() - ->toString(); - - $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); - } -} diff --git a/app/Model/FileModel.php b/app/Model/FileModel.php new file mode 100644 index 00000000..ad839799 --- /dev/null +++ b/app/Model/FileModel.php @@ -0,0 +1,341 @@ +db + ->table(static::TABLE) + ->columns( + static::TABLE.'.id', + static::TABLE.'.name', + static::TABLE.'.path', + static::TABLE.'.is_image', + static::TABLE.'.'.static::FOREIGN_KEY, + static::TABLE.'.date', + static::TABLE.'.user_id', + static::TABLE.'.size', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name as user_name' + ) + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc(static::TABLE.'.name'); + } + + /** + * Get a file by the id + * + * @access public + * @param integer $file_id File id + * @return array + */ + public function getById($file_id) + { + return $this->db->table(static::TABLE)->eq('id', $file_id)->findOne(); + } + + /** + * Get all files + * + * @access public + * @param integer $id + * @return array + */ + public function getAll($id) + { + return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->findAll(); + } + + /** + * Get all images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllImages($id) + { + return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->eq('is_image', 1)->findAll(); + } + + /** + * Get all files without images + * + * @access public + * @param integer $id + * @return array + */ + public function getAllDocuments($id) + { + return $this->getQuery()->eq(static::FOREIGN_KEY, $id)->eq('is_image', 0)->findAll(); + } + + /** + * Create a file entry in the database + * + * @access public + * @param integer $id Foreign key + * @param string $name Filename + * @param string $path Path on the disk + * @param integer $size File size + * @return bool|integer + */ + public function create($id, $name, $path, $size) + { + $values = array( + static::FOREIGN_KEY => $id, + 'name' => substr($name, 0, 255), + 'path' => $path, + 'is_image' => $this->isImage($name) ? 1 : 0, + 'size' => $size, + 'user_id' => $this->userSession->getId() ?: 0, + 'date' => time(), + ); + + $result = $this->db->table(static::TABLE)->insert($values); + + if ($result) { + $file_id = (int) $this->db->getLastId(); + $event = new FileEvent($values + array('file_id' => $file_id)); + $this->dispatcher->dispatch(static::EVENT_CREATE, $event); + return $file_id; + } + + return false; + } + + /** + * Remove all files + * + * @access public + * @param integer $id + * @return bool + */ + public function removeAll($id) + { + $file_ids = $this->db->table(static::TABLE)->eq(static::FOREIGN_KEY, $id)->asc('id')->findAllByColumn('id'); + $results = array(); + + foreach ($file_ids as $file_id) { + $results[] = $this->remove($file_id); + } + + return ! in_array(false, $results, true); + } + + /** + * Remove a file + * + * @access public + * @param integer $file_id File id + * @return bool + */ + public function remove($file_id) + { + try { + $file = $this->getById($file_id); + $this->objectStorage->remove($file['path']); + + if ($file['is_image'] == 1) { + $this->objectStorage->remove($this->getThumbnailPath($file['path'])); + } + + return $this->db->table(static::TABLE)->eq('id', $file['id'])->remove(); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Check if a filename is an image (file types that can be shown as thumbnail) + * + * @access public + * @param string $filename Filename + * @return bool + */ + public function isImage($filename) + { + $extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); + + switch ($extension) { + case 'jpeg': + case 'jpg': + case 'png': + case 'gif': + return true; + } + + return false; + } + + /** + * Generate the path for a thumbnails + * + * @access public + * @param string $key Storage key + * @return string + */ + public function getThumbnailPath($key) + { + return 'thumbnails'.DIRECTORY_SEPARATOR.$key; + } + + /** + * Generate the path for a new filename + * + * @access public + * @param integer $id Foreign key + * @param string $filename Filename + * @return string + */ + public function generatePath($id, $filename) + { + return static::PATH_PREFIX.DIRECTORY_SEPARATOR.$id.DIRECTORY_SEPARATOR.hash('sha1', $filename.time()); + } + + /** + * Upload multiple files + * + * @access public + * @param integer $id + * @param array $files + * @return bool + */ + public function uploadFiles($id, array $files) + { + try { + if (empty($files)) { + return false; + } + + foreach (array_keys($files['error']) as $key) { + $file = array( + 'name' => $files['name'][$key], + 'tmp_name' => $files['tmp_name'][$key], + 'size' => $files['size'][$key], + 'error' => $files['error'][$key], + ); + + $this->uploadFile($id, $file); + } + + return true; + } catch (Exception $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Upload a file + * + * @access public + * @param integer $id + * @param array $file + */ + public function uploadFile($id, array $file) + { + if ($file['error'] == UPLOAD_ERR_OK && $file['size'] > 0) { + $destination_filename = $this->generatePath($id, $file['name']); + + if ($this->isImage($file['name'])) { + $this->generateThumbnailFromFile($file['tmp_name'], $destination_filename); + } + + $this->objectStorage->moveUploadedFile($file['tmp_name'], $destination_filename); + $this->create($id, $file['name'], $destination_filename, $file['size']); + } else { + throw new Exception('File not uploaded: '.var_export($file['error'], true)); + } + } + + /** + * Handle file upload (base64 encoded content) + * + * @access public + * @param integer $id + * @param string $original_filename + * @param string $blob + * @return bool|integer + */ + public function uploadContent($id, $original_filename, $blob) + { + try { + $data = base64_decode($blob); + + if (empty($data)) { + return false; + } + + $destination_filename = $this->generatePath($id, $original_filename); + $this->objectStorage->put($destination_filename, $data); + + if ($this->isImage($original_filename)) { + $this->generateThumbnailFromData($destination_filename, $data); + } + + return $this->create( + $id, + $original_filename, + $destination_filename, + strlen($data) + ); + } catch (ObjectStorageException $e) { + $this->logger->error($e->getMessage()); + return false; + } + } + + /** + * Generate thumbnail from a blob + * + * @access public + * @param string $destination_filename + * @param string $data + */ + public function generateThumbnailFromData($destination_filename, &$data) + { + $blob = Thumbnail::createFromString($data) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } + + /** + * Generate thumbnail from a local file + * + * @access public + * @param string $uploaded_filename + * @param string $destination_filename + */ + public function generateThumbnailFromFile($uploaded_filename, $destination_filename) + { + $blob = Thumbnail::createFromFile($uploaded_filename) + ->resize() + ->toString(); + + $this->objectStorage->put($this->getThumbnailPath($destination_filename), $blob); + } +} diff --git a/app/Model/Group.php b/app/Model/Group.php deleted file mode 100644 index 25579206..00000000 --- a/app/Model/Group.php +++ /dev/null @@ -1,119 +0,0 @@ -db->table(self::TABLE); - } - - /** - * Get a specific group by id - * - * @access public - * @param integer $group_id - * @return array - */ - public function getById($group_id) - { - return $this->getQuery()->eq('id', $group_id)->findOne(); - } - - /** - * Get a specific group by external id - * - * @access public - * @param integer $external_id - * @return array - */ - public function getByExternalId($external_id) - { - return $this->getQuery()->eq('external_id', $external_id)->findOne(); - } - - /** - * Get all groups - * - * @access public - * @return array - */ - public function getAll() - { - return $this->getQuery()->asc('name')->findAll(); - } - - /** - * Search groups by name - * - * @access public - * @param string $input - * @return array - */ - public function search($input) - { - return $this->db->table(self::TABLE)->ilike('name', '%'.$input.'%')->asc('name')->findAll(); - } - - /** - * Remove a group - * - * @access public - * @param integer $group_id - * @return boolean - */ - public function remove($group_id) - { - return $this->db->table(self::TABLE)->eq('id', $group_id)->remove(); - } - - /** - * Create a new group - * - * @access public - * @param string $name - * @param string $external_id - * @return integer|boolean - */ - public function create($name, $external_id = '') - { - return $this->db->table(self::TABLE)->persist(array( - 'name' => $name, - 'external_id' => $external_id, - )); - } - - /** - * Update existing group - * - * @access public - * @param array $values - * @return boolean - */ - public function update(array $values) - { - return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); - } -} diff --git a/app/Model/GroupMember.php b/app/Model/GroupMember.php deleted file mode 100644 index a0bbb9f8..00000000 --- a/app/Model/GroupMember.php +++ /dev/null @@ -1,130 +0,0 @@ -db->table(self::TABLE) - ->join(User::TABLE, 'id', 'user_id') - ->eq('group_id', $group_id); - } - - /** - * Get all users - * - * @access public - * @param integer $group_id - * @return array - */ - public function getMembers($group_id) - { - return $this->getQuery($group_id)->findAll(); - } - - /** - * Get all not members - * - * @access public - * @param integer $group_id - * @return array - */ - public function getNotMembers($group_id) - { - $subquery = $this->db->table(self::TABLE) - ->columns('user_id') - ->eq('group_id', $group_id); - - return $this->db->table(User::TABLE) - ->notInSubquery('id', $subquery) - ->findAll(); - } - - /** - * Add user to a group - * - * @access public - * @param integer $group_id - * @param integer $user_id - * @return boolean - */ - public function addUser($group_id, $user_id) - { - return $this->db->table(self::TABLE)->insert(array( - 'group_id' => $group_id, - 'user_id' => $user_id, - )); - } - - /** - * Remove user from a group - * - * @access public - * @param integer $group_id - * @param integer $user_id - * @return boolean - */ - public function removeUser($group_id, $user_id) - { - return $this->db->table(self::TABLE) - ->eq('group_id', $group_id) - ->eq('user_id', $user_id) - ->remove(); - } - - /** - * Check if a user is member - * - * @access public - * @param integer $group_id - * @param integer $user_id - * @return boolean - */ - public function isMember($group_id, $user_id) - { - return $this->db->table(self::TABLE) - ->eq('group_id', $group_id) - ->eq('user_id', $user_id) - ->exists(); - } - - /** - * Get all groups for a given user - * - * @access public - * @param integer $user_id - * @return array - */ - public function getGroups($user_id) - { - return $this->db->table(self::TABLE) - ->columns(Group::TABLE.'.id', Group::TABLE.'.external_id', Group::TABLE.'.name') - ->join(Group::TABLE, 'id', 'group_id') - ->eq(self::TABLE.'.user_id', $user_id) - ->asc(Group::TABLE.'.name') - ->findAll(); - } -} diff --git a/app/Model/GroupMemberModel.php b/app/Model/GroupMemberModel.php new file mode 100644 index 00000000..a2077789 --- /dev/null +++ b/app/Model/GroupMemberModel.php @@ -0,0 +1,130 @@ +db->table(self::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq('group_id', $group_id); + } + + /** + * Get all users + * + * @access public + * @param integer $group_id + * @return array + */ + public function getMembers($group_id) + { + return $this->getQuery($group_id)->findAll(); + } + + /** + * Get all not members + * + * @access public + * @param integer $group_id + * @return array + */ + public function getNotMembers($group_id) + { + $subquery = $this->db->table(self::TABLE) + ->columns('user_id') + ->eq('group_id', $group_id); + + return $this->db->table(UserModel::TABLE) + ->notInSubquery('id', $subquery) + ->findAll(); + } + + /** + * Add user to a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function addUser($group_id, $user_id) + { + return $this->db->table(self::TABLE)->insert(array( + 'group_id' => $group_id, + 'user_id' => $user_id, + )); + } + + /** + * Remove user from a group + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function removeUser($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->remove(); + } + + /** + * Check if a user is member + * + * @access public + * @param integer $group_id + * @param integer $user_id + * @return boolean + */ + public function isMember($group_id, $user_id) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('user_id', $user_id) + ->exists(); + } + + /** + * Get all groups for a given user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getGroups($user_id) + { + return $this->db->table(self::TABLE) + ->columns(GroupModel::TABLE.'.id', GroupModel::TABLE.'.external_id', GroupModel::TABLE.'.name') + ->join(GroupModel::TABLE, 'id', 'group_id') + ->eq(self::TABLE.'.user_id', $user_id) + ->asc(GroupModel::TABLE.'.name') + ->findAll(); + } +} diff --git a/app/Model/GroupModel.php b/app/Model/GroupModel.php new file mode 100644 index 00000000..0a975570 --- /dev/null +++ b/app/Model/GroupModel.php @@ -0,0 +1,119 @@ +db->table(self::TABLE); + } + + /** + * Get a specific group by id + * + * @access public + * @param integer $group_id + * @return array + */ + public function getById($group_id) + { + return $this->getQuery()->eq('id', $group_id)->findOne(); + } + + /** + * Get a specific group by external id + * + * @access public + * @param integer $external_id + * @return array + */ + public function getByExternalId($external_id) + { + return $this->getQuery()->eq('external_id', $external_id)->findOne(); + } + + /** + * Get all groups + * + * @access public + * @return array + */ + public function getAll() + { + return $this->getQuery()->asc('name')->findAll(); + } + + /** + * Search groups by name + * + * @access public + * @param string $input + * @return array + */ + public function search($input) + { + return $this->db->table(self::TABLE)->ilike('name', '%'.$input.'%')->asc('name')->findAll(); + } + + /** + * Remove a group + * + * @access public + * @param integer $group_id + * @return boolean + */ + public function remove($group_id) + { + return $this->db->table(self::TABLE)->eq('id', $group_id)->remove(); + } + + /** + * Create a new group + * + * @access public + * @param string $name + * @param string $external_id + * @return integer|boolean + */ + public function create($name, $external_id = '') + { + return $this->db->table(self::TABLE)->persist(array( + 'name' => $name, + 'external_id' => $external_id, + )); + } + + /** + * Update existing group + * + * @access public + * @param array $values + * @return boolean + */ + public function update(array $values) + { + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); + } +} diff --git a/app/Model/Language.php b/app/Model/Language.php deleted file mode 100644 index 126dc5c0..00000000 --- a/app/Model/Language.php +++ /dev/null @@ -1,179 +0,0 @@ - 'Bahasa Indonesia', - 'bs_BA' => 'Bosanski', - 'cs_CZ' => 'Čeština', - 'da_DK' => 'Dansk', - 'de_DE' => 'Deutsch', - 'en_US' => 'English', - 'es_ES' => 'Español', - 'fr_FR' => 'Français', - 'el_GR' => 'Grec', - 'it_IT' => 'Italiano', - 'hu_HU' => 'Magyar', - 'my_MY' => 'Melayu', - 'nl_NL' => 'Nederlands', - 'nb_NO' => 'Norsk', - 'pl_PL' => 'Polski', - 'pt_PT' => 'Português', - 'pt_BR' => 'Português (Brasil)', - 'ru_RU' => 'Русский', - 'sr_Latn_RS' => 'Srpski', - 'fi_FI' => 'Suomi', - 'sv_SE' => 'Svenska', - 'tr_TR' => 'Türkçe', - 'ko_KR' => '한국어', - 'zh_CN' => '中文(简体)', - 'ja_JP' => '日本語', - 'th_TH' => 'ไทย', - ); - - if ($prepend) { - return array('' => t('Application default')) + $languages; - } - - return $languages; - } - - /** - * Get javascript language code - * - * @access public - * @return string - */ - public function getJsLanguageCode() - { - $languages = array( - 'cs_CZ' => 'cs', - 'da_DK' => 'da', - 'de_DE' => 'de', - 'en_US' => 'en', - 'es_ES' => 'es', - 'fr_FR' => 'fr', - 'it_IT' => 'it', - 'hu_HU' => 'hu', - 'nl_NL' => 'nl', - 'nb_NO' => 'nb', - 'pl_PL' => 'pl', - 'pt_PT' => 'pt', - 'pt_BR' => 'pt-br', - 'ru_RU' => 'ru', - 'sr_Latn_RS' => 'sr', - 'fi_FI' => 'fi', - 'sv_SE' => 'sv', - 'tr_TR' => 'tr', - 'ko_KR' => 'ko', - 'zh_CN' => 'zh-cn', - 'ja_JP' => 'ja', - 'th_TH' => 'th', - 'id_ID' => 'id', - 'el_GR' => 'el', - ); - - $lang = $this->getCurrentLanguage(); - - return isset($languages[$lang]) ? $languages[$lang] : 'en'; - } - - /** - * Get current language - * - * @access public - * @return string - */ - public function getCurrentLanguage() - { - if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['language'])) { - return $this->sessionStorage->user['language']; - } - - return $this->config->get('application_language', 'en_US'); - } - - /** - * Load translations for the current language - * - * @access public - */ - public function loadCurrentLanguage() - { - Translator::load($this->getCurrentLanguage()); - } -} diff --git a/app/Model/LanguageModel.php b/app/Model/LanguageModel.php new file mode 100644 index 00000000..eb6de004 --- /dev/null +++ b/app/Model/LanguageModel.php @@ -0,0 +1,179 @@ + 'Bahasa Indonesia', + 'bs_BA' => 'Bosanski', + 'cs_CZ' => 'Čeština', + 'da_DK' => 'Dansk', + 'de_DE' => 'Deutsch', + 'en_US' => 'English', + 'es_ES' => 'Español', + 'fr_FR' => 'Français', + 'el_GR' => 'Grec', + 'it_IT' => 'Italiano', + 'hu_HU' => 'Magyar', + 'my_MY' => 'Melayu', + 'nl_NL' => 'Nederlands', + 'nb_NO' => 'Norsk', + 'pl_PL' => 'Polski', + 'pt_PT' => 'Português', + 'pt_BR' => 'Português (Brasil)', + 'ru_RU' => 'Русский', + 'sr_Latn_RS' => 'Srpski', + 'fi_FI' => 'Suomi', + 'sv_SE' => 'Svenska', + 'tr_TR' => 'Türkçe', + 'ko_KR' => '한국어', + 'zh_CN' => '中文(简体)', + 'ja_JP' => '日本語', + 'th_TH' => 'ไทย', + ); + + if ($prepend) { + return array('' => t('Application default')) + $languages; + } + + return $languages; + } + + /** + * Get javascript language code + * + * @access public + * @return string + */ + public function getJsLanguageCode() + { + $languages = array( + 'cs_CZ' => 'cs', + 'da_DK' => 'da', + 'de_DE' => 'de', + 'en_US' => 'en', + 'es_ES' => 'es', + 'fr_FR' => 'fr', + 'it_IT' => 'it', + 'hu_HU' => 'hu', + 'nl_NL' => 'nl', + 'nb_NO' => 'nb', + 'pl_PL' => 'pl', + 'pt_PT' => 'pt', + 'pt_BR' => 'pt-br', + 'ru_RU' => 'ru', + 'sr_Latn_RS' => 'sr', + 'fi_FI' => 'fi', + 'sv_SE' => 'sv', + 'tr_TR' => 'tr', + 'ko_KR' => 'ko', + 'zh_CN' => 'zh-cn', + 'ja_JP' => 'ja', + 'th_TH' => 'th', + 'id_ID' => 'id', + 'el_GR' => 'el', + ); + + $lang = $this->getCurrentLanguage(); + + return isset($languages[$lang]) ? $languages[$lang] : 'en'; + } + + /** + * Get current language + * + * @access public + * @return string + */ + public function getCurrentLanguage() + { + if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['language'])) { + return $this->sessionStorage->user['language']; + } + + return $this->configModel->get('application_language', 'en_US'); + } + + /** + * Load translations for the current language + * + * @access public + */ + public function loadCurrentLanguage() + { + Translator::load($this->getCurrentLanguage()); + } +} diff --git a/app/Model/LastLogin.php b/app/Model/LastLogin.php deleted file mode 100644 index 35c7afc9..00000000 --- a/app/Model/LastLogin.php +++ /dev/null @@ -1,92 +0,0 @@ -cleanup($user_id); - - return $this->db - ->table(self::TABLE) - ->insert(array( - 'auth_type' => $auth_type, - 'user_id' => $user_id, - 'ip' => $ip, - 'user_agent' => substr($user_agent, 0, 255), - 'date_creation' => time(), - )); - } - - /** - * Cleanup login history - * - * @access public - * @param integer $user_id - */ - public function cleanup($user_id) - { - $connections = $this->db - ->table(self::TABLE) - ->eq('user_id', $user_id) - ->desc('id') - ->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(); - } - } - - /** - * 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('id') - ->columns('id', 'auth_type', 'ip', 'user_agent', 'date_creation') - ->findAll(); - } -} diff --git a/app/Model/LastLoginModel.php b/app/Model/LastLoginModel.php new file mode 100644 index 00000000..16821392 --- /dev/null +++ b/app/Model/LastLoginModel.php @@ -0,0 +1,92 @@ +cleanup($user_id); + + return $this->db + ->table(self::TABLE) + ->insert(array( + 'auth_type' => $auth_type, + 'user_id' => $user_id, + 'ip' => $ip, + 'user_agent' => substr($user_agent, 0, 255), + 'date_creation' => time(), + )); + } + + /** + * Cleanup login history + * + * @access public + * @param integer $user_id + */ + public function cleanup($user_id) + { + $connections = $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->desc('id') + ->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(); + } + } + + /** + * 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('id') + ->columns('id', 'auth_type', 'ip', 'user_agent', 'date_creation') + ->findAll(); + } +} diff --git a/app/Model/Link.php b/app/Model/Link.php deleted file mode 100644 index f275edae..00000000 --- a/app/Model/Link.php +++ /dev/null @@ -1,178 +0,0 @@ -db->table(self::TABLE)->eq('id', $link_id)->findOne(); - } - - /** - * Get a link by name - * - * @access public - * @param string $label - * @return array - */ - public function getByLabel($label) - { - return $this->db->table(self::TABLE)->eq('label', $label)->findOne(); - } - - /** - * Get the opposite link id - * - * @access public - * @param integer $link_id Link id - * @return integer - */ - public function getOppositeLinkId($link_id) - { - return $this->db->table(self::TABLE)->eq('id', $link_id)->findOneColumn('opposite_id') ?: $link_id; - } - - /** - * Get all links - * - * @access public - * @return array - */ - public function getAll() - { - return $this->db->table(self::TABLE)->findAll(); - } - - /** - * Get merged links - * - * @access public - * @return array - */ - public function getMergedList() - { - return $this->db - ->execute(' - SELECT - links.id, links.label, opposite.label as opposite_label - FROM links - LEFT JOIN links AS opposite ON opposite.id=links.opposite_id - ') - ->fetchAll(PDO::FETCH_ASSOC); - } - - /** - * Get label list - * - * @access public - * @param integer $exclude_id Exclude this link - * @param boolean $prepend Prepend default value - * @return array - */ - public function getList($exclude_id = 0, $prepend = true) - { - $labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label'); - - foreach ($labels as &$value) { - $value = t($value); - } - - return $prepend ? array('') + $labels : $labels; - } - - /** - * Create a new link label - * - * @access public - * @param string $label - * @param string $opposite_label - * @return boolean|integer - */ - public function create($label, $opposite_label = '') - { - $this->db->startTransaction(); - - if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) { - $this->db->cancelTransaction(); - return false; - } - - $label_id = $this->db->getLastId(); - - if (! empty($opposite_label)) { - $this->db - ->table(self::TABLE) - ->insert(array( - 'label' => $opposite_label, - 'opposite_id' => $label_id, - )); - - $this->db - ->table(self::TABLE) - ->eq('id', $label_id) - ->update(array( - 'opposite_id' => $this->db->getLastId() - )); - } - - $this->db->closeTransaction(); - - return (int) $label_id; - } - - /** - * Update a link - * - * @access public - * @param array $values - * @return boolean - */ - public function update(array $values) - { - return $this->db - ->table(self::TABLE) - ->eq('id', $values['id']) - ->update(array( - 'label' => $values['label'], - 'opposite_id' => $values['opposite_id'], - )); - } - - /** - * Remove a link a the relation to its opposite - * - * @access public - * @param integer $link_id - * @return boolean - */ - public function remove($link_id) - { - $this->db->table(self::TABLE)->eq('opposite_id', $link_id)->update(array('opposite_id' => 0)); - return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); - } -} diff --git a/app/Model/LinkModel.php b/app/Model/LinkModel.php new file mode 100644 index 00000000..b72c7532 --- /dev/null +++ b/app/Model/LinkModel.php @@ -0,0 +1,178 @@ +db->table(self::TABLE)->eq('id', $link_id)->findOne(); + } + + /** + * Get a link by name + * + * @access public + * @param string $label + * @return array + */ + public function getByLabel($label) + { + return $this->db->table(self::TABLE)->eq('label', $label)->findOne(); + } + + /** + * Get the opposite link id + * + * @access public + * @param integer $link_id Link id + * @return integer + */ + public function getOppositeLinkId($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOneColumn('opposite_id') ?: $link_id; + } + + /** + * Get all links + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->findAll(); + } + + /** + * Get merged links + * + * @access public + * @return array + */ + public function getMergedList() + { + return $this->db + ->execute(' + SELECT + links.id, links.label, opposite.label as opposite_label + FROM links + LEFT JOIN links AS opposite ON opposite.id=links.opposite_id + ') + ->fetchAll(PDO::FETCH_ASSOC); + } + + /** + * Get label list + * + * @access public + * @param integer $exclude_id Exclude this link + * @param boolean $prepend Prepend default value + * @return array + */ + public function getList($exclude_id = 0, $prepend = true) + { + $labels = $this->db->hashtable(self::TABLE)->neq('id', $exclude_id)->asc('id')->getAll('id', 'label'); + + foreach ($labels as &$value) { + $value = t($value); + } + + return $prepend ? array('') + $labels : $labels; + } + + /** + * Create a new link label + * + * @access public + * @param string $label + * @param string $opposite_label + * @return boolean|integer + */ + public function create($label, $opposite_label = '') + { + $this->db->startTransaction(); + + if (! $this->db->table(self::TABLE)->insert(array('label' => $label))) { + $this->db->cancelTransaction(); + return false; + } + + $label_id = $this->db->getLastId(); + + if (! empty($opposite_label)) { + $this->db + ->table(self::TABLE) + ->insert(array( + 'label' => $opposite_label, + 'opposite_id' => $label_id, + )); + + $this->db + ->table(self::TABLE) + ->eq('id', $label_id) + ->update(array( + 'opposite_id' => $this->db->getLastId() + )); + } + + $this->db->closeTransaction(); + + return (int) $label_id; + } + + /** + * Update a link + * + * @access public + * @param array $values + * @return boolean + */ + public function update(array $values) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update(array( + 'label' => $values['label'], + 'opposite_id' => $values['opposite_id'], + )); + } + + /** + * Remove a link a the relation to its opposite + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function remove($link_id) + { + $this->db->table(self::TABLE)->eq('opposite_id', $link_id)->update(array('opposite_id' => 0)); + return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); + } +} diff --git a/app/Model/Metadata.php b/app/Model/Metadata.php deleted file mode 100644 index b39ce098..00000000 --- a/app/Model/Metadata.php +++ /dev/null @@ -1,139 +0,0 @@ -db - ->hashtable($this->getTable()) - ->eq($this->getEntityKey(), $entity_id) - ->asc('name') - ->getAll('name', 'value'); - } - - /** - * Get a metadata for the given entity - * - * @access public - * @param integer $entity_id - * @param string $name - * @param string $default - * @return mixed - */ - public function get($entity_id, $name, $default = '') - { - return $this->db - ->table($this->getTable()) - ->eq($this->getEntityKey(), $entity_id) - ->eq('name', $name) - ->findOneColumn('value') ?: $default; - } - - /** - * Return true if a metadata exists - * - * @access public - * @param integer $entity_id - * @param string $name - * @return boolean - */ - public function exists($entity_id, $name) - { - return $this->db - ->table($this->getTable()) - ->eq($this->getEntityKey(), $entity_id) - ->eq('name', $name) - ->exists(); - } - - /** - * Update or insert new metadata - * - * @access public - * @param integer $entity_id - * @param array $values - * @return boolean - */ - public function save($entity_id, array $values) - { - $results = array(); - $user_id = $this->userSession->getId(); - $timestamp = time(); - - $this->db->startTransaction(); - - foreach ($values as $key => $value) { - if ($this->exists($entity_id, $key)) { - $results[] = $this->db->table($this->getTable()) - ->eq($this->getEntityKey(), $entity_id) - ->eq('name', $key)->update(array( - 'value' => $value, - 'changed_on' => $timestamp, - 'changed_by' => $user_id, - )); - } else { - $results[] = $this->db->table($this->getTable())->insert(array( - 'name' => $key, - 'value' => $value, - $this->getEntityKey() => $entity_id, - 'changed_on' => $timestamp, - 'changed_by' => $user_id, - )); - } - } - - $this->db->closeTransaction(); - return ! in_array(false, $results, true); - } - - /** - * Remove a metadata - * - * @access public - * @param integer $entity_id - * @param string $name - * @return bool - */ - public function remove($entity_id, $name) - { - return $this->db->table($this->getTable()) - ->eq($this->getEntityKey(), $entity_id) - ->eq('name', $name) - ->remove(); - } -} diff --git a/app/Model/MetadataModel.php b/app/Model/MetadataModel.php new file mode 100644 index 00000000..6177e5f3 --- /dev/null +++ b/app/Model/MetadataModel.php @@ -0,0 +1,139 @@ +db + ->hashtable($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->asc('name') + ->getAll('name', 'value'); + } + + /** + * Get a metadata for the given entity + * + * @access public + * @param integer $entity_id + * @param string $name + * @param string $default + * @return mixed + */ + public function get($entity_id, $name, $default = '') + { + return $this->db + ->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->findOneColumn('value') ?: $default; + } + + /** + * Return true if a metadata exists + * + * @access public + * @param integer $entity_id + * @param string $name + * @return boolean + */ + public function exists($entity_id, $name) + { + return $this->db + ->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->exists(); + } + + /** + * Update or insert new metadata + * + * @access public + * @param integer $entity_id + * @param array $values + * @return boolean + */ + public function save($entity_id, array $values) + { + $results = array(); + $user_id = $this->userSession->getId(); + $timestamp = time(); + + $this->db->startTransaction(); + + foreach ($values as $key => $value) { + if ($this->exists($entity_id, $key)) { + $results[] = $this->db->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $key)->update(array( + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } else { + $results[] = $this->db->table($this->getTable())->insert(array( + 'name' => $key, + 'value' => $value, + $this->getEntityKey() => $entity_id, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } + } + + $this->db->closeTransaction(); + return ! in_array(false, $results, true); + } + + /** + * Remove a metadata + * + * @access public + * @param integer $entity_id + * @param string $name + * @return bool + */ + public function remove($entity_id, $name) + { + return $this->db->table($this->getTable()) + ->eq($this->getEntityKey(), $entity_id) + ->eq('name', $name) + ->remove(); + } +} diff --git a/app/Model/Notification.php b/app/Model/Notification.php deleted file mode 100644 index 1e96a8b4..00000000 --- a/app/Model/Notification.php +++ /dev/null @@ -1,136 +0,0 @@ - 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']); - case Task::EVENT_USER_MENTION: - return e('You were mentioned in the task #%d', $event_data['task']['id']); - case Comment::EVENT_USER_MENTION: - return e('You were mentioned in a comment on the task #%d', $event_data['task']['id']); - default: - return e('Notification'); - } - } -} diff --git a/app/Model/NotificationModel.php b/app/Model/NotificationModel.php new file mode 100644 index 00000000..8937b77e --- /dev/null +++ b/app/Model/NotificationModel.php @@ -0,0 +1,136 @@ + 1 ? e('%d overdue tasks', $nb) : e('Task #%d is overdue', $event_data['tasks'][0]['id']); + case TaskModel::EVENT_USER_MENTION: + return e('You were mentioned in the task #%d', $event_data['task']['id']); + case CommentModel::EVENT_USER_MENTION: + return e('You were mentioned in a comment on the task #%d', $event_data['task']['id']); + default: + return e('Notification'); + } + } +} diff --git a/app/Model/NotificationType.php b/app/Model/NotificationType.php deleted file mode 100644 index a4dffa09..00000000 --- a/app/Model/NotificationType.php +++ /dev/null @@ -1,128 +0,0 @@ -classes = new Container; - } - - /** - * Add a new notification type - * - * @access public - * @param string $type - * @param string $label - * @param string $class - * @param boolean $hidden - * @return NotificationType - */ - public function setType($type, $label, $class, $hidden = false) - { - $container = $this->container; - - if ($hidden) { - $this->hiddens[] = $type; - } else { - $this->labels[$type] = $label; - } - - $this->classes[$type] = function () use ($class, $container) { - return new $class($container); - }; - - return $this; - } - - /** - * Get mail notification type instance - * - * @access public - * @param string $type - * @return \Kanboard\Notification\NotificationInterface - */ - public function getType($type) - { - return $this->classes[$type]; - } - - /** - * Get all notification types with labels - * - * @access public - * @return array - */ - public function getTypes() - { - return $this->labels; - } - - /** - * Get all hidden notification types - * - * @access public - * @return array - */ - public function getHiddenTypes() - { - return $this->hiddens; - } - - /** - * Keep only loaded notification types - * - * @access public - * @param string[] $types - * @return array - */ - public function filterTypes(array $types) - { - $classes = $this->classes; - - return array_filter($types, function ($type) use ($classes) { - return isset($classes[$type]); - }); - } -} diff --git a/app/Model/NotificationTypeModel.php b/app/Model/NotificationTypeModel.php new file mode 100644 index 00000000..432832ee --- /dev/null +++ b/app/Model/NotificationTypeModel.php @@ -0,0 +1,128 @@ +classes = new Container; + } + + /** + * Add a new notification type + * + * @access public + * @param string $type + * @param string $label + * @param string $class + * @param boolean $hidden + * @return NotificationTypeModel + */ + public function setType($type, $label, $class, $hidden = false) + { + $container = $this->container; + + if ($hidden) { + $this->hiddens[] = $type; + } else { + $this->labels[$type] = $label; + } + + $this->classes[$type] = function () use ($class, $container) { + return new $class($container); + }; + + return $this; + } + + /** + * Get mail notification type instance + * + * @access public + * @param string $type + * @return \Kanboard\Core\Notification\NotificationInterface + */ + public function getType($type) + { + return $this->classes[$type]; + } + + /** + * Get all notification types with labels + * + * @access public + * @return array + */ + public function getTypes() + { + return $this->labels; + } + + /** + * Get all hidden notification types + * + * @access public + * @return array + */ + public function getHiddenTypes() + { + return $this->hiddens; + } + + /** + * Keep only loaded notification types + * + * @access public + * @param string[] $types + * @return array + */ + public function filterTypes(array $types) + { + $classes = $this->classes; + + return array_filter($types, function ($type) use ($classes) { + return isset($classes[$type]); + }); + } +} diff --git a/app/Model/PasswordReset.php b/app/Model/PasswordReset.php deleted file mode 100644 index 895acb07..00000000 --- a/app/Model/PasswordReset.php +++ /dev/null @@ -1,95 +0,0 @@ -db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_creation')->limit(100)->findAll(); - } - - /** - * Generate a new reset token for a user - * - * @access public - * @param string $username - * @param integer $expiration - * @return boolean|string - */ - public function create($username, $expiration = 0) - { - $user_id = $this->db->table(User::TABLE)->eq('username', $username)->neq('email', '')->notNull('email')->findOneColumn('id'); - - if (! $user_id) { - return false; - } - - $token = $this->token->getToken(); - - $result = $this->db->table(self::TABLE)->insert(array( - 'token' => $token, - 'user_id' => $user_id, - 'date_expiration' => $expiration ?: time() + self::DURATION, - 'date_creation' => time(), - 'ip' => $this->request->getIpAddress(), - 'user_agent' => $this->request->getUserAgent(), - 'is_active' => 1, - )); - - return $result ? $token : false; - } - - /** - * Get user id from the token - * - * @access public - * @param string $token - * @return integer - */ - public function getUserIdByToken($token) - { - return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_active', 1)->gte('date_expiration', time())->findOneColumn('user_id'); - } - - /** - * Disable all tokens for a user - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function disable($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->update(array('is_active' => 0)); - } -} diff --git a/app/Model/PasswordResetModel.php b/app/Model/PasswordResetModel.php new file mode 100644 index 00000000..d7c74969 --- /dev/null +++ b/app/Model/PasswordResetModel.php @@ -0,0 +1,95 @@ +db->table(self::TABLE)->eq('user_id', $user_id)->desc('date_creation')->limit(100)->findAll(); + } + + /** + * Generate a new reset token for a user + * + * @access public + * @param string $username + * @param integer $expiration + * @return boolean|string + */ + public function create($username, $expiration = 0) + { + $user_id = $this->db->table(UserModel::TABLE)->eq('username', $username)->neq('email', '')->notNull('email')->findOneColumn('id'); + + if (! $user_id) { + return false; + } + + $token = $this->token->getToken(); + + $result = $this->db->table(self::TABLE)->insert(array( + 'token' => $token, + 'user_id' => $user_id, + 'date_expiration' => $expiration ?: time() + self::DURATION, + 'date_creation' => time(), + 'ip' => $this->request->getIpAddress(), + 'user_agent' => $this->request->getUserAgent(), + 'is_active' => 1, + )); + + return $result ? $token : false; + } + + /** + * Get user id from the token + * + * @access public + * @param string $token + * @return integer + */ + public function getUserIdByToken($token) + { + return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_active', 1)->gte('date_expiration', time())->findOneColumn('user_id'); + } + + /** + * Disable all tokens for a user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disable($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->update(array('is_active' => 0)); + } +} diff --git a/app/Model/Project.php b/app/Model/Project.php deleted file mode 100644 index 2cb0a431..00000000 --- a/app/Model/Project.php +++ /dev/null @@ -1,526 +0,0 @@ -db->table(self::TABLE)->eq('id', $project_id)->findOne(); - } - - /** - * Get a project by id with owner name - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getByIdWithOwner($project_id) - { - return $this->db->table(self::TABLE) - ->columns(self::TABLE.'.*', User::TABLE.'.username AS owner_username', User::TABLE.'.name AS owner_name') - ->eq(self::TABLE.'.id', $project_id) - ->join(User::TABLE, 'id', 'owner_id') - ->findOne(); - } - - /** - * Get a project by the name - * - * @access public - * @param string $name Project name - * @return array - */ - public function getByName($name) - { - return $this->db->table(self::TABLE)->eq('name', $name)->findOne(); - } - - /** - * Get a project by the identifier (code) - * - * @access public - * @param string $identifier - * @return array|boolean - */ - public function getByIdentifier($identifier) - { - if (empty($identifier)) { - return false; - } - - return $this->db->table(self::TABLE)->eq('identifier', strtoupper($identifier))->findOne(); - } - - /** - * Fetch project data by using the token - * - * @access public - * @param string $token Token - * @return array|boolean - */ - public function getByToken($token) - { - if (empty($token)) { - return false; - } - - return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_public', 1)->findOne(); - } - - /** - * Return the first project from the database (no sorting) - * - * @access public - * @return array - */ - public function getFirst() - { - return $this->db->table(self::TABLE)->findOne(); - } - - /** - * Return true if the project is private - * - * @access public - * @param integer $project_id Project id - * @return boolean - */ - public function isPrivate($project_id) - { - return $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->exists(); - } - - /** - * Get all projects - * - * @access public - * @return array - */ - public function getAll() - { - return $this->db->table(self::TABLE)->asc('name')->findAll(); - } - - /** - * Get all projects with given Ids - * - * @access public - * @param integer[] $project_ids - * @return array - */ - public function getAllByIds(array $project_ids) - { - if (empty($project_ids)) { - return array(); - } - - return $this->db->table(self::TABLE)->in('id', $project_ids)->asc('name')->findAll(); - } - - /** - * Get all project ids - * - * @access public - * @return array - */ - public function getAllIds() - { - return $this->db->table(self::TABLE)->asc('name')->findAllByColumn('id'); - } - - /** - * 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->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); - } - - return $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); - } - - /** - * Get all projects with all its data for a given status - * - * @access public - * @param integer $status Project 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 Project status: self::ACTIVE or self:INACTIVE - * @return array - */ - public function getListByStatus($status) - { - return $this->db - ->hashtable(self::TABLE) - ->asc('name') - ->eq('is_active', $status) - ->getAll('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(); - } - - /** - * Get Priority range from a project - * - * @access public - * @param array $project - * @return array - */ - public function getPriorities(array $project) - { - $range = range($project['priority_start'], $project['priority_end']); - return array_combine($range, $range); - } - - /** - * Gather some task metrics for a given project - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getTaskStats($project_id) - { - $stats = array(); - $stats['nb_active_tasks'] = 0; - $columns = $this->column->getAll($project_id); - $column_stats = $this->board->getColumnStats($project_id); - - foreach ($columns as &$column) { - $column['nb_active_tasks'] = isset($column_stats[$column['id']]) ? $column_stats[$column['id']] : 0; - $stats['nb_active_tasks'] += $column['nb_active_tasks']; - } - - $stats['columns'] = $columns; - $stats['nb_tasks'] = $this->taskFinder->countByProjectId($project_id); - $stats['nb_inactive_tasks'] = $stats['nb_tasks'] - $stats['nb_active_tasks']; - - return $stats; - } - - /** - * Get stats for each column of a project - * - * @access public - * @param array $project - * @return array - */ - public function getColumnStats(array &$project) - { - $project['columns'] = $this->column->getAll($project['id']); - $stats = $this->board->getColumnStats($project['id']); - - foreach ($project['columns'] as &$column) { - $column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0; - } - - return $project; - } - - /** - * Apply column stats to a collection of projects (filter callback) - * - * @access public - * @param array $projects - * @return array - */ - public function applyColumnStats(array $projects) - { - foreach ($projects as &$project) { - $this->getColumnStats($project); - } - - return $projects; - } - - /** - * Get project summary for a list of project - * - * @access public - * @param array $project_ids List of project id - * @return \PicoDb\Table - */ - public function getQueryColumnStats(array $project_ids) - { - if (empty($project_ids)) { - return $this->db->table(Project::TABLE)->limit(0); - } - - return $this->db - ->table(Project::TABLE) - ->columns(self::TABLE.'.*', User::TABLE.'.username AS owner_username', User::TABLE.'.name AS owner_name') - ->join(User::TABLE, 'id', 'owner_id') - ->in(self::TABLE.'.id', $project_ids) - ->callback(array($this, 'applyColumnStats')); - } - - /** - * Create a project - * - * @access public - * @param array $values Form values - * @param integer $user_id User who create the project - * @param bool $add_user Automatically add the user - * @return integer Project id - */ - public function create(array $values, $user_id = 0, $add_user = false) - { - $this->db->startTransaction(); - - $values['token'] = ''; - $values['last_modified'] = time(); - $values['is_private'] = empty($values['is_private']) ? 0 : 1; - $values['owner_id'] = $user_id; - - if (! empty($values['identifier'])) { - $values['identifier'] = strtoupper($values['identifier']); - } - - $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end')); - - if (! $this->db->table(self::TABLE)->save($values)) { - $this->db->cancelTransaction(); - return false; - } - - $project_id = $this->db->getLastId(); - - if (! $this->board->create($project_id, $this->board->getUserColumns())) { - $this->db->cancelTransaction(); - return false; - } - - if ($add_user && $user_id) { - $this->projectUserRole->addUser($project_id, $user_id, Role::PROJECT_MANAGER); - } - - $this->category->createDefaultCategories($project_id); - - $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)->update(array( - 'last_modified' => time() - )); - } - - /** - * Update a project - * - * @access public - * @param array $values Form values - * @return bool - */ - public function update(array $values) - { - if (! empty($values['identifier'])) { - $values['identifier'] = strtoupper($values['identifier']); - } - - $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end')); - - return $this->exists($values['id']) && - $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(); - } - - /** - * Return true if the project exists - * - * @access public - * @param integer $project_id Project id - * @return boolean - */ - public function exists($project_id) - { - return $this->db->table(self::TABLE)->eq('id', $project_id)->exists(); - } - - /** - * Enable a project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function enable($project_id) - { - return $this->exists($project_id) && - $this->db - ->table(self::TABLE) - ->eq('id', $project_id) - ->update(array('is_active' => 1)); - } - - /** - * Disable a project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function disable($project_id) - { - return $this->exists($project_id) && - $this->db - ->table(self::TABLE) - ->eq('id', $project_id) - ->update(array('is_active' => 0)); - } - - /** - * Enable public access for a project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function enablePublicAccess($project_id) - { - return $this->exists($project_id) && - $this->db - ->table(self::TABLE) - ->eq('id', $project_id) - ->save(array('is_public' => 1, 'token' => Token::getToken())); - } - - /** - * Disable public access for a project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function disablePublicAccess($project_id) - { - return $this->exists($project_id) && - $this->db - ->table(self::TABLE) - ->eq('id', $project_id) - ->save(array('is_public' => 0, 'token' => '')); - } -} diff --git a/app/Model/ProjectActivity.php b/app/Model/ProjectActivity.php deleted file mode 100644 index f6bdbf92..00000000 --- a/app/Model/ProjectActivity.php +++ /dev/null @@ -1,94 +0,0 @@ - $project_id, - 'task_id' => $task_id, - 'creator_id' => $creator_id, - 'event_name' => $event_name, - 'date_creation' => time(), - 'data' => json_encode($data), - ); - - $this->cleanup(self::MAX_EVENTS - 1); - return $this->db->table(self::TABLE)->insert($values); - } - - /** - * Get query - * - * @access public - * @return Table - */ - public function getQuery() - { - return $this - ->db - ->table(ProjectActivity::TABLE) - ->columns( - ProjectActivity::TABLE.'.*', - 'uc.username AS author_username', - 'uc.name AS author_name', - 'uc.email', - 'uc.avatar_path' - ) - ->join(Task::TABLE, 'id', 'task_id') - ->join(Project::TABLE, 'id', 'project_id') - ->left(User::TABLE, 'uc', 'id', ProjectActivity::TABLE, 'creator_id'); - } - - /** - * Remove old event entries to avoid large table - * - * @access public - * @param integer $max Maximum number of items to keep in the table - */ - public function cleanup($max) - { - $total = $this->db->table(self::TABLE)->count(); - - if ($total > $max) { - $ids = $this->db->table(self::TABLE)->asc('id')->limit($total - $max)->findAllByColumn('id'); - $this->db->table(self::TABLE)->in('id', $ids)->remove(); - } - } -} diff --git a/app/Model/ProjectActivityModel.php b/app/Model/ProjectActivityModel.php new file mode 100644 index 00000000..380ea125 --- /dev/null +++ b/app/Model/ProjectActivityModel.php @@ -0,0 +1,94 @@ + $project_id, + 'task_id' => $task_id, + 'creator_id' => $creator_id, + 'event_name' => $event_name, + 'date_creation' => time(), + 'data' => json_encode($data), + ); + + $this->cleanup(self::MAX_EVENTS - 1); + return $this->db->table(self::TABLE)->insert($values); + } + + /** + * Get query + * + * @access public + * @return Table + */ + public function getQuery() + { + return $this + ->db + ->table(ProjectActivityModel::TABLE) + ->columns( + ProjectActivityModel::TABLE.'.*', + 'uc.username AS author_username', + 'uc.name AS author_name', + 'uc.email', + 'uc.avatar_path' + ) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->left(UserModel::TABLE, 'uc', 'id', ProjectActivityModel::TABLE, 'creator_id'); + } + + /** + * Remove old event entries to avoid large table + * + * @access public + * @param integer $max Maximum number of items to keep in the table + */ + public function cleanup($max) + { + $total = $this->db->table(self::TABLE)->count(); + + if ($total > $max) { + $ids = $this->db->table(self::TABLE)->asc('id')->limit($total - $max)->findAllByColumn('id'); + $this->db->table(self::TABLE)->in('id', $ids)->remove(); + } + } +} diff --git a/app/Model/ProjectDailyColumnStats.php b/app/Model/ProjectDailyColumnStats.php deleted file mode 100644 index 38487c76..00000000 --- a/app/Model/ProjectDailyColumnStats.php +++ /dev/null @@ -1,254 +0,0 @@ -db->startTransaction(); - $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('day', $date)->remove(); - - foreach ($this->getStatsByColumns($project_id) as $column_id => $column) { - $this->db->table(self::TABLE)->insert(array( - 'day' => $date, - 'project_id' => $project_id, - 'column_id' => $column_id, - 'total' => $column['total'], - 'score' => $column['score'], - )); - } - - $this->db->closeTransaction(); - - return true; - } - - /** - * Count the number of recorded days for the data range - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return integer - */ - public function countDays($project_id, $from, $to) - { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->findOneColumn('COUNT(DISTINCT day)'); - } - - /** - * Get aggregated metrics for the project within a data range - * - * [ - * ['Date', 'Column1', 'Column2'], - * ['2014-11-16', 2, 5], - * ['2014-11-17', 20, 15], - * ] - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @param string $field Column to aggregate - * @return array - */ - public function getAggregatedMetrics($project_id, $from, $to, $field = 'total') - { - $columns = $this->column->getList($project_id); - $metrics = $this->getMetrics($project_id, $from, $to); - return $this->buildAggregate($metrics, $columns, $field); - } - - /** - * Fetch metrics - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return array - */ - public function getMetrics($project_id, $from, $to) - { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->asc(self::TABLE.'.day') - ->findAll(); - } - - /** - * Build aggregate - * - * @access private - * @param array $metrics - * @param array $columns - * @param string $field - * @return array - */ - private function buildAggregate(array &$metrics, array &$columns, $field) - { - $column_ids = array_keys($columns); - $days = array_unique(array_column($metrics, 'day')); - $rows = array(array_merge(array(e('Date')), array_values($columns))); - - foreach ($days as $day) { - $rows[] = $this->buildRowAggregate($metrics, $column_ids, $day, $field); - } - - return $rows; - } - - /** - * Build one row of the aggregate - * - * @access private - * @param array $metrics - * @param array $column_ids - * @param string $day - * @param string $field - * @return array - */ - private function buildRowAggregate(array &$metrics, array &$column_ids, $day, $field) - { - $row = array($day); - - foreach ($column_ids as $column_id) { - $row[] = $this->findValueInMetrics($metrics, $day, $column_id, $field); - } - - return $row; - } - - /** - * Find the value in the metrics - * - * @access private - * @param array $metrics - * @param string $day - * @param string $column_id - * @param string $field - * @return integer - */ - private function findValueInMetrics(array &$metrics, $day, $column_id, $field) - { - foreach ($metrics as $metric) { - if ($metric['day'] === $day && $metric['column_id'] == $column_id) { - return (int) $metric[$field]; - } - } - - return 0; - } - - /** - * Get number of tasks and score by columns - * - * @access private - * @param integer $project_id - * @return array - */ - private function getStatsByColumns($project_id) - { - $totals = $this->getTotalByColumns($project_id); - $scores = $this->getScoreByColumns($project_id); - $columns = array(); - - foreach ($totals as $column_id => $total) { - $columns[$column_id] = array('total' => $total, 'score' => 0); - } - - foreach ($scores as $column_id => $score) { - $columns[$column_id]['score'] = (int) $score; - } - - return $columns; - } - - /** - * Get number of tasks and score by columns - * - * @access private - * @param integer $project_id - * @return array - */ - private function getScoreByColumns($project_id) - { - $stats = $this->db->table(Task::TABLE) - ->columns('column_id', 'SUM(score) AS score') - ->eq('project_id', $project_id) - ->eq('is_active', Task::STATUS_OPEN) - ->notNull('score') - ->groupBy('column_id') - ->findAll(); - - return array_column($stats, 'score', 'column_id'); - } - - /** - * Get number of tasks and score by columns - * - * @access private - * @param integer $project_id - * @return array - */ - private function getTotalByColumns($project_id) - { - $stats = $this->db->table(Task::TABLE) - ->columns('column_id', 'COUNT(*) AS total') - ->eq('project_id', $project_id) - ->in('is_active', $this->getTaskStatusConfig()) - ->groupBy('column_id') - ->findAll(); - - return array_column($stats, 'total', 'column_id'); - } - - /** - * Get task status to use for total calculation - * - * @access private - * @return array - */ - private function getTaskStatusConfig() - { - if ($this->config->get('cfd_include_closed_tasks') == 1) { - return array(Task::STATUS_OPEN, Task::STATUS_CLOSED); - } - - return array(Task::STATUS_OPEN); - } -} diff --git a/app/Model/ProjectDailyColumnStatsModel.php b/app/Model/ProjectDailyColumnStatsModel.php new file mode 100644 index 00000000..a0f14cf4 --- /dev/null +++ b/app/Model/ProjectDailyColumnStatsModel.php @@ -0,0 +1,254 @@ +db->startTransaction(); + $this->db->table(self::TABLE)->eq('project_id', $project_id)->eq('day', $date)->remove(); + + foreach ($this->getStatsByColumns($project_id) as $column_id => $column) { + $this->db->table(self::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'column_id' => $column_id, + 'total' => $column['total'], + 'score' => $column['score'], + )); + } + + $this->db->closeTransaction(); + + return true; + } + + /** + * Count the number of recorded days for the data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return integer + */ + public function countDays($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->findOneColumn('COUNT(DISTINCT day)'); + } + + /** + * Get aggregated metrics for the project within a data range + * + * [ + * ['Date', 'Column1', 'Column2'], + * ['2014-11-16', 2, 5], + * ['2014-11-17', 20, 15], + * ] + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @param string $field Column to aggregate + * @return array + */ + public function getAggregatedMetrics($project_id, $from, $to, $field = 'total') + { + $columns = $this->columnModel->getList($project_id); + $metrics = $this->getMetrics($project_id, $from, $to); + return $this->buildAggregate($metrics, $columns, $field); + } + + /** + * Fetch metrics + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getMetrics($project_id, $from, $to) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc(self::TABLE.'.day') + ->findAll(); + } + + /** + * Build aggregate + * + * @access private + * @param array $metrics + * @param array $columns + * @param string $field + * @return array + */ + private function buildAggregate(array &$metrics, array &$columns, $field) + { + $column_ids = array_keys($columns); + $days = array_unique(array_column($metrics, 'day')); + $rows = array(array_merge(array(e('Date')), array_values($columns))); + + foreach ($days as $day) { + $rows[] = $this->buildRowAggregate($metrics, $column_ids, $day, $field); + } + + return $rows; + } + + /** + * Build one row of the aggregate + * + * @access private + * @param array $metrics + * @param array $column_ids + * @param string $day + * @param string $field + * @return array + */ + private function buildRowAggregate(array &$metrics, array &$column_ids, $day, $field) + { + $row = array($day); + + foreach ($column_ids as $column_id) { + $row[] = $this->findValueInMetrics($metrics, $day, $column_id, $field); + } + + return $row; + } + + /** + * Find the value in the metrics + * + * @access private + * @param array $metrics + * @param string $day + * @param string $column_id + * @param string $field + * @return integer + */ + private function findValueInMetrics(array &$metrics, $day, $column_id, $field) + { + foreach ($metrics as $metric) { + if ($metric['day'] === $day && $metric['column_id'] == $column_id) { + return (int) $metric[$field]; + } + } + + return 0; + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getStatsByColumns($project_id) + { + $totals = $this->getTotalByColumns($project_id); + $scores = $this->getScoreByColumns($project_id); + $columns = array(); + + foreach ($totals as $column_id => $total) { + $columns[$column_id] = array('total' => $total, 'score' => 0); + } + + foreach ($scores as $column_id => $score) { + $columns[$column_id]['score'] = (int) $score; + } + + return $columns; + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getScoreByColumns($project_id) + { + $stats = $this->db->table(TaskModel::TABLE) + ->columns('column_id', 'SUM(score) AS score') + ->eq('project_id', $project_id) + ->eq('is_active', TaskModel::STATUS_OPEN) + ->notNull('score') + ->groupBy('column_id') + ->findAll(); + + return array_column($stats, 'score', 'column_id'); + } + + /** + * Get number of tasks and score by columns + * + * @access private + * @param integer $project_id + * @return array + */ + private function getTotalByColumns($project_id) + { + $stats = $this->db->table(TaskModel::TABLE) + ->columns('column_id', 'COUNT(*) AS total') + ->eq('project_id', $project_id) + ->in('is_active', $this->getTaskStatusConfig()) + ->groupBy('column_id') + ->findAll(); + + return array_column($stats, 'total', 'column_id'); + } + + /** + * Get task status to use for total calculation + * + * @access private + * @return array + */ + private function getTaskStatusConfig() + { + if ($this->configModel->get('cfd_include_closed_tasks') == 1) { + return array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED); + } + + return array(TaskModel::STATUS_OPEN); + } +} diff --git a/app/Model/ProjectDailyStats.php b/app/Model/ProjectDailyStats.php deleted file mode 100644 index 08a0039e..00000000 --- a/app/Model/ProjectDailyStats.php +++ /dev/null @@ -1,76 +0,0 @@ -db->startTransaction(); - - $lead_cycle_time = $this->averageLeadCycleTimeAnalytic->build($project_id); - - $this->db->table(self::TABLE)->eq('day', $date)->eq('project_id', $project_id)->remove(); - - $this->db->table(self::TABLE)->insert(array( - 'day' => $date, - 'project_id' => $project_id, - 'avg_lead_time' => $lead_cycle_time['avg_lead_time'], - 'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'], - )); - - $this->db->closeTransaction(); - - return true; - } - - /** - * Get raw metrics for the project within a data range - * - * @access public - * @param integer $project_id Project id - * @param string $from Start date (ISO format YYYY-MM-DD) - * @param string $to End date - * @return array - */ - public function getRawMetrics($project_id, $from, $to) - { - $metrics = $this->db->table(self::TABLE) - ->columns('day', 'avg_lead_time', 'avg_cycle_time') - ->eq('project_id', $project_id) - ->gte('day', $from) - ->lte('day', $to) - ->asc('day') - ->findAll(); - - foreach ($metrics as &$metric) { - $metric['avg_lead_time'] = (int) $metric['avg_lead_time']; - $metric['avg_cycle_time'] = (int) $metric['avg_cycle_time']; - } - - return $metrics; - } -} diff --git a/app/Model/ProjectDailyStatsModel.php b/app/Model/ProjectDailyStatsModel.php new file mode 100644 index 00000000..0754d263 --- /dev/null +++ b/app/Model/ProjectDailyStatsModel.php @@ -0,0 +1,76 @@ +db->startTransaction(); + + $lead_cycle_time = $this->averageLeadCycleTimeAnalytic->build($project_id); + + $this->db->table(self::TABLE)->eq('day', $date)->eq('project_id', $project_id)->remove(); + + $this->db->table(self::TABLE)->insert(array( + 'day' => $date, + 'project_id' => $project_id, + 'avg_lead_time' => $lead_cycle_time['avg_lead_time'], + 'avg_cycle_time' => $lead_cycle_time['avg_cycle_time'], + )); + + $this->db->closeTransaction(); + + return true; + } + + /** + * Get raw metrics for the project within a data range + * + * @access public + * @param integer $project_id Project id + * @param string $from Start date (ISO format YYYY-MM-DD) + * @param string $to End date + * @return array + */ + public function getRawMetrics($project_id, $from, $to) + { + $metrics = $this->db->table(self::TABLE) + ->columns('day', 'avg_lead_time', 'avg_cycle_time') + ->eq('project_id', $project_id) + ->gte('day', $from) + ->lte('day', $to) + ->asc('day') + ->findAll(); + + foreach ($metrics as &$metric) { + $metric['avg_lead_time'] = (int) $metric['avg_lead_time']; + $metric['avg_cycle_time'] = (int) $metric['avg_cycle_time']; + } + + return $metrics; + } +} diff --git a/app/Model/ProjectDuplication.php b/app/Model/ProjectDuplication.php deleted file mode 100644 index 871cadc8..00000000 --- a/app/Model/ProjectDuplication.php +++ /dev/null @@ -1,161 +0,0 @@ - $max_length) { - $name = substr($name, 0, $max_length - strlen($suffix)); - } - - return $name.$suffix; - } - - /** - * Clone a project with all settings - * - * @param integer $src_project_id Project Id - * @param array $selection Selection of optional project parts to duplicate - * @param integer $owner_id Owner of the project - * @param string $name Name of the project - * @param boolean $private Force the project to be private - * @return integer Cloned Project Id - */ - public function duplicate($src_project_id, $selection = array('projectPermission', 'category', 'action'), $owner_id = 0, $name = null, $private = null) - { - $this->db->startTransaction(); - - // Get the cloned project Id - $dst_project_id = $this->copy($src_project_id, $owner_id, $name, $private); - - if ($dst_project_id === false) { - $this->db->cancelTransaction(); - return false; - } - - // Clone Columns, Categories, Permissions and Actions - foreach ($this->getPossibleSelection() as $model) { - - // Skip if optional part has not been selected - if (in_array($model, $this->getOptionalSelection()) && ! in_array($model, $selection)) { - continue; - } - - // Skip permissions for private projects - if ($private && $model === 'projectPermission') { - continue; - } - - if (! $this->$model->duplicate($src_project_id, $dst_project_id)) { - $this->db->cancelTransaction(); - return false; - } - } - - if (! $this->makeOwnerManager($dst_project_id, $owner_id)) { - $this->db->cancelTransaction(); - return false; - } - - $this->db->closeTransaction(); - - return (int) $dst_project_id; - } - - /** - * Create a project from another one - * - * @access private - * @param integer $src_project_id - * @param integer $owner_id - * @param string $name - * @param boolean $private - * @return integer - */ - private function copy($src_project_id, $owner_id = 0, $name = null, $private = null) - { - $project = $this->project->getById($src_project_id); - $is_private = empty($project['is_private']) ? 0 : 1; - - $values = array( - 'name' => $name ?: $this->getClonedProjectName($project['name']), - 'is_active' => 1, - 'last_modified' => time(), - 'token' => '', - 'is_public' => 0, - 'is_private' => $private ? 1 : $is_private, - 'owner_id' => $owner_id, - ); - - if (! $this->db->table(Project::TABLE)->save($values)) { - return false; - } - - return $this->db->getLastId(); - } - - /** - * Make sure that the creator of the duplicated project is alsp owner - * - * @access private - * @param integer $dst_project_id - * @param integer $owner_id - * @return boolean - */ - private function makeOwnerManager($dst_project_id, $owner_id) - { - if ($owner_id > 0) { - $this->projectUserRole->removeUser($dst_project_id, $owner_id); - - if (! $this->projectUserRole->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) { - return false; - } - } - - return true; - } -} diff --git a/app/Model/ProjectDuplicationModel.php b/app/Model/ProjectDuplicationModel.php new file mode 100644 index 00000000..b67f8302 --- /dev/null +++ b/app/Model/ProjectDuplicationModel.php @@ -0,0 +1,161 @@ + $max_length) { + $name = substr($name, 0, $max_length - strlen($suffix)); + } + + return $name.$suffix; + } + + /** + * Clone a project with all settings + * + * @param integer $src_project_id Project Id + * @param array $selection Selection of optional project parts to duplicate + * @param integer $owner_id Owner of the project + * @param string $name Name of the project + * @param boolean $private Force the project to be private + * @return integer Cloned Project Id + */ + public function duplicate($src_project_id, $selection = array('projectPermissionModel', 'categoryModel', 'actionModel'), $owner_id = 0, $name = null, $private = null) + { + $this->db->startTransaction(); + + // Get the cloned project Id + $dst_project_id = $this->copy($src_project_id, $owner_id, $name, $private); + + if ($dst_project_id === false) { + $this->db->cancelTransaction(); + return false; + } + + // Clone Columns, Categories, Permissions and Actions + foreach ($this->getPossibleSelection() as $model) { + + // Skip if optional part has not been selected + if (in_array($model, $this->getOptionalSelection()) && ! in_array($model, $selection)) { + continue; + } + + // Skip permissions for private projects + if ($private && $model === 'projectPermissionModel') { + continue; + } + + if (! $this->$model->duplicate($src_project_id, $dst_project_id)) { + $this->db->cancelTransaction(); + return false; + } + } + + if (! $this->makeOwnerManager($dst_project_id, $owner_id)) { + $this->db->cancelTransaction(); + return false; + } + + $this->db->closeTransaction(); + + return (int) $dst_project_id; + } + + /** + * Create a project from another one + * + * @access private + * @param integer $src_project_id + * @param integer $owner_id + * @param string $name + * @param boolean $private + * @return integer + */ + private function copy($src_project_id, $owner_id = 0, $name = null, $private = null) + { + $project = $this->projectModel->getById($src_project_id); + $is_private = empty($project['is_private']) ? 0 : 1; + + $values = array( + 'name' => $name ?: $this->getClonedProjectName($project['name']), + 'is_active' => 1, + 'last_modified' => time(), + 'token' => '', + 'is_public' => 0, + 'is_private' => $private ? 1 : $is_private, + 'owner_id' => $owner_id, + ); + + if (! $this->db->table(ProjectModel::TABLE)->save($values)) { + return false; + } + + return $this->db->getLastId(); + } + + /** + * Make sure that the creator of the duplicated project is alsp owner + * + * @access private + * @param integer $dst_project_id + * @param integer $owner_id + * @return boolean + */ + private function makeOwnerManager($dst_project_id, $owner_id) + { + if ($owner_id > 0) { + $this->projectUserRoleModel->removeUser($dst_project_id, $owner_id); + + if (! $this->projectUserRoleModel->addUser($dst_project_id, $owner_id, Role::PROJECT_MANAGER)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectFile.php b/app/Model/ProjectFile.php deleted file mode 100644 index 75348e0c..00000000 --- a/app/Model/ProjectFile.php +++ /dev/null @@ -1,42 +0,0 @@ -db - ->hashtable(Project::TABLE) - ->join(self::TABLE, 'project_id', 'id') - ->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE) - ->eq(GroupMember::TABLE.'.user_id', $user_id) - ->in(Project::TABLE.'.is_active', $status) - ->getAll(Project::TABLE.'.id', Project::TABLE.'.name'); - } - - /** - * For a given project get the role of the specified user - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @return string - */ - public function getUserRole($project_id, $user_id) - { - $roles = $this->db->table(self::TABLE) - ->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE) - ->eq(GroupMember::TABLE.'.user_id', $user_id) - ->eq(self::TABLE.'.project_id', $project_id) - ->findAllByColumn('role'); - - return $this->projectAccessMap->getHighestRole($roles); - } - - /** - * Get all groups associated directly to the project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getGroups($project_id) - { - return $this->db->table(self::TABLE) - ->columns(Group::TABLE.'.id', Group::TABLE.'.name', self::TABLE.'.role') - ->join(Group::TABLE, 'id', 'group_id') - ->eq('project_id', $project_id) - ->asc('name') - ->findAll(); - } - - /** - * From groups get all users associated to the project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getUsers($project_id) - { - return $this->db->table(self::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', self::TABLE.'.role') - ->join(GroupMember::TABLE, 'group_id', 'group_id', self::TABLE) - ->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE) - ->eq(self::TABLE.'.project_id', $project_id) - ->asc(User::TABLE.'.username') - ->findAll(); - } - - /** - * From groups get all users assignable to tasks - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAssignableUsers($project_id) - { - return $this->db->table(User::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name') - ->join(GroupMember::TABLE, 'user_id', 'id', User::TABLE) - ->join(self::TABLE, 'group_id', 'group_id', GroupMember::TABLE) - ->eq(self::TABLE.'.project_id', $project_id) - ->eq(User::TABLE.'.is_active', 1) - ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) - ->asc(User::TABLE.'.username') - ->findAll(); - } - - /** - * Add a group to the project - * - * @access public - * @param integer $project_id - * @param integer $group_id - * @param string $role - * @return boolean - */ - public function addGroup($project_id, $group_id, $role) - { - return $this->db->table(self::TABLE)->insert(array( - 'group_id' => $group_id, - 'project_id' => $project_id, - 'role' => $role, - )); - } - - /** - * Remove a group from the project - * - * @access public - * @param integer $project_id - * @param integer $group_id - * @return boolean - */ - public function removeGroup($project_id, $group_id) - { - return $this->db->table(self::TABLE)->eq('group_id', $group_id)->eq('project_id', $project_id)->remove(); - } - - /** - * Change a group role for the project - * - * @access public - * @param integer $project_id - * @param integer $group_id - * @param string $role - * @return boolean - */ - public function changeGroupRole($project_id, $group_id, $role) - { - return $this->db->table(self::TABLE) - ->eq('group_id', $group_id) - ->eq('project_id', $project_id) - ->update(array( - 'role' => $role, - )); - } - - /** - * Copy group access from a project to another one - * - * @param integer $project_src_id Project Template - * @param integer $project_dst_id Project that receives the copy - * @return boolean - */ - public function duplicate($project_src_id, $project_dst_id) - { - $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); - - foreach ($rows as $row) { - $result = $this->db->table(self::TABLE)->save(array( - 'project_id' => $project_dst_id, - 'group_id' => $row['group_id'], - 'role' => $row['role'], - )); - - if (! $result) { - return false; - } - } - - return true; - } -} diff --git a/app/Model/ProjectGroupRoleModel.php b/app/Model/ProjectGroupRoleModel.php new file mode 100644 index 00000000..2729d5a6 --- /dev/null +++ b/app/Model/ProjectGroupRoleModel.php @@ -0,0 +1,191 @@ +db + ->hashtable(ProjectModel::TABLE) + ->join(self::TABLE, 'project_id', 'id') + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) + ->in(ProjectModel::TABLE.'.is_active', $status) + ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); + } + + /** + * For a given project get the role of the specified user + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return string + */ + public function getUserRole($project_id, $user_id) + { + $roles = $this->db->table(self::TABLE) + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->eq(GroupMemberModel::TABLE.'.user_id', $user_id) + ->eq(self::TABLE.'.project_id', $project_id) + ->findAllByColumn('role'); + + return $this->projectAccessMap->getHighestRole($roles); + } + + /** + * Get all groups associated directly to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getGroups($project_id) + { + return $this->db->table(self::TABLE) + ->columns(GroupModel::TABLE.'.id', GroupModel::TABLE.'.name', self::TABLE.'.role') + ->join(GroupModel::TABLE, 'id', 'group_id') + ->eq('project_id', $project_id) + ->asc('name') + ->findAll(); + } + + /** + * From groups get all users associated to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getUsers($project_id) + { + return $this->db->table(self::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', self::TABLE.'.role') + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', self::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) + ->eq(self::TABLE.'.project_id', $project_id) + ->asc(UserModel::TABLE.'.username') + ->findAll(); + } + + /** + * From groups get all users assignable to tasks + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAssignableUsers($project_id) + { + return $this->db->table(UserModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->join(GroupMemberModel::TABLE, 'user_id', 'id', UserModel::TABLE) + ->join(self::TABLE, 'group_id', 'group_id', GroupMemberModel::TABLE) + ->eq(self::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.is_active', 1) + ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) + ->asc(UserModel::TABLE.'.username') + ->findAll(); + } + + /** + * Add a group to the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @param string $role + * @return boolean + */ + public function addGroup($project_id, $group_id, $role) + { + return $this->db->table(self::TABLE)->insert(array( + 'group_id' => $group_id, + 'project_id' => $project_id, + 'role' => $role, + )); + } + + /** + * Remove a group from the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @return boolean + */ + public function removeGroup($project_id, $group_id) + { + return $this->db->table(self::TABLE)->eq('group_id', $group_id)->eq('project_id', $project_id)->remove(); + } + + /** + * Change a group role for the project + * + * @access public + * @param integer $project_id + * @param integer $group_id + * @param string $role + * @return boolean + */ + public function changeGroupRole($project_id, $group_id, $role) + { + return $this->db->table(self::TABLE) + ->eq('group_id', $group_id) + ->eq('project_id', $project_id) + ->update(array( + 'role' => $role, + )); + } + + /** + * Copy group access from a project to another one + * + * @param integer $project_src_id Project Template + * @param integer $project_dst_id Project that receives the copy + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); + + foreach ($rows as $row) { + $result = $this->db->table(self::TABLE)->save(array( + 'project_id' => $project_dst_id, + 'group_id' => $row['group_id'], + 'role' => $row['role'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/ProjectMetadata.php b/app/Model/ProjectMetadata.php deleted file mode 100644 index c8e4fe63..00000000 --- a/app/Model/ProjectMetadata.php +++ /dev/null @@ -1,56 +0,0 @@ -getAll($src_project_id); - - if (! $this->save($dst_project_id, $metadata)) { - return false; - } - - return true; - } -} diff --git a/app/Model/ProjectMetadataModel.php b/app/Model/ProjectMetadataModel.php new file mode 100644 index 00000000..760acd7d --- /dev/null +++ b/app/Model/ProjectMetadataModel.php @@ -0,0 +1,54 @@ +getAll($src_project_id); + + if (! $this->save($dst_project_id, $metadata)) { + return false; + } + + return true; + } +} diff --git a/app/Model/ProjectModel.php b/app/Model/ProjectModel.php new file mode 100644 index 00000000..34e11c13 --- /dev/null +++ b/app/Model/ProjectModel.php @@ -0,0 +1,526 @@ +db->table(self::TABLE)->eq('id', $project_id)->findOne(); + } + + /** + * Get a project by id with owner name + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getByIdWithOwner($project_id) + { + return $this->db->table(self::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->eq(self::TABLE.'.id', $project_id) + ->join(UserModel::TABLE, 'id', 'owner_id') + ->findOne(); + } + + /** + * Get a project by the name + * + * @access public + * @param string $name Project name + * @return array + */ + public function getByName($name) + { + return $this->db->table(self::TABLE)->eq('name', $name)->findOne(); + } + + /** + * Get a project by the identifier (code) + * + * @access public + * @param string $identifier + * @return array|boolean + */ + public function getByIdentifier($identifier) + { + if (empty($identifier)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('identifier', strtoupper($identifier))->findOne(); + } + + /** + * Fetch project data by using the token + * + * @access public + * @param string $token Token + * @return array|boolean + */ + public function getByToken($token) + { + if (empty($token)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('token', $token)->eq('is_public', 1)->findOne(); + } + + /** + * Return the first project from the database (no sorting) + * + * @access public + * @return array + */ + public function getFirst() + { + return $this->db->table(self::TABLE)->findOne(); + } + + /** + * Return true if the project is private + * + * @access public + * @param integer $project_id Project id + * @return boolean + */ + public function isPrivate($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->exists(); + } + + /** + * Get all projects + * + * @access public + * @return array + */ + public function getAll() + { + return $this->db->table(self::TABLE)->asc('name')->findAll(); + } + + /** + * Get all projects with given Ids + * + * @access public + * @param integer[] $project_ids + * @return array + */ + public function getAllByIds(array $project_ids) + { + if (empty($project_ids)) { + return array(); + } + + return $this->db->table(self::TABLE)->in('id', $project_ids)->asc('name')->findAll(); + } + + /** + * Get all project ids + * + * @access public + * @return array + */ + public function getAllIds() + { + return $this->db->table(self::TABLE)->asc('name')->findAllByColumn('id'); + } + + /** + * 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->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); + } + + return $this->db->hashtable(self::TABLE)->asc('name')->getAll('id', 'name'); + } + + /** + * Get all projects with all its data for a given status + * + * @access public + * @param integer $status Project 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 Project status: self::ACTIVE or self:INACTIVE + * @return array + */ + public function getListByStatus($status) + { + return $this->db + ->hashtable(self::TABLE) + ->asc('name') + ->eq('is_active', $status) + ->getAll('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(); + } + + /** + * Get Priority range from a project + * + * @access public + * @param array $project + * @return array + */ + public function getPriorities(array $project) + { + $range = range($project['priority_start'], $project['priority_end']); + return array_combine($range, $range); + } + + /** + * Gather some task metrics for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getTaskStats($project_id) + { + $stats = array(); + $stats['nb_active_tasks'] = 0; + $columns = $this->columnModel->getAll($project_id); + $column_stats = $this->boardModel->getColumnStats($project_id); + + foreach ($columns as &$column) { + $column['nb_active_tasks'] = isset($column_stats[$column['id']]) ? $column_stats[$column['id']] : 0; + $stats['nb_active_tasks'] += $column['nb_active_tasks']; + } + + $stats['columns'] = $columns; + $stats['nb_tasks'] = $this->taskFinderModel->countByProjectId($project_id); + $stats['nb_inactive_tasks'] = $stats['nb_tasks'] - $stats['nb_active_tasks']; + + return $stats; + } + + /** + * Get stats for each column of a project + * + * @access public + * @param array $project + * @return array + */ + public function getColumnStats(array &$project) + { + $project['columns'] = $this->columnModel->getAll($project['id']); + $stats = $this->boardModel->getColumnStats($project['id']); + + foreach ($project['columns'] as &$column) { + $column['nb_tasks'] = isset($stats[$column['id']]) ? $stats[$column['id']] : 0; + } + + return $project; + } + + /** + * Apply column stats to a collection of projects (filter callback) + * + * @access public + * @param array $projects + * @return array + */ + public function applyColumnStats(array $projects) + { + foreach ($projects as &$project) { + $this->getColumnStats($project); + } + + return $projects; + } + + /** + * Get project summary for a list of project + * + * @access public + * @param array $project_ids List of project id + * @return \PicoDb\Table + */ + public function getQueryColumnStats(array $project_ids) + { + if (empty($project_ids)) { + return $this->db->table(ProjectModel::TABLE)->limit(0); + } + + return $this->db + ->table(ProjectModel::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username AS owner_username', UserModel::TABLE.'.name AS owner_name') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->in(self::TABLE.'.id', $project_ids) + ->callback(array($this, 'applyColumnStats')); + } + + /** + * Create a project + * + * @access public + * @param array $values Form values + * @param integer $user_id User who create the project + * @param bool $add_user Automatically add the user + * @return integer Project id + */ + public function create(array $values, $user_id = 0, $add_user = false) + { + $this->db->startTransaction(); + + $values['token'] = ''; + $values['last_modified'] = time(); + $values['is_private'] = empty($values['is_private']) ? 0 : 1; + $values['owner_id'] = $user_id; + + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end')); + + if (! $this->db->table(self::TABLE)->save($values)) { + $this->db->cancelTransaction(); + return false; + } + + $project_id = $this->db->getLastId(); + + if (! $this->boardModel->create($project_id, $this->boardModel->getUserColumns())) { + $this->db->cancelTransaction(); + return false; + } + + if ($add_user && $user_id) { + $this->projectUserRoleModel->addUser($project_id, $user_id, Role::PROJECT_MANAGER); + } + + $this->categoryModel->createDefaultCategories($project_id); + + $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)->update(array( + 'last_modified' => time() + )); + } + + /** + * Update a project + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + if (! empty($values['identifier'])) { + $values['identifier'] = strtoupper($values['identifier']); + } + + $this->helper->model->convertIntegerFields($values, array('priority_default', 'priority_start', 'priority_end')); + + return $this->exists($values['id']) && + $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(); + } + + /** + * Return true if the project exists + * + * @access public + * @param integer $project_id Project id + * @return boolean + */ + public function exists($project_id) + { + return $this->db->table(self::TABLE)->eq('id', $project_id)->exists(); + } + + /** + * Enable a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function enable($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->update(array('is_active' => 1)); + } + + /** + * Disable a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function disable($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->update(array('is_active' => 0)); + } + + /** + * Enable public access for a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function enablePublicAccess($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->save(array('is_public' => 1, 'token' => Token::getToken())); + } + + /** + * Disable public access for a project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function disablePublicAccess($project_id) + { + return $this->exists($project_id) && + $this->db + ->table(self::TABLE) + ->eq('id', $project_id) + ->save(array('is_public' => 0, 'token' => '')); + } +} diff --git a/app/Model/ProjectNotification.php b/app/Model/ProjectNotification.php deleted file mode 100644 index 5df042ca..00000000 --- a/app/Model/ProjectNotification.php +++ /dev/null @@ -1,67 +0,0 @@ -project->getById($project_id); - - $types = array_merge( - $this->projectNotificationType->getHiddenTypes(), - $this->projectNotificationType->getSelectedTypes($project_id) - ); - - foreach ($types as $type) { - $this->projectNotificationType->getType($type)->notifyProject($project, $event_name, $event_data); - } - } - - /** - * Save settings for the given project - * - * @access public - * @param integer $project_id - * @param array $values - */ - public function saveSettings($project_id, array $values) - { - $this->db->startTransaction(); - - $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); - $this->projectNotificationType->saveSelectedTypes($project_id, $types); - - $this->db->closeTransaction(); - } - - /** - * Read user settings to display the form - * - * @access public - * @param integer $project_id - * @return array - */ - public function readSettings($project_id) - { - return array( - 'notification_types' => $this->projectNotificationType->getSelectedTypes($project_id), - ); - } -} diff --git a/app/Model/ProjectNotificationModel.php b/app/Model/ProjectNotificationModel.php new file mode 100644 index 00000000..aeeee4cd --- /dev/null +++ b/app/Model/ProjectNotificationModel.php @@ -0,0 +1,67 @@ +projectModel->getById($project_id); + + $types = array_merge( + $this->projectNotificationTypeModel->getHiddenTypes(), + $this->projectNotificationTypeModel->getSelectedTypes($project_id) + ); + + foreach ($types as $type) { + $this->projectNotificationTypeModel->getType($type)->notifyProject($project, $event_name, $event_data); + } + } + + /** + * Save settings for the given project + * + * @access public + * @param integer $project_id + * @param array $values + */ + public function saveSettings($project_id, array $values) + { + $this->db->startTransaction(); + + $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); + $this->projectNotificationTypeModel->saveSelectedTypes($project_id, $types); + + $this->db->closeTransaction(); + } + + /** + * Read user settings to display the form + * + * @access public + * @param integer $project_id + * @return array + */ + public function readSettings($project_id) + { + return array( + 'notification_types' => $this->projectNotificationTypeModel->getSelectedTypes($project_id), + ); + } +} diff --git a/app/Model/ProjectNotificationType.php b/app/Model/ProjectNotificationType.php deleted file mode 100644 index a00e465c..00000000 --- a/app/Model/ProjectNotificationType.php +++ /dev/null @@ -1,59 +0,0 @@ -db - ->table(self::TABLE) - ->eq('project_id', $project_id) - ->asc('notification_type') - ->findAllByColumn('notification_type'); - - return $this->filterTypes($types); - } - - /** - * Save notification types for a given project - * - * @access public - * @param integer $project_id - * @param string[] $types - * @return boolean - */ - public function saveSelectedTypes($project_id, array $types) - { - $results = array(); - $this->db->table(self::TABLE)->eq('project_id', $project_id)->remove(); - - foreach ($types as $type) { - $results[] = $this->db->table(self::TABLE)->insert(array('project_id' => $project_id, 'notification_type' => $type)); - } - - return ! in_array(false, $results, true); - } -} diff --git a/app/Model/ProjectNotificationTypeModel.php b/app/Model/ProjectNotificationTypeModel.php new file mode 100644 index 00000000..aeec77f2 --- /dev/null +++ b/app/Model/ProjectNotificationTypeModel.php @@ -0,0 +1,57 @@ +db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->asc('notification_type') + ->findAllByColumn('notification_type'); + + return $this->filterTypes($types); + } + + /** + * Save notification types for a given project + * + * @access public + * @param integer $project_id + * @param string[] $types + * @return boolean + */ + public function saveSelectedTypes($project_id, array $types) + { + $results = array(); + $this->db->table(self::TABLE)->eq('project_id', $project_id)->remove(); + + foreach ($types as $type) { + $results[] = $this->db->table(self::TABLE)->insert(array('project_id' => $project_id, 'notification_type' => $type)); + } + + return ! in_array(false, $results, true); + } +} diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php deleted file mode 100644 index bc35c956..00000000 --- a/app/Model/ProjectPermission.php +++ /dev/null @@ -1,166 +0,0 @@ -db - ->table(ProjectUserRole::TABLE) - ->join(User::TABLE, 'id', 'user_id') - ->join(Project::TABLE, 'id', 'project_id') - ->eq(ProjectUserRole::TABLE.'.role', $role) - ->eq(Project::TABLE.'.is_private', 0) - ->in(Project::TABLE.'.id', $project_ids) - ->columns( - User::TABLE.'.id', - User::TABLE.'.username', - User::TABLE.'.name', - Project::TABLE.'.name AS project_name', - Project::TABLE.'.id' - ); - } - - /** - * Get all usernames (fetch users from groups) - * - * @access public - * @param integer $project_id - * @param string $input - * @return array - */ - public function findUsernames($project_id, $input) - { - $userMembers = $this->projectUserRoleQuery - ->withFilter(new ProjectUserRoleProjectFilter($project_id)) - ->withFilter(new ProjectUserRoleUsernameFilter($input)) - ->getQuery() - ->findAllByColumn('username'); - - $groupMembers = $this->projectGroupRoleQuery - ->withFilter(new ProjectGroupRoleProjectFilter($project_id)) - ->withFilter(new ProjectGroupRoleUsernameFilter($input)) - ->getQuery() - ->findAllByColumn('username'); - - $members = array_unique(array_merge($userMembers, $groupMembers)); - - sort($members); - - return $members; - } - - /** - * Return true if everybody is allowed for the project - * - * @access public - * @param integer $project_id Project id - * @return bool - */ - public function isEverybodyAllowed($project_id) - { - return $this->db - ->table(Project::TABLE) - ->eq('id', $project_id) - ->eq('is_everybody_allowed', 1) - ->exists(); - } - - /** - * Return true if the user is allowed to access a project - * - * @param integer $project_id - * @param integer $user_id - * @return boolean - */ - public function isUserAllowed($project_id, $user_id) - { - if ($this->userSession->isAdmin()) { - return true; - } - - return in_array( - $this->projectUserRole->getUserRole($project_id, $user_id), - array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER, Role::PROJECT_VIEWER) - ); - } - - /** - * Return true if the user is assignable - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @return boolean - */ - public function isAssignable($project_id, $user_id) - { - return $this->user->isActive($user_id) && - in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER)); - } - - /** - * Return true if the user is member - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @return boolean - */ - public function isMember($project_id, $user_id) - { - return in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER, Role::PROJECT_VIEWER)); - } - - /** - * Get active project ids by user - * - * @access public - * @param integer $user_id - * @return array - */ - public function getActiveProjectIds($user_id) - { - return array_keys($this->projectUserRole->getActiveProjectsByUser($user_id)); - } - - /** - * Copy permissions to another project - * - * @param integer $project_src_id Project Template - * @param integer $project_dst_id Project that receives the copy - * @return boolean - */ - public function duplicate($project_src_id, $project_dst_id) - { - return $this->projectUserRole->duplicate($project_src_id, $project_dst_id) && - $this->projectGroupRole->duplicate($project_src_id, $project_dst_id); - } -} diff --git a/app/Model/ProjectPermissionModel.php b/app/Model/ProjectPermissionModel.php new file mode 100644 index 00000000..a7c1857c --- /dev/null +++ b/app/Model/ProjectPermissionModel.php @@ -0,0 +1,166 @@ +db + ->table(ProjectUserRoleModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->eq(ProjectUserRoleModel::TABLE.'.role', $role) + ->eq(ProjectModel::TABLE.'.is_private', 0) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->columns( + UserModel::TABLE.'.id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name', + ProjectModel::TABLE.'.name AS project_name', + ProjectModel::TABLE.'.id' + ); + } + + /** + * Get all usernames (fetch users from groups) + * + * @access public + * @param integer $project_id + * @param string $input + * @return array + */ + public function findUsernames($project_id, $input) + { + $userMembers = $this->projectUserRoleQuery + ->withFilter(new ProjectUserRoleProjectFilter($project_id)) + ->withFilter(new ProjectUserRoleUsernameFilter($input)) + ->getQuery() + ->findAllByColumn('username'); + + $groupMembers = $this->projectGroupRoleQuery + ->withFilter(new ProjectGroupRoleProjectFilter($project_id)) + ->withFilter(new ProjectGroupRoleUsernameFilter($input)) + ->getQuery() + ->findAllByColumn('username'); + + $members = array_unique(array_merge($userMembers, $groupMembers)); + + sort($members); + + return $members; + } + + /** + * Return true if everybody is allowed for the project + * + * @access public + * @param integer $project_id Project id + * @return bool + */ + public function isEverybodyAllowed($project_id) + { + return $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->eq('is_everybody_allowed', 1) + ->exists(); + } + + /** + * Return true if the user is allowed to access a project + * + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isUserAllowed($project_id, $user_id) + { + if ($this->userSession->isAdmin()) { + return true; + } + + return in_array( + $this->projectUserRoleModel->getUserRole($project_id, $user_id), + array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER, Role::PROJECT_VIEWER) + ); + } + + /** + * Return true if the user is assignable + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isAssignable($project_id, $user_id) + { + return $this->userModel->isActive($user_id) && + in_array($this->projectUserRoleModel->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER)); + } + + /** + * Return true if the user is member + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function isMember($project_id, $user_id) + { + return in_array($this->projectUserRoleModel->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER, Role::PROJECT_VIEWER)); + } + + /** + * Get active project ids by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getActiveProjectIds($user_id) + { + return array_keys($this->projectUserRoleModel->getActiveProjectsByUser($user_id)); + } + + /** + * Copy permissions to another project + * + * @param integer $project_src_id Project Template + * @param integer $project_dst_id Project that receives the copy + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + return $this->projectUserRoleModel->duplicate($project_src_id, $project_dst_id) && + $this->projectGroupRoleModel->duplicate($project_src_id, $project_dst_id); + } +} diff --git a/app/Model/ProjectUserRole.php b/app/Model/ProjectUserRole.php deleted file mode 100644 index 05f96c6e..00000000 --- a/app/Model/ProjectUserRole.php +++ /dev/null @@ -1,282 +0,0 @@ -getProjectsByUser($user_id, array(Project::ACTIVE)); - } - - /** - * Get the list of project visible for the given user - * - * @access public - * @param integer $user_id - * @param array $status - * @return array - */ - public function getProjectsByUser($user_id, $status = array(Project::ACTIVE, Project::INACTIVE)) - { - $userProjects = $this->db - ->hashtable(Project::TABLE) - ->beginOr() - ->eq(self::TABLE.'.user_id', $user_id) - ->eq(Project::TABLE.'.is_everybody_allowed', 1) - ->closeOr() - ->in(Project::TABLE.'.is_active', $status) - ->join(self::TABLE, 'project_id', 'id') - ->getAll(Project::TABLE.'.id', Project::TABLE.'.name'); - - $groupProjects = $this->projectGroupRole->getProjectsByUser($user_id, $status); - $projects = $userProjects + $groupProjects; - - asort($projects); - - return $projects; - } - - /** - * For a given project get the role of the specified user - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @return string - */ - public function getUserRole($project_id, $user_id) - { - $projectInfo = $this->db->table(Project::TABLE) - ->eq('id', $project_id) - ->columns('owner_id', 'is_everybody_allowed') - ->findOne(); - - if ($projectInfo['is_everybody_allowed'] == 1) { - return $projectInfo['owner_id'] == $user_id ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER; - } - - $role = $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->findOneColumn('role'); - - if (empty($role)) { - $role = $this->projectGroupRole->getUserRole($project_id, $user_id); - } - - return $role; - } - - /** - * Get all users associated directly to the project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getUsers($project_id) - { - return $this->db->table(self::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', self::TABLE.'.role') - ->join(User::TABLE, 'id', 'user_id') - ->eq('project_id', $project_id) - ->asc(User::TABLE.'.username') - ->asc(User::TABLE.'.name') - ->findAll(); - } - - /** - * Get all users (fetch users from groups) - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAllUsers($project_id) - { - $userMembers = $this->getUsers($project_id); - $groupMembers = $this->projectGroupRole->getUsers($project_id); - $members = array_merge($userMembers, $groupMembers); - - return $this->user->prepareList($members); - } - - /** - * Get users grouped by role - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getAllUsersGroupedByRole($project_id) - { - $users = array(); - - $userMembers = $this->getUsers($project_id); - $groupMembers = $this->projectGroupRole->getUsers($project_id); - $members = array_merge($userMembers, $groupMembers); - - foreach ($members as $user) { - if (! isset($users[$user['role']])) { - $users[$user['role']] = array(); - } - - $users[$user['role']][$user['id']] = $user['name'] ?: $user['username']; - } - - return $users; - } - - /** - * Get list of users that can be assigned to a task (only Manager and Member) - * - * @access public - * @param integer $project_id - * @return array - */ - public function getAssignableUsers($project_id) - { - if ($this->projectPermission->isEverybodyAllowed($project_id)) { - return $this->user->getActiveUsersList(); - } - - $userMembers = $this->db->table(self::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name') - ->join(User::TABLE, 'id', 'user_id') - ->eq(User::TABLE.'.is_active', 1) - ->eq(self::TABLE.'.project_id', $project_id) - ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) - ->findAll(); - - $groupMembers = $this->projectGroupRole->getAssignableUsers($project_id); - $members = array_merge($userMembers, $groupMembers); - - return $this->user->prepareList($members); - } - - /** - * Get list of users that can be assigned to a task (only Manager and Member) - * - * @access public - * @param integer $project_id Project id - * @param bool $unassigned Prepend the 'Unassigned' value - * @param bool $everybody Prepend the 'Everbody' value - * @param bool $singleUser If there is only one user return only this user - * @return array - */ - public function getAssignableUsersList($project_id, $unassigned = true, $everybody = false, $singleUser = false) - { - $users = $this->getAssignableUsers($project_id); - - if ($singleUser && count($users) === 1) { - return $users; - } - - if ($unassigned) { - $users = array(t('Unassigned')) + $users; - } - - if ($everybody) { - $users = array(User::EVERYBODY_ID => t('Everybody')) + $users; - } - - return $users; - } - - /** - * Add a user to the project - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @param string $role - * @return boolean - */ - public function addUser($project_id, $user_id, $role) - { - return $this->db->table(self::TABLE)->insert(array( - 'user_id' => $user_id, - 'project_id' => $project_id, - 'role' => $role, - )); - } - - /** - * Remove a user from the project - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @return boolean - */ - public function removeUser($project_id, $user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->remove(); - } - - /** - * Change a user role for the project - * - * @access public - * @param integer $project_id - * @param integer $user_id - * @param string $role - * @return boolean - */ - public function changeUserRole($project_id, $user_id, $role) - { - return $this->db->table(self::TABLE) - ->eq('user_id', $user_id) - ->eq('project_id', $project_id) - ->update(array( - 'role' => $role, - )); - } - - /** - * Copy user access from a project to another one - * - * @param integer $project_src_id - * @param integer $project_dst_id - * @return boolean - */ - public function duplicate($project_src_id, $project_dst_id) - { - $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); - - foreach ($rows as $row) { - $result = $this->db->table(self::TABLE)->save(array( - 'project_id' => $project_dst_id, - 'user_id' => $row['user_id'], - 'role' => $row['role'], - )); - - if (! $result) { - return false; - } - } - - return true; - } -} diff --git a/app/Model/ProjectUserRoleModel.php b/app/Model/ProjectUserRoleModel.php new file mode 100644 index 00000000..a0df0cfa --- /dev/null +++ b/app/Model/ProjectUserRoleModel.php @@ -0,0 +1,282 @@ +getProjectsByUser($user_id, array(ProjectModel::ACTIVE)); + } + + /** + * Get the list of project visible for the given user + * + * @access public + * @param integer $user_id + * @param array $status + * @return array + */ + public function getProjectsByUser($user_id, $status = array(ProjectModel::ACTIVE, ProjectModel::INACTIVE)) + { + $userProjects = $this->db + ->hashtable(ProjectModel::TABLE) + ->beginOr() + ->eq(self::TABLE.'.user_id', $user_id) + ->eq(ProjectModel::TABLE.'.is_everybody_allowed', 1) + ->closeOr() + ->in(ProjectModel::TABLE.'.is_active', $status) + ->join(self::TABLE, 'project_id', 'id') + ->getAll(ProjectModel::TABLE.'.id', ProjectModel::TABLE.'.name'); + + $groupProjects = $this->projectGroupRoleModel->getProjectsByUser($user_id, $status); + $projects = $userProjects + $groupProjects; + + asort($projects); + + return $projects; + } + + /** + * For a given project get the role of the specified user + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return string + */ + public function getUserRole($project_id, $user_id) + { + $projectInfo = $this->db->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->columns('owner_id', 'is_everybody_allowed') + ->findOne(); + + if ($projectInfo['is_everybody_allowed'] == 1) { + return $projectInfo['owner_id'] == $user_id ? Role::PROJECT_MANAGER : Role::PROJECT_MEMBER; + } + + $role = $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->findOneColumn('role'); + + if (empty($role)) { + $role = $this->projectGroupRoleModel->getUserRole($project_id, $user_id); + } + + return $role; + } + + /** + * Get all users associated directly to the project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getUsers($project_id) + { + return $this->db->table(self::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', self::TABLE.'.role') + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq('project_id', $project_id) + ->asc(UserModel::TABLE.'.username') + ->asc(UserModel::TABLE.'.name') + ->findAll(); + } + + /** + * Get all users (fetch users from groups) + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAllUsers($project_id) + { + $userMembers = $this->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + return $this->userModel->prepareList($members); + } + + /** + * Get users grouped by role + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAllUsersGroupedByRole($project_id) + { + $users = array(); + + $userMembers = $this->getUsers($project_id); + $groupMembers = $this->projectGroupRoleModel->getUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + foreach ($members as $user) { + if (! isset($users[$user['role']])) { + $users[$user['role']] = array(); + } + + $users[$user['role']][$user['id']] = $user['name'] ?: $user['username']; + } + + return $users; + } + + /** + * Get list of users that can be assigned to a task (only Manager and Member) + * + * @access public + * @param integer $project_id + * @return array + */ + public function getAssignableUsers($project_id) + { + if ($this->projectPermissionModel->isEverybodyAllowed($project_id)) { + return $this->userModel->getActiveUsersList(); + } + + $userMembers = $this->db->table(self::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq(UserModel::TABLE.'.is_active', 1) + ->eq(self::TABLE.'.project_id', $project_id) + ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) + ->findAll(); + + $groupMembers = $this->projectGroupRoleModel->getAssignableUsers($project_id); + $members = array_merge($userMembers, $groupMembers); + + return $this->userModel->prepareList($members); + } + + /** + * Get list of users that can be assigned to a task (only Manager and Member) + * + * @access public + * @param integer $project_id Project id + * @param bool $unassigned Prepend the 'Unassigned' value + * @param bool $everybody Prepend the 'Everbody' value + * @param bool $singleUser If there is only one user return only this user + * @return array + */ + public function getAssignableUsersList($project_id, $unassigned = true, $everybody = false, $singleUser = false) + { + $users = $this->getAssignableUsers($project_id); + + if ($singleUser && count($users) === 1) { + return $users; + } + + if ($unassigned) { + $users = array(t('Unassigned')) + $users; + } + + if ($everybody) { + $users = array(UserModel::EVERYBODY_ID => t('Everybody')) + $users; + } + + return $users; + } + + /** + * Add a user to the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @param string $role + * @return boolean + */ + public function addUser($project_id, $user_id, $role) + { + return $this->db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $project_id, + 'role' => $role, + )); + } + + /** + * Remove a user from the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @return boolean + */ + public function removeUser($project_id, $user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->eq('project_id', $project_id)->remove(); + } + + /** + * Change a user role for the project + * + * @access public + * @param integer $project_id + * @param integer $user_id + * @param string $role + * @return boolean + */ + public function changeUserRole($project_id, $user_id, $role) + { + return $this->db->table(self::TABLE) + ->eq('user_id', $user_id) + ->eq('project_id', $project_id) + ->update(array( + 'role' => $role, + )); + } + + /** + * Copy user access from a project to another one + * + * @param integer $project_src_id + * @param integer $project_dst_id + * @return boolean + */ + public function duplicate($project_src_id, $project_dst_id) + { + $rows = $this->db->table(self::TABLE)->eq('project_id', $project_src_id)->findAll(); + + foreach ($rows as $row) { + $result = $this->db->table(self::TABLE)->save(array( + 'project_id' => $project_dst_id, + 'user_id' => $row['user_id'], + 'role' => $row['role'], + )); + + if (! $result) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/RememberMeSession.php b/app/Model/RememberMeSession.php deleted file mode 100644 index e3f2f132..00000000 --- a/app/Model/RememberMeSession.php +++ /dev/null @@ -1,152 +0,0 @@ -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(); - } - - /** - * 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.Token::getToken()); - $sequence = Token::getToken(); - $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 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 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 - * @return string - */ - public function updateSequence($token) - { - $sequence = Token::getToken(); - - $this - ->db - ->table(self::TABLE) - ->eq('token', $token) - ->update(array('sequence' => $sequence)); - - return $sequence; - } -} diff --git a/app/Model/RememberMeSessionModel.php b/app/Model/RememberMeSessionModel.php new file mode 100644 index 00000000..f6c8d648 --- /dev/null +++ b/app/Model/RememberMeSessionModel.php @@ -0,0 +1,152 @@ +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(); + } + + /** + * 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.Token::getToken()); + $sequence = Token::getToken(); + $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 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 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 + * @return string + */ + public function updateSequence($token) + { + $sequence = Token::getToken(); + + $this + ->db + ->table(self::TABLE) + ->eq('token', $token) + ->update(array('sequence' => $sequence)); + + return $sequence; + } +} diff --git a/app/Model/Setting.php b/app/Model/Setting.php deleted file mode 100644 index 38af22e0..00000000 --- a/app/Model/Setting.php +++ /dev/null @@ -1,113 +0,0 @@ -db->hashtable(self::TABLE)->getAll('option', 'value'); - } - - /** - * Get a setting value - * - * @access public - * @param string $name - * @param string $default - * @return mixed - */ - public function getOption($name, $default = '') - { - $value = $this->db - ->table(self::TABLE) - ->eq('option', $name) - ->findOneColumn('value'); - - return $value === null || $value === false || $value === '' ? $default : $value; - } - - /** - * Return true if a setting exists - * - * @access public - * @param string $name - * @return boolean - */ - public function exists($name) - { - return $this->db - ->table(self::TABLE) - ->eq('option', $name) - ->exists(); - } - - /** - * Update or insert new settings - * - * @access public - * @param array $values - * @return boolean - */ - public function save(array $values) - { - $results = array(); - $values = $this->prepare($values); - $user_id = $this->userSession->getId(); - $timestamp = time(); - - $this->db->startTransaction(); - - foreach ($values as $option => $value) { - if ($this->exists($option)) { - $results[] = $this->db->table(self::TABLE)->eq('option', $option)->update(array( - 'value' => $value, - 'changed_on' => $timestamp, - 'changed_by' => $user_id, - )); - } else { - $results[] = $this->db->table(self::TABLE)->insert(array( - 'option' => $option, - 'value' => $value, - 'changed_on' => $timestamp, - 'changed_by' => $user_id, - )); - } - } - - $this->db->closeTransaction(); - - return ! in_array(false, $results, true); - } -} diff --git a/app/Model/SettingModel.php b/app/Model/SettingModel.php new file mode 100644 index 00000000..5b2ee54f --- /dev/null +++ b/app/Model/SettingModel.php @@ -0,0 +1,113 @@ +db->hashtable(self::TABLE)->getAll('option', 'value'); + } + + /** + * Get a setting value + * + * @access public + * @param string $name + * @param string $default + * @return mixed + */ + public function getOption($name, $default = '') + { + $value = $this->db + ->table(self::TABLE) + ->eq('option', $name) + ->findOneColumn('value'); + + return $value === null || $value === false || $value === '' ? $default : $value; + } + + /** + * Return true if a setting exists + * + * @access public + * @param string $name + * @return boolean + */ + public function exists($name) + { + return $this->db + ->table(self::TABLE) + ->eq('option', $name) + ->exists(); + } + + /** + * Update or insert new settings + * + * @access public + * @param array $values + * @return boolean + */ + public function save(array $values) + { + $results = array(); + $values = $this->prepare($values); + $user_id = $this->userSession->getId(); + $timestamp = time(); + + $this->db->startTransaction(); + + foreach ($values as $option => $value) { + if ($this->exists($option)) { + $results[] = $this->db->table(self::TABLE)->eq('option', $option)->update(array( + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } else { + $results[] = $this->db->table(self::TABLE)->insert(array( + 'option' => $option, + 'value' => $value, + 'changed_on' => $timestamp, + 'changed_by' => $user_id, + )); + } + } + + $this->db->closeTransaction(); + + return ! in_array(false, $results, true); + } +} diff --git a/app/Model/Subtask.php b/app/Model/Subtask.php deleted file mode 100644 index 1d53d6de..00000000 --- a/app/Model/Subtask.php +++ /dev/null @@ -1,428 +0,0 @@ - t('Todo'), - self::STATUS_INPROGRESS => t('In progress'), - self::STATUS_DONE => t('Done'), - ); - } - - /** - * Add subtask status status to the resultset - * - * @access public - * @param array $subtasks Subtasks - * @return array - */ - public function addStatusName(array $subtasks) - { - $status = $this->getStatusList(); - - foreach ($subtasks as &$subtask) { - $subtask['status_name'] = $status[$subtask['status']]; - $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0; - $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']); - } - - return $subtasks; - } - - /** - * Get the query to fetch subtasks assigned to a user - * - * @access public - * @param integer $user_id User id - * @param array $status List of status - * @return \PicoDb\Table - */ - public function getUserQuery($user_id, array $status) - { - return $this->db->table(Subtask::TABLE) - ->columns( - Subtask::TABLE.'.*', - Task::TABLE.'.project_id', - Task::TABLE.'.color_id', - Task::TABLE.'.title AS task_name', - Project::TABLE.'.name AS project_name' - ) - ->subquery($this->subtaskTimeTracking->getTimerQuery($user_id), 'timer_start_date') - ->eq('user_id', $user_id) - ->eq(Project::TABLE.'.is_active', Project::ACTIVE) - ->in(Subtask::TABLE.'.status', $status) - ->join(Task::TABLE, 'id', 'task_id') - ->join(Project::TABLE, 'id', 'project_id', Task::TABLE) - ->callback(array($this, 'addStatusName')); - } - - /** - * Get all subtasks 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) - ->eq('task_id', $task_id) - ->columns( - self::TABLE.'.*', - User::TABLE.'.username', - User::TABLE.'.name' - ) - ->subquery($this->subtaskTimeTracking->getTimerQuery($this->userSession->getId()), 'timer_start_date') - ->join(User::TABLE, 'id', 'user_id') - ->asc(self::TABLE.'.position') - ->callback(array($this, 'addStatusName')) - ->findAll(); - } - - /** - * Get a subtask by the id - * - * @access public - * @param integer $subtask_id Subtask id - * @param bool $more Fetch more data - * @return array - */ - public function getById($subtask_id, $more = false) - { - if ($more) { - return $this->db - ->table(self::TABLE) - ->eq(self::TABLE.'.id', $subtask_id) - ->columns(self::TABLE.'.*', User::TABLE.'.username', User::TABLE.'.name') - ->subquery($this->subtaskTimeTracking->getTimerQuery($this->userSession->getId()), 'timer_start_date') - ->join(User::TABLE, 'id', 'user_id') - ->callback(array($this, 'addStatusName')) - ->findOne(); - } - - return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne(); - } - - /** - * Prepare data before insert/update - * - * @access public - * @param array $values Form values - */ - public function prepare(array &$values) - { - $this->helper->model->removeFields($values, array('another_subtask')); - $this->helper->model->resetFields($values, array('time_estimated', 'time_spent')); - } - - /** - * Prepare data before insert - * - * @access public - * @param array $values Form values - */ - public function prepareCreation(array &$values) - { - $this->prepare($values); - - $values['position'] = $this->getLastPosition($values['task_id']) + 1; - $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO; - $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0; - $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0; - $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0; - } - - /** - * Get the position of the last column for a given project - * - * @access public - * @param integer $task_id Task id - * @return integer - */ - public function getLastPosition($task_id) - { - return (int) $this->db - ->table(self::TABLE) - ->eq('task_id', $task_id) - ->desc('position') - ->findOneColumn('position'); - } - - /** - * Create a new subtask - * - * @access public - * @param array $values Form values - * @return bool|integer - */ - public function create(array $values) - { - $this->prepareCreation($values); - $subtask_id = $this->db->table(self::TABLE)->persist($values); - - if ($subtask_id) { - $this->container['dispatcher']->dispatch( - self::EVENT_CREATE, - new SubtaskEvent(array('id' => $subtask_id) + $values) - ); - } - - return $subtask_id; - } - - /** - * Update - * - * @access public - * @param array $values Form values - * @param bool $fire_events If true, will be called an event - * @return bool - */ - public function update(array $values, $fire_events = true) - { - $this->prepare($values); - $subtask = $this->getById($values['id']); - $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); - - if ($result && $fire_events) { - $event = $subtask; - $event['changes'] = array_diff_assoc($values, $subtask); - $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new SubtaskEvent($event)); - } - - return $result; - } - - /** - * Close all subtasks of a task - * - * @access public - * @param integer $task_id - * @return boolean - */ - public function closeAll($task_id) - { - return $this->db->table(self::TABLE)->eq('task_id', $task_id)->update(array('status' => self::STATUS_DONE)); - } - - /** - * Save subtask position - * - * @access public - * @param integer $task_id - * @param integer $subtask_id - * @param integer $position - * @return boolean - */ - public function changePosition($task_id, $subtask_id, $position) - { - if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('task_id', $task_id)->count()) { - return false; - } - - $subtask_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->neq('id', $subtask_id)->asc('position')->findAllByColumn('id'); - $offset = 1; - $results = array(); - - foreach ($subtask_ids as $current_subtask_id) { - if ($offset == $position) { - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $current_subtask_id)->update(array('position' => $offset)); - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position)); - - return !in_array(false, $results, true); - } - - /** - * Change the status of subtask - * - * @access public - * @param integer $subtask_id - * @return boolean|integer - */ - public function toggleStatus($subtask_id) - { - $subtask = $this->getById($subtask_id); - $status = ($subtask['status'] + 1) % 3; - - $values = array( - 'id' => $subtask['id'], - 'status' => $status, - 'task_id' => $subtask['task_id'], - ); - - if (empty($subtask['user_id']) && $this->userSession->isLogged()) { - $values['user_id'] = $this->userSession->getId(); - } - - return $this->update($values) ? $status : false; - } - - /** - * Get the subtask in progress for this user - * - * @access public - * @param integer $user_id - * @return array - */ - public function getSubtaskInProgress($user_id) - { - return $this->db->table(self::TABLE) - ->eq('status', self::STATUS_INPROGRESS) - ->eq('user_id', $user_id) - ->findOne(); - } - - /** - * Return true if the user have a subtask in progress - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function hasSubtaskInProgress($user_id) - { - return $this->config->get('subtask_restriction') == 1 && - $this->db->table(self::TABLE) - ->eq('status', self::STATUS_INPROGRESS) - ->eq('user_id', $user_id) - ->exists(); - } - - /** - * Remove - * - * @access public - * @param integer $subtask_id Subtask id - * @return bool - */ - public function remove($subtask_id) - { - $subtask = $this->getById($subtask_id); - $result = $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); - - if ($result) { - $this->container['dispatcher']->dispatch(self::EVENT_DELETE, new SubtaskEvent($subtask)); - } - - return $result; - } - - /** - * Duplicate all subtasks to another task - * - * @access public - * @param integer $src_task_id Source task id - * @param integer $dst_task_id Destination task id - * @return bool - */ - public function duplicate($src_task_id, $dst_task_id) - { - return $this->db->transaction(function (Database $db) use ($src_task_id, $dst_task_id) { - - $subtasks = $db->table(Subtask::TABLE) - ->columns('title', 'time_estimated', 'position') - ->eq('task_id', $src_task_id) - ->asc('position') - ->findAll(); - - foreach ($subtasks as &$subtask) { - $subtask['task_id'] = $dst_task_id; - - if (! $db->table(Subtask::TABLE)->save($subtask)) { - return false; - } - } - }); - } - - /** - * Convert a subtask to a task - * - * @access public - * @param integer $project_id - * @param integer $subtask_id - * @return integer - */ - public function convertToTask($project_id, $subtask_id) - { - $subtask = $this->getById($subtask_id); - - $task_id = $this->taskCreation->create(array( - 'project_id' => $project_id, - 'title' => $subtask['title'], - 'time_estimated' => $subtask['time_estimated'], - 'time_spent' => $subtask['time_spent'], - 'owner_id' => $subtask['user_id'], - )); - - if ($task_id !== false) { - $this->remove($subtask_id); - } - - return $task_id; - } -} diff --git a/app/Model/SubtaskModel.php b/app/Model/SubtaskModel.php new file mode 100644 index 00000000..b63cf0a1 --- /dev/null +++ b/app/Model/SubtaskModel.php @@ -0,0 +1,428 @@ + t('Todo'), + self::STATUS_INPROGRESS => t('In progress'), + self::STATUS_DONE => t('Done'), + ); + } + + /** + * Add subtask status status to the resultset + * + * @access public + * @param array $subtasks Subtasks + * @return array + */ + public function addStatusName(array $subtasks) + { + $status = $this->getStatusList(); + + foreach ($subtasks as &$subtask) { + $subtask['status_name'] = $status[$subtask['status']]; + $subtask['timer_start_date'] = isset($subtask['timer_start_date']) ? $subtask['timer_start_date'] : 0; + $subtask['is_timer_started'] = ! empty($subtask['timer_start_date']); + } + + return $subtasks; + } + + /** + * Get the query to fetch subtasks assigned to a user + * + * @access public + * @param integer $user_id User id + * @param array $status List of status + * @return \PicoDb\Table + */ + public function getUserQuery($user_id, array $status) + { + return $this->db->table(SubtaskModel::TABLE) + ->columns( + SubtaskModel::TABLE.'.*', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.title AS task_name', + ProjectModel::TABLE.'.name AS project_name' + ) + ->subquery($this->subtaskTimeTrackingModel->getTimerQuery($user_id), 'timer_start_date') + ->eq('user_id', $user_id) + ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE) + ->in(SubtaskModel::TABLE.'.status', $status) + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) + ->callback(array($this, 'addStatusName')); + } + + /** + * Get all subtasks 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) + ->eq('task_id', $task_id) + ->columns( + self::TABLE.'.*', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name' + ) + ->subquery($this->subtaskTimeTrackingModel->getTimerQuery($this->userSession->getId()), 'timer_start_date') + ->join(UserModel::TABLE, 'id', 'user_id') + ->asc(self::TABLE.'.position') + ->callback(array($this, 'addStatusName')) + ->findAll(); + } + + /** + * Get a subtask by the id + * + * @access public + * @param integer $subtask_id Subtask id + * @param bool $more Fetch more data + * @return array + */ + public function getById($subtask_id, $more = false) + { + if ($more) { + return $this->db + ->table(self::TABLE) + ->eq(self::TABLE.'.id', $subtask_id) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.username', UserModel::TABLE.'.name') + ->subquery($this->subtaskTimeTrackingModel->getTimerQuery($this->userSession->getId()), 'timer_start_date') + ->join(UserModel::TABLE, 'id', 'user_id') + ->callback(array($this, 'addStatusName')) + ->findOne(); + } + + return $this->db->table(self::TABLE)->eq('id', $subtask_id)->findOne(); + } + + /** + * Prepare data before insert/update + * + * @access public + * @param array $values Form values + */ + public function prepare(array &$values) + { + $this->helper->model->removeFields($values, array('another_subtask')); + $this->helper->model->resetFields($values, array('time_estimated', 'time_spent')); + } + + /** + * Prepare data before insert + * + * @access public + * @param array $values Form values + */ + public function prepareCreation(array &$values) + { + $this->prepare($values); + + $values['position'] = $this->getLastPosition($values['task_id']) + 1; + $values['status'] = isset($values['status']) ? $values['status'] : self::STATUS_TODO; + $values['time_estimated'] = isset($values['time_estimated']) ? $values['time_estimated'] : 0; + $values['time_spent'] = isset($values['time_spent']) ? $values['time_spent'] : 0; + $values['user_id'] = isset($values['user_id']) ? $values['user_id'] : 0; + } + + /** + * Get the position of the last column for a given project + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function getLastPosition($task_id) + { + return (int) $this->db + ->table(self::TABLE) + ->eq('task_id', $task_id) + ->desc('position') + ->findOneColumn('position'); + } + + /** + * Create a new subtask + * + * @access public + * @param array $values Form values + * @return bool|integer + */ + public function create(array $values) + { + $this->prepareCreation($values); + $subtask_id = $this->db->table(self::TABLE)->persist($values); + + if ($subtask_id) { + $this->container['dispatcher']->dispatch( + self::EVENT_CREATE, + new SubtaskEvent(array('id' => $subtask_id) + $values) + ); + } + + return $subtask_id; + } + + /** + * Update + * + * @access public + * @param array $values Form values + * @param bool $fire_events If true, will be called an event + * @return bool + */ + public function update(array $values, $fire_events = true) + { + $this->prepare($values); + $subtask = $this->getById($values['id']); + $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->save($values); + + if ($result && $fire_events) { + $event = $subtask; + $event['changes'] = array_diff_assoc($values, $subtask); + $this->container['dispatcher']->dispatch(self::EVENT_UPDATE, new SubtaskEvent($event)); + } + + return $result; + } + + /** + * Close all subtasks of a task + * + * @access public + * @param integer $task_id + * @return boolean + */ + public function closeAll($task_id) + { + return $this->db->table(self::TABLE)->eq('task_id', $task_id)->update(array('status' => self::STATUS_DONE)); + } + + /** + * Save subtask position + * + * @access public + * @param integer $task_id + * @param integer $subtask_id + * @param integer $position + * @return boolean + */ + public function changePosition($task_id, $subtask_id, $position) + { + if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('task_id', $task_id)->count()) { + return false; + } + + $subtask_ids = $this->db->table(self::TABLE)->eq('task_id', $task_id)->neq('id', $subtask_id)->asc('position')->findAllByColumn('id'); + $offset = 1; + $results = array(); + + foreach ($subtask_ids as $current_subtask_id) { + if ($offset == $position) { + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $current_subtask_id)->update(array('position' => $offset)); + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $subtask_id)->update(array('position' => $position)); + + return !in_array(false, $results, true); + } + + /** + * Change the status of subtask + * + * @access public + * @param integer $subtask_id + * @return boolean|integer + */ + public function toggleStatus($subtask_id) + { + $subtask = $this->getById($subtask_id); + $status = ($subtask['status'] + 1) % 3; + + $values = array( + 'id' => $subtask['id'], + 'status' => $status, + 'task_id' => $subtask['task_id'], + ); + + if (empty($subtask['user_id']) && $this->userSession->isLogged()) { + $values['user_id'] = $this->userSession->getId(); + } + + return $this->update($values) ? $status : false; + } + + /** + * Get the subtask in progress for this user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getSubtaskInProgress($user_id) + { + return $this->db->table(self::TABLE) + ->eq('status', self::STATUS_INPROGRESS) + ->eq('user_id', $user_id) + ->findOne(); + } + + /** + * Return true if the user have a subtask in progress + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function hasSubtaskInProgress($user_id) + { + return $this->configModel->get('subtask_restriction') == 1 && + $this->db->table(self::TABLE) + ->eq('status', self::STATUS_INPROGRESS) + ->eq('user_id', $user_id) + ->exists(); + } + + /** + * Remove + * + * @access public + * @param integer $subtask_id Subtask id + * @return bool + */ + public function remove($subtask_id) + { + $subtask = $this->getById($subtask_id); + $result = $this->db->table(self::TABLE)->eq('id', $subtask_id)->remove(); + + if ($result) { + $this->container['dispatcher']->dispatch(self::EVENT_DELETE, new SubtaskEvent($subtask)); + } + + return $result; + } + + /** + * Duplicate all subtasks to another task + * + * @access public + * @param integer $src_task_id Source task id + * @param integer $dst_task_id Destination task id + * @return bool + */ + public function duplicate($src_task_id, $dst_task_id) + { + return $this->db->transaction(function (Database $db) use ($src_task_id, $dst_task_id) { + + $subtasks = $db->table(SubtaskModel::TABLE) + ->columns('title', 'time_estimated', 'position') + ->eq('task_id', $src_task_id) + ->asc('position') + ->findAll(); + + foreach ($subtasks as &$subtask) { + $subtask['task_id'] = $dst_task_id; + + if (! $db->table(SubtaskModel::TABLE)->save($subtask)) { + return false; + } + } + }); + } + + /** + * Convert a subtask to a task + * + * @access public + * @param integer $project_id + * @param integer $subtask_id + * @return integer + */ + public function convertToTask($project_id, $subtask_id) + { + $subtask = $this->getById($subtask_id); + + $task_id = $this->taskCreationModel->create(array( + 'project_id' => $project_id, + 'title' => $subtask['title'], + 'time_estimated' => $subtask['time_estimated'], + 'time_spent' => $subtask['time_spent'], + 'owner_id' => $subtask['user_id'], + )); + + if ($task_id !== false) { + $this->remove($subtask_id); + } + + return $task_id; + } +} diff --git a/app/Model/SubtaskTimeTracking.php b/app/Model/SubtaskTimeTracking.php deleted file mode 100644 index 8b5d2545..00000000 --- a/app/Model/SubtaskTimeTracking.php +++ /dev/null @@ -1,298 +0,0 @@ -db->escapeIdentifier('start'), - $this->db->escapeIdentifier(self::TABLE), - $this->db->escapeIdentifier('user_id'), - $user_id, - $this->db->escapeIdentifier('end'), - $this->db->escapeIdentifier('subtask_id'), - Subtask::TABLE.'.id' - ); - } - - /** - * Get query for user timesheet (pagination) - * - * @access public - * @param integer $user_id User id - * @return \PicoDb\Table - */ - public function getUserQuery($user_id) - { - return $this->db - ->table(self::TABLE) - ->columns( - self::TABLE.'.id', - self::TABLE.'.subtask_id', - self::TABLE.'.end', - self::TABLE.'.start', - self::TABLE.'.time_spent', - Subtask::TABLE.'.task_id', - Subtask::TABLE.'.title AS subtask_title', - Task::TABLE.'.title AS task_title', - Task::TABLE.'.project_id', - Task::TABLE.'.color_id' - ) - ->join(Subtask::TABLE, 'id', 'subtask_id') - ->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE) - ->eq(self::TABLE.'.user_id', $user_id); - } - - /** - * Get query for task timesheet (pagination) - * - * @access public - * @param integer $task_id Task id - * @return \PicoDb\Table - */ - public function getTaskQuery($task_id) - { - return $this->db - ->table(self::TABLE) - ->columns( - self::TABLE.'.id', - self::TABLE.'.subtask_id', - self::TABLE.'.end', - self::TABLE.'.start', - self::TABLE.'.time_spent', - self::TABLE.'.user_id', - Subtask::TABLE.'.task_id', - Subtask::TABLE.'.title AS subtask_title', - Task::TABLE.'.project_id', - User::TABLE.'.username', - User::TABLE.'.name AS user_fullname' - ) - ->join(Subtask::TABLE, 'id', 'subtask_id') - ->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE) - ->join(User::TABLE, 'id', 'user_id', self::TABLE) - ->eq(Task::TABLE.'.id', $task_id); - } - - /** - * Get query for project timesheet (pagination) - * - * @access public - * @param integer $project_id Project id - * @return \PicoDb\Table - */ - public function getProjectQuery($project_id) - { - return $this->db - ->table(self::TABLE) - ->columns( - self::TABLE.'.id', - self::TABLE.'.subtask_id', - self::TABLE.'.end', - self::TABLE.'.start', - self::TABLE.'.time_spent', - self::TABLE.'.user_id', - Subtask::TABLE.'.task_id', - Subtask::TABLE.'.title AS subtask_title', - Task::TABLE.'.project_id', - Task::TABLE.'.color_id', - User::TABLE.'.username', - User::TABLE.'.name AS user_fullname' - ) - ->join(Subtask::TABLE, 'id', 'subtask_id') - ->join(Task::TABLE, 'id', 'task_id', Subtask::TABLE) - ->join(User::TABLE, 'id', 'user_id', self::TABLE) - ->eq(Task::TABLE.'.project_id', $project_id) - ->asc(self::TABLE.'.id'); - } - - /** - * Get all recorded time slots for a given user - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function getUserTimesheet($user_id) - { - return $this->db - ->table(self::TABLE) - ->eq('user_id', $user_id) - ->findAll(); - } - - /** - * Return true if a timer is started for this use and subtask - * - * @access public - * @param integer $subtask_id - * @param integer $user_id - * @return boolean - */ - public function hasTimer($subtask_id, $user_id) - { - return $this->db->table(self::TABLE)->eq('subtask_id', $subtask_id)->eq('user_id', $user_id)->eq('end', 0)->exists(); - } - - /** - * Log start time - * - * @access public - * @param integer $subtask_id - * @param integer $user_id - * @return boolean - */ - public function logStartTime($subtask_id, $user_id) - { - return - ! $this->hasTimer($subtask_id, $user_id) && - $this->db - ->table(self::TABLE) - ->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time(), 'end' => 0)); - } - - /** - * Log end time - * - * @access public - * @param integer $subtask_id - * @param integer $user_id - * @return boolean - */ - public function logEndTime($subtask_id, $user_id) - { - $time_spent = $this->getTimeSpent($subtask_id, $user_id); - - if ($time_spent > 0) { - $this->updateSubtaskTimeSpent($subtask_id, $time_spent); - } - - return $this->db - ->table(self::TABLE) - ->eq('subtask_id', $subtask_id) - ->eq('user_id', $user_id) - ->eq('end', 0) - ->update(array( - 'end' => time(), - 'time_spent' => $time_spent, - )); - } - - /** - * Calculate the time spent when the clock is stopped - * - * @access public - * @param integer $subtask_id - * @param integer $user_id - * @return float - */ - public function getTimeSpent($subtask_id, $user_id) - { - $hook = 'model:subtask-time-tracking:calculate:time-spent'; - $start_time = $this->db - ->table(self::TABLE) - ->eq('subtask_id', $subtask_id) - ->eq('user_id', $user_id) - ->eq('end', 0) - ->findOneColumn('start'); - - if (empty($start_time)) { - return 0; - } - - $end = new DateTime; - $start = new DateTime; - $start->setTimestamp($start_time); - - if ($this->hook->exists($hook)) { - return $this->hook->first($hook, array( - 'user_id' => $user_id, - 'start' => $start, - 'end' => $end, - )); - } - - return $this->dateParser->getHours($start, $end); - } - - /** - * Update subtask time spent - * - * @access public - * @param integer $subtask_id - * @param float $time_spent - * @return bool - */ - public function updateSubtaskTimeSpent($subtask_id, $time_spent) - { - $subtask = $this->subtask->getById($subtask_id); - - // Fire the event subtask.update - return $this->subtask->update(array( - 'id' => $subtask['id'], - 'time_spent' => $subtask['time_spent'] + $time_spent, - 'task_id' => $subtask['task_id'], - ), false); - } - - /** - * Update task time tracking based on subtasks time tracking - * - * @access public - * @param integer $task_id Task id - * @return bool - */ - public function updateTaskTimeTracking($task_id) - { - $values = $this->calculateSubtaskTime($task_id); - - return $this->db - ->table(Task::TABLE) - ->eq('id', $task_id) - ->update($values); - } - - /** - * Sum time spent and time estimated for all subtasks - * - * @access public - * @param integer $task_id Task id - * @return array - */ - public function calculateSubtaskTime($task_id) - { - return $this->db - ->table(Subtask::TABLE) - ->eq('task_id', $task_id) - ->columns( - 'SUM(time_spent) AS time_spent', - 'SUM(time_estimated) AS time_estimated' - ) - ->findOne(); - } -} diff --git a/app/Model/SubtaskTimeTrackingModel.php b/app/Model/SubtaskTimeTrackingModel.php new file mode 100644 index 00000000..062e594a --- /dev/null +++ b/app/Model/SubtaskTimeTrackingModel.php @@ -0,0 +1,298 @@ +db->escapeIdentifier('start'), + $this->db->escapeIdentifier(self::TABLE), + $this->db->escapeIdentifier('user_id'), + $user_id, + $this->db->escapeIdentifier('end'), + $this->db->escapeIdentifier('subtask_id'), + SubtaskModel::TABLE.'.id' + ); + } + + /** + * Get query for user timesheet (pagination) + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.subtask_id', + self::TABLE.'.end', + self::TABLE.'.start', + self::TABLE.'.time_spent', + SubtaskModel::TABLE.'.task_id', + SubtaskModel::TABLE.'.title AS subtask_title', + TaskModel::TABLE.'.title AS task_title', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id' + ) + ->join(SubtaskModel::TABLE, 'id', 'subtask_id') + ->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) + ->eq(self::TABLE.'.user_id', $user_id); + } + + /** + * Get query for task timesheet (pagination) + * + * @access public + * @param integer $task_id Task id + * @return \PicoDb\Table + */ + public function getTaskQuery($task_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.subtask_id', + self::TABLE.'.end', + self::TABLE.'.start', + self::TABLE.'.time_spent', + self::TABLE.'.user_id', + SubtaskModel::TABLE.'.task_id', + SubtaskModel::TABLE.'.title AS subtask_title', + TaskModel::TABLE.'.project_id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name AS user_fullname' + ) + ->join(SubtaskModel::TABLE, 'id', 'subtask_id') + ->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', self::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id); + } + + /** + * Get query for project timesheet (pagination) + * + * @access public + * @param integer $project_id Project id + * @return \PicoDb\Table + */ + public function getProjectQuery($project_id) + { + return $this->db + ->table(self::TABLE) + ->columns( + self::TABLE.'.id', + self::TABLE.'.subtask_id', + self::TABLE.'.end', + self::TABLE.'.start', + self::TABLE.'.time_spent', + self::TABLE.'.user_id', + SubtaskModel::TABLE.'.task_id', + SubtaskModel::TABLE.'.title AS subtask_title', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + UserModel::TABLE.'.username', + UserModel::TABLE.'.name AS user_fullname' + ) + ->join(SubtaskModel::TABLE, 'id', 'subtask_id') + ->join(TaskModel::TABLE, 'id', 'task_id', SubtaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', self::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->asc(self::TABLE.'.id'); + } + + /** + * Get all recorded time slots for a given user + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function getUserTimesheet($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('user_id', $user_id) + ->findAll(); + } + + /** + * Return true if a timer is started for this use and subtask + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function hasTimer($subtask_id, $user_id) + { + return $this->db->table(self::TABLE)->eq('subtask_id', $subtask_id)->eq('user_id', $user_id)->eq('end', 0)->exists(); + } + + /** + * Log start time + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function logStartTime($subtask_id, $user_id) + { + return + ! $this->hasTimer($subtask_id, $user_id) && + $this->db + ->table(self::TABLE) + ->insert(array('subtask_id' => $subtask_id, 'user_id' => $user_id, 'start' => time(), 'end' => 0)); + } + + /** + * Log end time + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return boolean + */ + public function logEndTime($subtask_id, $user_id) + { + $time_spent = $this->getTimeSpent($subtask_id, $user_id); + + if ($time_spent > 0) { + $this->updateSubtaskTimeSpent($subtask_id, $time_spent); + } + + return $this->db + ->table(self::TABLE) + ->eq('subtask_id', $subtask_id) + ->eq('user_id', $user_id) + ->eq('end', 0) + ->update(array( + 'end' => time(), + 'time_spent' => $time_spent, + )); + } + + /** + * Calculate the time spent when the clock is stopped + * + * @access public + * @param integer $subtask_id + * @param integer $user_id + * @return float + */ + public function getTimeSpent($subtask_id, $user_id) + { + $hook = 'model:subtask-time-tracking:calculate:time-spent'; + $start_time = $this->db + ->table(self::TABLE) + ->eq('subtask_id', $subtask_id) + ->eq('user_id', $user_id) + ->eq('end', 0) + ->findOneColumn('start'); + + if (empty($start_time)) { + return 0; + } + + $end = new DateTime; + $start = new DateTime; + $start->setTimestamp($start_time); + + if ($this->hook->exists($hook)) { + return $this->hook->first($hook, array( + 'user_id' => $user_id, + 'start' => $start, + 'end' => $end, + )); + } + + return $this->dateParser->getHours($start, $end); + } + + /** + * Update subtask time spent + * + * @access public + * @param integer $subtask_id + * @param float $time_spent + * @return bool + */ + public function updateSubtaskTimeSpent($subtask_id, $time_spent) + { + $subtask = $this->subtaskModel->getById($subtask_id); + + // Fire the event subtask.update + return $this->subtaskModel->update(array( + 'id' => $subtask['id'], + 'time_spent' => $subtask['time_spent'] + $time_spent, + 'task_id' => $subtask['task_id'], + ), false); + } + + /** + * Update task time tracking based on subtasks time tracking + * + * @access public + * @param integer $task_id Task id + * @return bool + */ + public function updateTaskTimeTracking($task_id) + { + $values = $this->calculateSubtaskTime($task_id); + + return $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update($values); + } + + /** + * Sum time spent and time estimated for all subtasks + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function calculateSubtaskTime($task_id) + { + return $this->db + ->table(SubtaskModel::TABLE) + ->eq('task_id', $task_id) + ->columns( + 'SUM(time_spent) AS time_spent', + 'SUM(time_estimated) AS time_estimated' + ) + ->findOne(); + } +} diff --git a/app/Model/Swimlane.php b/app/Model/Swimlane.php deleted file mode 100644 index 8efe68da..00000000 --- a/app/Model/Swimlane.php +++ /dev/null @@ -1,490 +0,0 @@ -db->table(self::TABLE)->eq('id', $swimlane_id)->findOne(); - } - - /** - * Get the swimlane name by the id - * - * @access public - * @param integer $swimlane_id Swimlane id - * @return string - */ - public function getNameById($swimlane_id) - { - return $this->db->table(self::TABLE)->eq('id', $swimlane_id)->findOneColumn('name') ?: ''; - } - - /** - * Get a swimlane id by the project and the name - * - * @access public - * @param integer $project_id Project id - * @param string $name Name - * @return integer - */ - public function getIdByName($project_id, $name) - { - return (int) $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('name', $name) - ->findOneColumn('id'); - } - - /** - * Get a swimlane by the project and the name - * - * @access public - * @param integer $project_id Project id - * @param string $name Swimlane name - * @return array - */ - public function getByName($project_id, $name) - { - return $this->db->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('name', $name) - ->findOne(); - } - - /** - * Get default swimlane properties - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getDefault($project_id) - { - $result = $this->db - ->table(Project::TABLE) - ->eq('id', $project_id) - ->columns('id', 'default_swimlane', 'show_default_swimlane') - ->findOne(); - - if ($result['default_swimlane'] === 'Default swimlane') { - $result['default_swimlane'] = t($result['default_swimlane']); - } - - return $result; - } - - /** - * Get all swimlanes for a given project - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getAll($project_id) - { - return $this->db - ->table(self::TABLE) - ->eq('project_id', $project_id) - ->orderBy('position', 'asc') - ->findAll(); - } - - /** - * Get the list of swimlanes by status - * - * @access public - * @param integer $project_id Project id - * @param integer $status Status - * @return array - */ - public function getAllByStatus($project_id, $status = self::ACTIVE) - { - $query = $this->db - ->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', $status); - - if ($status == self::ACTIVE) { - $query->asc('position'); - } else { - $query->asc('name'); - } - - return $query->findAll(); - } - - /** - * Get active swimlanes - * - * @access public - * @param integer $project_id Project id - * @return array - */ - public function getSwimlanes($project_id) - { - $swimlanes = $this->db - ->table(self::TABLE) - ->columns('id', 'name', 'description') - ->eq('project_id', $project_id) - ->eq('is_active', self::ACTIVE) - ->orderBy('position', 'asc') - ->findAll(); - - $default_swimlane = $this->db - ->table(Project::TABLE) - ->eq('id', $project_id) - ->eq('show_default_swimlane', 1) - ->findOneColumn('default_swimlane'); - - if ($default_swimlane) { - if ($default_swimlane === 'Default swimlane') { - $default_swimlane = t($default_swimlane); - } - - array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane)); - } - - return $swimlanes; - } - - /** - * Get list of all swimlanes - * - * @access public - * @param integer $project_id Project id - * @param boolean $prepend Prepend default value - * @param boolean $only_active Return only active swimlanes - * @return array - */ - public function getList($project_id, $prepend = false, $only_active = false) - { - $swimlanes = array(); - $default = $this->db->table(Project::TABLE)->eq('id', $project_id)->eq('show_default_swimlane', 1)->findOneColumn('default_swimlane'); - - if ($prepend) { - $swimlanes[-1] = t('All swimlanes'); - } - - if (! empty($default)) { - $swimlanes[0] = $default === 'Default swimlane' ? t($default) : $default; - } - - return $swimlanes + $this->db - ->hashtable(self::TABLE) - ->eq('project_id', $project_id) - ->in('is_active', $only_active ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE)) - ->orderBy('position', 'asc') - ->getAll('id', 'name'); - } - - /** - * Add a new swimlane - * - * @access public - * @param array $values Form values - * @return integer|boolean - */ - public function create($values) - { - if (! $this->project->exists($values['project_id'])) { - return 0; - } - - $values['position'] = $this->getLastPosition($values['project_id']); - return $this->db->table(self::TABLE)->persist($values); - } - - /** - * Update a swimlane - * - * @access public - * @param array $values Form values - * @return bool - */ - public function update(array $values) - { - return $this->db - ->table(self::TABLE) - ->eq('id', $values['id']) - ->update($values); - } - - /** - * Update the default swimlane - * - * @access public - * @param array $values Form values - * @return bool - */ - public function updateDefault(array $values) - { - return $this->db - ->table(Project::TABLE) - ->eq('id', $values['id']) - ->update(array( - 'default_swimlane' => $values['default_swimlane'], - 'show_default_swimlane' => $values['show_default_swimlane'], - )); - } - - /** - * Enable the default swimlane - * - * @access public - * @param integer $project_id - * @return bool - */ - public function enableDefault($project_id) - { - return $this->db - ->table(Project::TABLE) - ->eq('id', $project_id) - ->update(array( - 'show_default_swimlane' => 1, - )); - } - - /** - * Disable the default swimlane - * - * @access public - * @param integer $project_id - * @return bool - */ - public function disableDefault($project_id) - { - return $this->db - ->table(Project::TABLE) - ->eq('id', $project_id) - ->update(array( - 'show_default_swimlane' => 0, - )); - } - - /** - * Get the last position of a swimlane - * - * @access public - * @param integer $project_id - * @return integer - */ - public function getLastPosition($project_id) - { - return $this->db - ->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', 1) - ->count() + 1; - } - - /** - * Disable a swimlane - * - * @access public - * @param integer $project_id Project id - * @param integer $swimlane_id Swimlane id - * @return bool - */ - public function disable($project_id, $swimlane_id) - { - $result = $this->db - ->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array( - 'is_active' => self::INACTIVE, - 'position' => 0, - )); - - if ($result) { - // Re-order positions - $this->updatePositions($project_id); - } - - return $result; - } - - /** - * Enable a swimlane - * - * @access public - * @param integer $project_id Project id - * @param integer $swimlane_id Swimlane id - * @return bool - */ - public function enable($project_id, $swimlane_id) - { - return $this->db - ->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array( - 'is_active' => self::ACTIVE, - 'position' => $this->getLastPosition($project_id), - )); - } - - /** - * Remove a swimlane - * - * @access public - * @param integer $project_id Project id - * @param integer $swimlane_id Swimlane id - * @return bool - */ - public function remove($project_id, $swimlane_id) - { - $this->db->startTransaction(); - - // Tasks should not be assigned anymore to this swimlane - $this->db->table(Task::TABLE)->eq('swimlane_id', $swimlane_id)->update(array('swimlane_id' => 0)); - - if (! $this->db->table(self::TABLE)->eq('id', $swimlane_id)->remove()) { - $this->db->cancelTransaction(); - return false; - } - - // Re-order positions - $this->updatePositions($project_id); - - $this->db->closeTransaction(); - - return true; - } - - /** - * Update swimlane positions after disabling or removing a swimlane - * - * @access public - * @param integer $project_id Project id - * @return boolean - */ - public function updatePositions($project_id) - { - $position = 0; - $swimlanes = $this->db - ->table(self::TABLE) - ->eq('project_id', $project_id) - ->eq('is_active', 1) - ->asc('position') - ->asc('id') - ->findAllByColumn('id'); - - if (! $swimlanes) { - return false; - } - - foreach ($swimlanes as $swimlane_id) { - $this->db->table(self::TABLE) - ->eq('id', $swimlane_id) - ->update(array('position' => ++$position)); - } - - return true; - } - - /** - * Change swimlane position - * - * @access public - * @param integer $project_id - * @param integer $swimlane_id - * @param integer $position - * @return boolean - */ - public function changePosition($project_id, $swimlane_id, $position) - { - if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $project_id)->count()) { - return false; - } - - $swimlane_ids = $this->db->table(self::TABLE) - ->eq('is_active', 1) - ->eq('project_id', $project_id) - ->neq('id', $swimlane_id) - ->asc('position') - ->findAllByColumn('id'); - - $offset = 1; - $results = array(); - - foreach ($swimlane_ids as $current_swimlane_id) { - if ($offset == $position) { - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $current_swimlane_id)->update(array('position' => $offset)); - $offset++; - } - - $results[] = $this->db->table(self::TABLE)->eq('id', $swimlane_id)->update(array('position' => $position)); - - return !in_array(false, $results, true); - } - - /** - * Duplicate Swimlane to project - * - * @access public - * @param integer $project_from Project Template - * @param integer $project_to Project that receives the copy - * @return integer|boolean - */ - - public function duplicate($project_from, $project_to) - { - $swimlanes = $this->getAll($project_from); - - foreach ($swimlanes as $swimlane) { - unset($swimlane['id']); - $swimlane['project_id'] = $project_to; - - if (! $this->db->table(self::TABLE)->save($swimlane)) { - return false; - } - } - - $default_swimlane = $this->getDefault($project_from); - $default_swimlane['id'] = $project_to; - - $this->updateDefault($default_swimlane); - - return true; - } -} diff --git a/app/Model/SwimlaneModel.php b/app/Model/SwimlaneModel.php new file mode 100644 index 00000000..87591b99 --- /dev/null +++ b/app/Model/SwimlaneModel.php @@ -0,0 +1,490 @@ +db->table(self::TABLE)->eq('id', $swimlane_id)->findOne(); + } + + /** + * Get the swimlane name by the id + * + * @access public + * @param integer $swimlane_id Swimlane id + * @return string + */ + public function getNameById($swimlane_id) + { + return $this->db->table(self::TABLE)->eq('id', $swimlane_id)->findOneColumn('name') ?: ''; + } + + /** + * Get a swimlane id by the project and the name + * + * @access public + * @param integer $project_id Project id + * @param string $name Name + * @return integer + */ + public function getIdByName($project_id, $name) + { + return (int) $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('name', $name) + ->findOneColumn('id'); + } + + /** + * Get a swimlane by the project and the name + * + * @access public + * @param integer $project_id Project id + * @param string $name Swimlane name + * @return array + */ + public function getByName($project_id, $name) + { + return $this->db->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('name', $name) + ->findOne(); + } + + /** + * Get default swimlane properties + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getDefault($project_id) + { + $result = $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->columns('id', 'default_swimlane', 'show_default_swimlane') + ->findOne(); + + if ($result['default_swimlane'] === 'Default swimlane') { + $result['default_swimlane'] = t($result['default_swimlane']); + } + + return $result; + } + + /** + * Get all swimlanes for a given project + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getAll($project_id) + { + return $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->orderBy('position', 'asc') + ->findAll(); + } + + /** + * Get the list of swimlanes by status + * + * @access public + * @param integer $project_id Project id + * @param integer $status Status + * @return array + */ + public function getAllByStatus($project_id, $status = self::ACTIVE) + { + $query = $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', $status); + + if ($status == self::ACTIVE) { + $query->asc('position'); + } else { + $query->asc('name'); + } + + return $query->findAll(); + } + + /** + * Get active swimlanes + * + * @access public + * @param integer $project_id Project id + * @return array + */ + public function getSwimlanes($project_id) + { + $swimlanes = $this->db + ->table(self::TABLE) + ->columns('id', 'name', 'description') + ->eq('project_id', $project_id) + ->eq('is_active', self::ACTIVE) + ->orderBy('position', 'asc') + ->findAll(); + + $default_swimlane = $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->eq('show_default_swimlane', 1) + ->findOneColumn('default_swimlane'); + + if ($default_swimlane) { + if ($default_swimlane === 'Default swimlane') { + $default_swimlane = t($default_swimlane); + } + + array_unshift($swimlanes, array('id' => 0, 'name' => $default_swimlane)); + } + + return $swimlanes; + } + + /** + * Get list of all swimlanes + * + * @access public + * @param integer $project_id Project id + * @param boolean $prepend Prepend default value + * @param boolean $only_active Return only active swimlanes + * @return array + */ + public function getList($project_id, $prepend = false, $only_active = false) + { + $swimlanes = array(); + $default = $this->db->table(ProjectModel::TABLE)->eq('id', $project_id)->eq('show_default_swimlane', 1)->findOneColumn('default_swimlane'); + + if ($prepend) { + $swimlanes[-1] = t('All swimlanes'); + } + + if (! empty($default)) { + $swimlanes[0] = $default === 'Default swimlane' ? t($default) : $default; + } + + return $swimlanes + $this->db + ->hashtable(self::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $only_active ? array(self::ACTIVE) : array(self::ACTIVE, self::INACTIVE)) + ->orderBy('position', 'asc') + ->getAll('id', 'name'); + } + + /** + * Add a new swimlane + * + * @access public + * @param array $values Form values + * @return integer|boolean + */ + public function create($values) + { + if (! $this->projectModel->exists($values['project_id'])) { + return 0; + } + + $values['position'] = $this->getLastPosition($values['project_id']); + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Update a swimlane + * + * @access public + * @param array $values Form values + * @return bool + */ + public function update(array $values) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $values['id']) + ->update($values); + } + + /** + * Update the default swimlane + * + * @access public + * @param array $values Form values + * @return bool + */ + public function updateDefault(array $values) + { + return $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $values['id']) + ->update(array( + 'default_swimlane' => $values['default_swimlane'], + 'show_default_swimlane' => $values['show_default_swimlane'], + )); + } + + /** + * Enable the default swimlane + * + * @access public + * @param integer $project_id + * @return bool + */ + public function enableDefault($project_id) + { + return $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->update(array( + 'show_default_swimlane' => 1, + )); + } + + /** + * Disable the default swimlane + * + * @access public + * @param integer $project_id + * @return bool + */ + public function disableDefault($project_id) + { + return $this->db + ->table(ProjectModel::TABLE) + ->eq('id', $project_id) + ->update(array( + 'show_default_swimlane' => 0, + )); + } + + /** + * Get the last position of a swimlane + * + * @access public + * @param integer $project_id + * @return integer + */ + public function getLastPosition($project_id) + { + return $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->count() + 1; + } + + /** + * Disable a swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $swimlane_id Swimlane id + * @return bool + */ + public function disable($project_id, $swimlane_id) + { + $result = $this->db + ->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array( + 'is_active' => self::INACTIVE, + 'position' => 0, + )); + + if ($result) { + // Re-order positions + $this->updatePositions($project_id); + } + + return $result; + } + + /** + * Enable a swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $swimlane_id Swimlane id + * @return bool + */ + public function enable($project_id, $swimlane_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array( + 'is_active' => self::ACTIVE, + 'position' => $this->getLastPosition($project_id), + )); + } + + /** + * Remove a swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $swimlane_id Swimlane id + * @return bool + */ + public function remove($project_id, $swimlane_id) + { + $this->db->startTransaction(); + + // Tasks should not be assigned anymore to this swimlane + $this->db->table(TaskModel::TABLE)->eq('swimlane_id', $swimlane_id)->update(array('swimlane_id' => 0)); + + if (! $this->db->table(self::TABLE)->eq('id', $swimlane_id)->remove()) { + $this->db->cancelTransaction(); + return false; + } + + // Re-order positions + $this->updatePositions($project_id); + + $this->db->closeTransaction(); + + return true; + } + + /** + * Update swimlane positions after disabling or removing a swimlane + * + * @access public + * @param integer $project_id Project id + * @return boolean + */ + public function updatePositions($project_id) + { + $position = 0; + $swimlanes = $this->db + ->table(self::TABLE) + ->eq('project_id', $project_id) + ->eq('is_active', 1) + ->asc('position') + ->asc('id') + ->findAllByColumn('id'); + + if (! $swimlanes) { + return false; + } + + foreach ($swimlanes as $swimlane_id) { + $this->db->table(self::TABLE) + ->eq('id', $swimlane_id) + ->update(array('position' => ++$position)); + } + + return true; + } + + /** + * Change swimlane position + * + * @access public + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $position + * @return boolean + */ + public function changePosition($project_id, $swimlane_id, $position) + { + if ($position < 1 || $position > $this->db->table(self::TABLE)->eq('project_id', $project_id)->count()) { + return false; + } + + $swimlane_ids = $this->db->table(self::TABLE) + ->eq('is_active', 1) + ->eq('project_id', $project_id) + ->neq('id', $swimlane_id) + ->asc('position') + ->findAllByColumn('id'); + + $offset = 1; + $results = array(); + + foreach ($swimlane_ids as $current_swimlane_id) { + if ($offset == $position) { + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $current_swimlane_id)->update(array('position' => $offset)); + $offset++; + } + + $results[] = $this->db->table(self::TABLE)->eq('id', $swimlane_id)->update(array('position' => $position)); + + return !in_array(false, $results, true); + } + + /** + * Duplicate Swimlane to project + * + * @access public + * @param integer $project_from Project Template + * @param integer $project_to Project that receives the copy + * @return integer|boolean + */ + + public function duplicate($project_from, $project_to) + { + $swimlanes = $this->getAll($project_from); + + foreach ($swimlanes as $swimlane) { + unset($swimlane['id']); + $swimlane['project_id'] = $project_to; + + if (! $this->db->table(self::TABLE)->save($swimlane)) { + return false; + } + } + + $default_swimlane = $this->getDefault($project_from); + $default_swimlane['id'] = $project_to; + + $this->updateDefault($default_swimlane); + + return true; + } +} diff --git a/app/Model/Task.php b/app/Model/Task.php deleted file mode 100644 index bdb55c46..00000000 --- a/app/Model/Task.php +++ /dev/null @@ -1,225 +0,0 @@ -taskFinder->exists($task_id)) { - return false; - } - - $this->taskFile->removeAll($task_id); - - return $this->db->table(self::TABLE)->eq('id', $task_id)->remove(); - } - - /** - * Get a the task id from a text - * - * Example: "Fix bug #1234" will return 1234 - * - * @access public - * @param string $message Text - * @return integer - */ - public function getTaskIdFromText($message) - { - if (preg_match('!#(\d+)!i', $message, $matches) && isset($matches[1])) { - return $matches[1]; - } - - return 0; - } - - /** - * Return the list user selectable recurrence status - * - * @access public - * @return array - */ - public function getRecurrenceStatusList() - { - return array( - Task::RECURRING_STATUS_NONE => t('No'), - Task::RECURRING_STATUS_PENDING => t('Yes'), - ); - } - - /** - * Return the list recurrence triggers - * - * @access public - * @return array - */ - public function getRecurrenceTriggerList() - { - return array( - Task::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'), - Task::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'), - Task::RECURRING_TRIGGER_CLOSE => t('When task is closed'), - ); - } - - /** - * Return the list options to calculate recurrence due date - * - * @access public - * @return array - */ - public function getRecurrenceBasedateList() - { - return array( - Task::RECURRING_BASEDATE_DUEDATE => t('Existing due date'), - Task::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'), - ); - } - - /** - * Return the list recurrence timeframes - * - * @access public - * @return array - */ - public function getRecurrenceTimeframeList() - { - return array( - Task::RECURRING_TIMEFRAME_DAYS => t('Day(s)'), - Task::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'), - Task::RECURRING_TIMEFRAME_YEARS => t('Year(s)'), - ); - } - - /** - * Get task progress based on the column position - * - * @access public - * @param array $task - * @param array $columns - * @return integer - */ - public function getProgress(array $task, array $columns) - { - if ($task['is_active'] == self::STATUS_CLOSED) { - return 100; - } - - $position = 0; - - foreach ($columns as $column_id => $column_title) { - if ($column_id == $task['column_id']) { - break; - } - - $position++; - } - - return round(($position * 100) / count($columns), 1); - } - - /** - * Helper method to duplicate all tasks to another project - * - * @access public - * @param integer $src_project_id - * @param integer $dst_project_id - * @return boolean - */ - public function duplicate($src_project_id, $dst_project_id) - { - $task_ids = $this->taskFinder->getAllIds($src_project_id, array(Task::STATUS_OPEN, Task::STATUS_CLOSED)); - - foreach ($task_ids as $task_id) { - if (! $this->taskDuplication->duplicateToProject($task_id, $dst_project_id)) { - return false; - } - } - - return true; - } -} diff --git a/app/Model/TaskAnalytic.php b/app/Model/TaskAnalytic.php deleted file mode 100644 index bf118b4e..00000000 --- a/app/Model/TaskAnalytic.php +++ /dev/null @@ -1,72 +0,0 @@ -column->getList($task['project_id']); - $sums = $this->transition->getTimeSpentByTask($task['id']); - - foreach ($columns as $column_id => $column_title) { - $time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0; - - if ($task['column_id'] == $column_id) { - $time_spent += ($task['date_completed'] ?: time()) - $task['date_moved']; - } - - $result[] = array( - 'id' => $column_id, - 'title' => $column_title, - 'time_spent' => $time_spent, - ); - } - - return $result; - } -} diff --git a/app/Model/TaskAnalyticModel.php b/app/Model/TaskAnalyticModel.php new file mode 100644 index 00000000..3d6fe8a8 --- /dev/null +++ b/app/Model/TaskAnalyticModel.php @@ -0,0 +1,72 @@ +columnModel->getList($task['project_id']); + $sums = $this->transitionModel->getTimeSpentByTask($task['id']); + + foreach ($columns as $column_id => $column_title) { + $time_spent = isset($sums[$column_id]) ? $sums[$column_id] : 0; + + if ($task['column_id'] == $column_id) { + $time_spent += ($task['date_completed'] ?: time()) - $task['date_moved']; + } + + $result[] = array( + 'id' => $column_id, + 'title' => $column_title, + 'time_spent' => $time_spent, + ); + } + + return $result; + } +} diff --git a/app/Model/TaskCreation.php b/app/Model/TaskCreation.php deleted file mode 100644 index 2abfd95c..00000000 --- a/app/Model/TaskCreation.php +++ /dev/null @@ -1,103 +0,0 @@ -project->exists($values['project_id'])) { - return 0; - } - - $position = empty($values['position']) ? 0 : $values['position']; - - $this->prepare($values); - $task_id = $this->db->table(Task::TABLE)->persist($values); - - if ($task_id !== false) { - if ($position > 0 && $values['position'] > 1) { - $this->taskPosition->movePosition($values['project_id'], $task_id, $values['column_id'], $position, $values['swimlane_id'], false); - } - - $this->fireEvents($task_id, $values); - } - - return (int) $task_id; - } - - /** - * Prepare data - * - * @access public - * @param array $values Form values - */ - public function prepare(array &$values) - { - $values = $this->dateParser->convert($values, array('date_due')); - $values = $this->dateParser->convert($values, array('date_started'), true); - - $this->helper->model->removeFields($values, array('another_task')); - $this->helper->model->resetFields($values, array('date_started', 'creator_id', 'owner_id', 'swimlane_id', 'date_due', 'score', 'category_id', 'time_estimated')); - - if (empty($values['column_id'])) { - $values['column_id'] = $this->column->getFirstColumnId($values['project_id']); - } - - if (empty($values['color_id'])) { - $values['color_id'] = $this->color->getDefaultColor(); - } - - if (empty($values['title'])) { - $values['title'] = t('Untitled'); - } - - if ($this->userSession->isLogged()) { - $values['creator_id'] = $this->userSession->getId(); - } - - $values['swimlane_id'] = empty($values['swimlane_id']) ? 0 : $values['swimlane_id']; - $values['date_creation'] = time(); - $values['date_modification'] = $values['date_creation']; - $values['date_moved'] = $values['date_creation']; - $values['position'] = $this->taskFinder->countByColumnAndSwimlaneId($values['project_id'], $values['column_id'], $values['swimlane_id']) + 1; - } - - /** - * Fire events - * - * @access private - * @param integer $task_id Task id - * @param array $values Form values - */ - private function fireEvents($task_id, array $values) - { - $event = new TaskEvent(array('task_id' => $task_id) + $values); - - $this->logger->debug('Event fired: '.Task::EVENT_CREATE_UPDATE); - $this->logger->debug('Event fired: '.Task::EVENT_CREATE); - - $this->dispatcher->dispatch(Task::EVENT_CREATE_UPDATE, $event); - $this->dispatcher->dispatch(Task::EVENT_CREATE, $event); - - if (! empty($values['description'])) { - $this->userMention->fireEvents($values['description'], Task::EVENT_USER_MENTION, $event); - } - } -} diff --git a/app/Model/TaskCreationModel.php b/app/Model/TaskCreationModel.php new file mode 100644 index 00000000..3800f831 --- /dev/null +++ b/app/Model/TaskCreationModel.php @@ -0,0 +1,103 @@ +projectModel->exists($values['project_id'])) { + return 0; + } + + $position = empty($values['position']) ? 0 : $values['position']; + + $this->prepare($values); + $task_id = $this->db->table(TaskModel::TABLE)->persist($values); + + if ($task_id !== false) { + if ($position > 0 && $values['position'] > 1) { + $this->taskPositionModel->movePosition($values['project_id'], $task_id, $values['column_id'], $position, $values['swimlane_id'], false); + } + + $this->fireEvents($task_id, $values); + } + + return (int) $task_id; + } + + /** + * Prepare data + * + * @access public + * @param array $values Form values + */ + public function prepare(array &$values) + { + $values = $this->dateParser->convert($values, array('date_due')); + $values = $this->dateParser->convert($values, array('date_started'), true); + + $this->helper->model->removeFields($values, array('another_task')); + $this->helper->model->resetFields($values, array('date_started', 'creator_id', 'owner_id', 'swimlane_id', 'date_due', 'score', 'category_id', 'time_estimated')); + + if (empty($values['column_id'])) { + $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']); + } + + if (empty($values['color_id'])) { + $values['color_id'] = $this->colorModel->getDefaultColor(); + } + + if (empty($values['title'])) { + $values['title'] = t('Untitled'); + } + + if ($this->userSession->isLogged()) { + $values['creator_id'] = $this->userSession->getId(); + } + + $values['swimlane_id'] = empty($values['swimlane_id']) ? 0 : $values['swimlane_id']; + $values['date_creation'] = time(); + $values['date_modification'] = $values['date_creation']; + $values['date_moved'] = $values['date_creation']; + $values['position'] = $this->taskFinderModel->countByColumnAndSwimlaneId($values['project_id'], $values['column_id'], $values['swimlane_id']) + 1; + } + + /** + * Fire events + * + * @access private + * @param integer $task_id Task id + * @param array $values Form values + */ + private function fireEvents($task_id, array $values) + { + $event = new TaskEvent(array('task_id' => $task_id) + $values); + + $this->logger->debug('Event fired: '.TaskModel::EVENT_CREATE_UPDATE); + $this->logger->debug('Event fired: '.TaskModel::EVENT_CREATE); + + $this->dispatcher->dispatch(TaskModel::EVENT_CREATE_UPDATE, $event); + $this->dispatcher->dispatch(TaskModel::EVENT_CREATE, $event); + + if (! empty($values['description'])) { + $this->userMentionModel->fireEvents($values['description'], TaskModel::EVENT_USER_MENTION, $event); + } + } +} diff --git a/app/Model/TaskDuplication.php b/app/Model/TaskDuplication.php deleted file mode 100644 index 56457a01..00000000 --- a/app/Model/TaskDuplication.php +++ /dev/null @@ -1,270 +0,0 @@ -save($task_id, $this->copyFields($task_id)); - } - - /** - * Duplicate recurring task - * - * @access public - * @param integer $task_id Task id - * @return boolean|integer Recurrence task id - */ - public function duplicateRecurringTask($task_id) - { - $values = $this->copyFields($task_id); - - if ($values['recurrence_status'] == Task::RECURRING_STATUS_PENDING) { - $values['recurrence_parent'] = $task_id; - $values['column_id'] = $this->column->getFirstColumnId($values['project_id']); - $this->calculateRecurringTaskDueDate($values); - - $recurring_task_id = $this->save($task_id, $values); - - if ($recurring_task_id > 0) { - $parent_update = $this->db - ->table(Task::TABLE) - ->eq('id', $task_id) - ->update(array( - 'recurrence_status' => Task::RECURRING_STATUS_PROCESSED, - 'recurrence_child' => $recurring_task_id, - )); - - if ($parent_update) { - return $recurring_task_id; - } - } - } - - return false; - } - - /** - * Duplicate a task to another project - * - * @access public - * @param integer $task_id - * @param integer $project_id - * @param integer $swimlane_id - * @param integer $column_id - * @param integer $category_id - * @param integer $owner_id - * @return boolean|integer - */ - public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) - { - $values = $this->copyFields($task_id); - $values['project_id'] = $project_id; - $values['column_id'] = $column_id !== null ? $column_id : $values['column_id']; - $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id']; - $values['category_id'] = $category_id !== null ? $category_id : $values['category_id']; - $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id']; - - $this->checkDestinationProjectValues($values); - - return $this->save($task_id, $values); - } - - /** - * Move a task to another project - * - * @access public - * @param integer $task_id - * @param integer $project_id - * @param integer $swimlane_id - * @param integer $column_id - * @param integer $category_id - * @param integer $owner_id - * @return boolean - */ - public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) - { - $task = $this->taskFinder->getById($task_id); - - $values = array(); - $values['is_active'] = 1; - $values['project_id'] = $project_id; - $values['column_id'] = $column_id !== null ? $column_id : $task['column_id']; - $values['position'] = $this->taskFinder->countByColumnId($project_id, $values['column_id']) + 1; - $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id']; - $values['category_id'] = $category_id !== null ? $category_id : $task['category_id']; - $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id']; - - $this->checkDestinationProjectValues($values); - - if ($this->db->table(Task::TABLE)->eq('id', $task['id'])->update($values)) { - $this->container['dispatcher']->dispatch( - Task::EVENT_MOVE_PROJECT, - new TaskEvent(array_merge($task, $values, array('task_id' => $task['id']))) - ); - } - - return true; - } - - /** - * Check if the assignee and the category are available in the destination project - * - * @access public - * @param array $values - * @return array - */ - public function checkDestinationProjectValues(array &$values) - { - // Check if the assigned user is allowed for the destination project - if ($values['owner_id'] > 0 && ! $this->projectPermission->isUserAllowed($values['project_id'], $values['owner_id'])) { - $values['owner_id'] = 0; - } - - // Check if the category exists for the destination project - if ($values['category_id'] > 0) { - $values['category_id'] = $this->category->getIdByName( - $values['project_id'], - $this->category->getNameById($values['category_id']) - ); - } - - // Check if the swimlane exists for the destination project - if ($values['swimlane_id'] > 0) { - $values['swimlane_id'] = $this->swimlane->getIdByName( - $values['project_id'], - $this->swimlane->getNameById($values['swimlane_id']) - ); - } - - // Check if the column exists for the destination project - if ($values['column_id'] > 0) { - $values['column_id'] = $this->column->getColumnIdByTitle( - $values['project_id'], - $this->column->getColumnTitleById($values['column_id']) - ); - - $values['column_id'] = $values['column_id'] ?: $this->column->getFirstColumnId($values['project_id']); - } - - return $values; - } - - /** - * Calculate new due date for new recurrence task - * - * @access public - * @param array $values Task fields - */ - public function calculateRecurringTaskDueDate(array &$values) - { - if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) { - if ($values['recurrence_basedate'] == Task::RECURRING_BASEDATE_TRIGGERDATE) { - $values['date_due'] = time(); - } - - $factor = abs($values['recurrence_factor']); - $subtract = $values['recurrence_factor'] < 0; - - switch ($values['recurrence_timeframe']) { - case Task::RECURRING_TIMEFRAME_MONTHS: - $interval = 'P' . $factor . 'M'; - break; - case Task::RECURRING_TIMEFRAME_YEARS: - $interval = 'P' . $factor . 'Y'; - break; - default: - $interval = 'P' . $factor . 'D'; - } - - $date_due = new DateTime(); - $date_due->setTimestamp($values['date_due']); - - $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval)); - - $values['date_due'] = $date_due->getTimestamp(); - } - } - - /** - * Duplicate fields for the new task - * - * @access private - * @param integer $task_id Task id - * @return array - */ - private function copyFields($task_id) - { - $task = $this->taskFinder->getById($task_id); - $values = array(); - - foreach ($this->fields_to_duplicate as $field) { - $values[$field] = $task[$field]; - } - - return $values; - } - - /** - * Create the new task and duplicate subtasks - * - * @access private - * @param integer $task_id Task id - * @param array $values Form values - * @return boolean|integer - */ - private function save($task_id, array $values) - { - $new_task_id = $this->taskCreation->create($values); - - if ($new_task_id) { - $this->subtask->duplicate($task_id, $new_task_id); - } - - return $new_task_id; - } -} diff --git a/app/Model/TaskDuplicationModel.php b/app/Model/TaskDuplicationModel.php new file mode 100644 index 00000000..9a4613e2 --- /dev/null +++ b/app/Model/TaskDuplicationModel.php @@ -0,0 +1,270 @@ +save($task_id, $this->copyFields($task_id)); + } + + /** + * Duplicate recurring task + * + * @access public + * @param integer $task_id Task id + * @return boolean|integer Recurrence task id + */ + public function duplicateRecurringTask($task_id) + { + $values = $this->copyFields($task_id); + + if ($values['recurrence_status'] == TaskModel::RECURRING_STATUS_PENDING) { + $values['recurrence_parent'] = $task_id; + $values['column_id'] = $this->columnModel->getFirstColumnId($values['project_id']); + $this->calculateRecurringTaskDueDate($values); + + $recurring_task_id = $this->save($task_id, $values); + + if ($recurring_task_id > 0) { + $parent_update = $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update(array( + 'recurrence_status' => TaskModel::RECURRING_STATUS_PROCESSED, + 'recurrence_child' => $recurring_task_id, + )); + + if ($parent_update) { + return $recurring_task_id; + } + } + } + + return false; + } + + /** + * Duplicate a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean|integer + */ + public function duplicateToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + $values = $this->copyFields($task_id); + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $values['column_id']; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $values['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $values['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $values['owner_id']; + + $this->checkDestinationProjectValues($values); + + return $this->save($task_id, $values); + } + + /** + * Move a task to another project + * + * @access public + * @param integer $task_id + * @param integer $project_id + * @param integer $swimlane_id + * @param integer $column_id + * @param integer $category_id + * @param integer $owner_id + * @return boolean + */ + public function moveToProject($task_id, $project_id, $swimlane_id = null, $column_id = null, $category_id = null, $owner_id = null) + { + $task = $this->taskFinderModel->getById($task_id); + + $values = array(); + $values['is_active'] = 1; + $values['project_id'] = $project_id; + $values['column_id'] = $column_id !== null ? $column_id : $task['column_id']; + $values['position'] = $this->taskFinderModel->countByColumnId($project_id, $values['column_id']) + 1; + $values['swimlane_id'] = $swimlane_id !== null ? $swimlane_id : $task['swimlane_id']; + $values['category_id'] = $category_id !== null ? $category_id : $task['category_id']; + $values['owner_id'] = $owner_id !== null ? $owner_id : $task['owner_id']; + + $this->checkDestinationProjectValues($values); + + if ($this->db->table(TaskModel::TABLE)->eq('id', $task['id'])->update($values)) { + $this->container['dispatcher']->dispatch( + TaskModel::EVENT_MOVE_PROJECT, + new TaskEvent(array_merge($task, $values, array('task_id' => $task['id']))) + ); + } + + return true; + } + + /** + * Check if the assignee and the category are available in the destination project + * + * @access public + * @param array $values + * @return array + */ + public function checkDestinationProjectValues(array &$values) + { + // Check if the assigned user is allowed for the destination project + if ($values['owner_id'] > 0 && ! $this->projectPermissionModel->isUserAllowed($values['project_id'], $values['owner_id'])) { + $values['owner_id'] = 0; + } + + // Check if the category exists for the destination project + if ($values['category_id'] > 0) { + $values['category_id'] = $this->categoryModel->getIdByName( + $values['project_id'], + $this->categoryModel->getNameById($values['category_id']) + ); + } + + // Check if the swimlane exists for the destination project + if ($values['swimlane_id'] > 0) { + $values['swimlane_id'] = $this->swimlaneModel->getIdByName( + $values['project_id'], + $this->swimlaneModel->getNameById($values['swimlane_id']) + ); + } + + // Check if the column exists for the destination project + if ($values['column_id'] > 0) { + $values['column_id'] = $this->columnModel->getColumnIdByTitle( + $values['project_id'], + $this->columnModel->getColumnTitleById($values['column_id']) + ); + + $values['column_id'] = $values['column_id'] ?: $this->columnModel->getFirstColumnId($values['project_id']); + } + + return $values; + } + + /** + * Calculate new due date for new recurrence task + * + * @access public + * @param array $values Task fields + */ + public function calculateRecurringTaskDueDate(array &$values) + { + if (! empty($values['date_due']) && $values['recurrence_factor'] != 0) { + if ($values['recurrence_basedate'] == TaskModel::RECURRING_BASEDATE_TRIGGERDATE) { + $values['date_due'] = time(); + } + + $factor = abs($values['recurrence_factor']); + $subtract = $values['recurrence_factor'] < 0; + + switch ($values['recurrence_timeframe']) { + case TaskModel::RECURRING_TIMEFRAME_MONTHS: + $interval = 'P' . $factor . 'M'; + break; + case TaskModel::RECURRING_TIMEFRAME_YEARS: + $interval = 'P' . $factor . 'Y'; + break; + default: + $interval = 'P' . $factor . 'D'; + } + + $date_due = new DateTime(); + $date_due->setTimestamp($values['date_due']); + + $subtract ? $date_due->sub(new DateInterval($interval)) : $date_due->add(new DateInterval($interval)); + + $values['date_due'] = $date_due->getTimestamp(); + } + } + + /** + * Duplicate fields for the new task + * + * @access private + * @param integer $task_id Task id + * @return array + */ + private function copyFields($task_id) + { + $task = $this->taskFinderModel->getById($task_id); + $values = array(); + + foreach ($this->fields_to_duplicate as $field) { + $values[$field] = $task[$field]; + } + + return $values; + } + + /** + * Create the new task and duplicate subtasks + * + * @access private + * @param integer $task_id Task id + * @param array $values Form values + * @return boolean|integer + */ + private function save($task_id, array $values) + { + $new_task_id = $this->taskCreationModel->create($values); + + if ($new_task_id) { + $this->subtaskModel->duplicate($task_id, $new_task_id); + } + + return $new_task_id; + } +} diff --git a/app/Model/TaskExternalLink.php b/app/Model/TaskExternalLink.php deleted file mode 100644 index f77a72bf..00000000 --- a/app/Model/TaskExternalLink.php +++ /dev/null @@ -1,101 +0,0 @@ -externalLinkManager->getTypes(); - - $links = $this->db->table(self::TABLE) - ->columns(self::TABLE.'.*', User::TABLE.'.name AS creator_name', User::TABLE.'.username AS creator_username') - ->eq('task_id', $task_id) - ->asc('title') - ->join(User::TABLE, 'id', 'creator_id') - ->findAll(); - - foreach ($links as &$link) { - $link['dependency_label'] = $this->externalLinkManager->getDependencyLabel($link['link_type'], $link['dependency']); - $link['type'] = isset($types[$link['link_type']]) ? $types[$link['link_type']] : t('Unknown'); - } - - return $links; - } - - /** - * Get link - * - * @access public - * @param integer $link_id - * @return array - */ - public function getById($link_id) - { - return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne(); - } - - /** - * Add a new link in the database - * - * @access public - * @param array $values Form values - * @return boolean|integer - */ - public function create(array $values) - { - unset($values['id']); - $values['creator_id'] = $this->userSession->getId(); - $values['date_creation'] = time(); - $values['date_modification'] = $values['date_creation']; - - return $this->db->table(self::TABLE)->persist($values); - } - - /** - * Modify external link - * - * @access public - * @param array $values Form values - * @return boolean - */ - public function update(array $values) - { - $values['date_modification'] = time(); - return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); - } - - /** - * Remove a link - * - * @access public - * @param integer $link_id - * @return boolean - */ - public function remove($link_id) - { - return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); - } -} diff --git a/app/Model/TaskExternalLinkModel.php b/app/Model/TaskExternalLinkModel.php new file mode 100644 index 00000000..220b9c6f --- /dev/null +++ b/app/Model/TaskExternalLinkModel.php @@ -0,0 +1,101 @@ +externalLinkManager->getTypes(); + + $links = $this->db->table(self::TABLE) + ->columns(self::TABLE.'.*', UserModel::TABLE.'.name AS creator_name', UserModel::TABLE.'.username AS creator_username') + ->eq('task_id', $task_id) + ->asc('title') + ->join(UserModel::TABLE, 'id', 'creator_id') + ->findAll(); + + foreach ($links as &$link) { + $link['dependency_label'] = $this->externalLinkManager->getDependencyLabel($link['link_type'], $link['dependency']); + $link['type'] = isset($types[$link['link_type']]) ? $types[$link['link_type']] : t('Unknown'); + } + + return $links; + } + + /** + * Get link + * + * @access public + * @param integer $link_id + * @return array + */ + public function getById($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->findOne(); + } + + /** + * Add a new link in the database + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + unset($values['id']); + $values['creator_id'] = $this->userSession->getId(); + $values['date_creation'] = time(); + $values['date_modification'] = $values['date_creation']; + + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Modify external link + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $values['date_modification'] = time(); + return $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); + } + + /** + * Remove a link + * + * @access public + * @param integer $link_id + * @return boolean + */ + public function remove($link_id) + { + return $this->db->table(self::TABLE)->eq('id', $link_id)->remove(); + } +} diff --git a/app/Model/TaskFile.php b/app/Model/TaskFile.php deleted file mode 100644 index 45a3b97f..00000000 --- a/app/Model/TaskFile.php +++ /dev/null @@ -1,54 +0,0 @@ -helper->dt->datetime(time())).'.png'; - return $this->uploadContent($task_id, $original_filename, $blob); - } -} diff --git a/app/Model/TaskFileModel.php b/app/Model/TaskFileModel.php new file mode 100644 index 00000000..21d319b0 --- /dev/null +++ b/app/Model/TaskFileModel.php @@ -0,0 +1,54 @@ +helper->dt->datetime(time())).'.png'; + return $this->uploadContent($task_id, $original_filename, $blob); + } +} diff --git a/app/Model/TaskFinder.php b/app/Model/TaskFinder.php deleted file mode 100644 index fed3a580..00000000 --- a/app/Model/TaskFinder.php +++ /dev/null @@ -1,476 +0,0 @@ -db - ->table(Task::TABLE) - ->columns( - Task::TABLE.'.id', - Task::TABLE.'.title', - Task::TABLE.'.date_due', - Task::TABLE.'.date_started', - Task::TABLE.'.project_id', - Task::TABLE.'.color_id', - Task::TABLE.'.priority', - Task::TABLE.'.time_spent', - Task::TABLE.'.time_estimated', - Project::TABLE.'.name AS project_name', - Column::TABLE.'.title AS column_name', - User::TABLE.'.username AS assignee_username', - User::TABLE.'.name AS assignee_name' - ) - ->eq(Task::TABLE.'.is_active', $is_active) - ->in(Project::TABLE.'.id', $project_ids) - ->join(Project::TABLE, 'id', 'project_id') - ->join(Column::TABLE, 'id', 'column_id', Task::TABLE) - ->join(User::TABLE, 'id', 'owner_id', Task::TABLE); - } - - /** - * Get query for assigned user tasks - * - * @access public - * @param integer $user_id User id - * @return \PicoDb\Table - */ - public function getUserQuery($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', - 'tasks.priority', - 'tasks.time_spent', - 'tasks.time_estimated', - 'tasks.is_active', - 'tasks.creator_id', - 'projects.name AS project_name', - 'columns.title AS column_title' - ) - ->join(Project::TABLE, 'id', 'project_id') - ->join(Column::TABLE, 'id', 'column_id') - ->eq(Task::TABLE.'.owner_id', $user_id) - ->eq(Task::TABLE.'.is_active', Task::STATUS_OPEN) - ->eq(Project::TABLE.'.is_active', Project::ACTIVE); - } - - /** - * Extended query - * - * @access public - * @return \PicoDb\Table - */ - public function getExtendedQuery() - { - return $this->db - ->table(Task::TABLE) - ->columns( - '(SELECT COUNT(*) FROM '.Comment::TABLE.' WHERE task_id=tasks.id) AS nb_comments', - '(SELECT COUNT(*) FROM '.TaskFile::TABLE.' WHERE task_id=tasks.id) AS nb_files', - '(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id) AS nb_subtasks', - '(SELECT COUNT(*) FROM '.Subtask::TABLE.' WHERE '.Subtask::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', - '(SELECT COUNT(*) FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id) AS nb_links', - '(SELECT COUNT(*) FROM '.TaskExternalLink::TABLE.' WHERE '.TaskExternalLink::TABLE.'.task_id = tasks.id) AS nb_external_links', - '(SELECT DISTINCT 1 FROM '.TaskLink::TABLE.' WHERE '.TaskLink::TABLE.'.task_id = tasks.id AND '.TaskLink::TABLE.'.link_id = 9) AS is_milestone', - 'tasks.id', - 'tasks.reference', - 'tasks.title', - 'tasks.description', - 'tasks.date_creation', - 'tasks.date_modification', - 'tasks.date_completed', - 'tasks.date_started', - 'tasks.date_due', - 'tasks.color_id', - 'tasks.project_id', - 'tasks.column_id', - 'tasks.swimlane_id', - 'tasks.owner_id', - 'tasks.creator_id', - 'tasks.position', - 'tasks.is_active', - 'tasks.score', - 'tasks.category_id', - 'tasks.priority', - 'tasks.date_moved', - 'tasks.recurrence_status', - 'tasks.recurrence_trigger', - 'tasks.recurrence_factor', - 'tasks.recurrence_timeframe', - 'tasks.recurrence_basedate', - 'tasks.recurrence_parent', - 'tasks.recurrence_child', - 'tasks.time_estimated', - 'tasks.time_spent', - User::TABLE.'.username AS assignee_username', - User::TABLE.'.name AS assignee_name', - User::TABLE.'.email AS assignee_email', - User::TABLE.'.avatar_path AS assignee_avatar_path', - Category::TABLE.'.name AS category_name', - Category::TABLE.'.description AS category_description', - Column::TABLE.'.title AS column_name', - Column::TABLE.'.position AS column_position', - Swimlane::TABLE.'.name AS swimlane_name', - Project::TABLE.'.default_swimlane', - Project::TABLE.'.name AS project_name' - ) - ->join(User::TABLE, 'id', 'owner_id', Task::TABLE) - ->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id') - ->join(Category::TABLE, 'id', 'category_id', Task::TABLE) - ->join(Column::TABLE, 'id', 'column_id', Task::TABLE) - ->join(Swimlane::TABLE, 'id', 'swimlane_id', Task::TABLE) - ->join(Project::TABLE, 'id', 'project_id', Task::TABLE); - } - - /** - * Get all tasks shown on the board (sorted by position) - * - * @access public - * @param integer $project_id Project id - * @param integer $column_id Column id - * @param integer $swimlane_id Swimlane id - * @return array - */ - public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0) - { - return $this->getExtendedQuery() - ->eq(Task::TABLE.'.project_id', $project_id) - ->eq(Task::TABLE.'.column_id', $column_id) - ->eq(Task::TABLE.'.swimlane_id', $swimlane_id) - ->eq(Task::TABLE.'.is_active', Task::STATUS_OPEN) - ->asc(Task::TABLE.'.position') - ->findAll(); - } - - /** - * Get all tasks for a given project and status - * - * @access public - * @param integer $project_id Project id - * @param integer $status_id Status id - * @return array - */ - public function getAll($project_id, $status_id = Task::STATUS_OPEN) - { - return $this->db - ->table(Task::TABLE) - ->eq(Task::TABLE.'.project_id', $project_id) - ->eq(Task::TABLE.'.is_active', $status_id) - ->findAll(); - } - - /** - * Get all tasks for a given project and status - * - * @access public - * @param integer $project_id - * @param array $status - * @return array - */ - public function getAllIds($project_id, array $status = array(Task::STATUS_OPEN)) - { - return $this->db - ->table(Task::TABLE) - ->eq(Task::TABLE.'.project_id', $project_id) - ->in(Task::TABLE.'.is_active', $status) - ->findAllByColumn('id'); - } - - /** - * Get overdue tasks query - * - * @access public - * @return \PicoDb\Table - */ - public function getOverdueTasksQuery() - { - return $this->db->table(Task::TABLE) - ->columns( - Task::TABLE.'.id', - Task::TABLE.'.title', - Task::TABLE.'.date_due', - Task::TABLE.'.project_id', - Task::TABLE.'.creator_id', - Task::TABLE.'.owner_id', - Project::TABLE.'.name AS project_name', - User::TABLE.'.username AS assignee_username', - User::TABLE.'.name AS assignee_name' - ) - ->join(Project::TABLE, 'id', 'project_id') - ->join(User::TABLE, 'id', 'owner_id') - ->eq(Project::TABLE.'.is_active', 1) - ->eq(Task::TABLE.'.is_active', 1) - ->neq(Task::TABLE.'.date_due', 0) - ->lte(Task::TABLE.'.date_due', mktime(23, 59, 59)); - } - - /** - * Get a list of overdue tasks for all projects - * - * @access public - * @return array - */ - public function getOverdueTasks() - { - return $this->getOverdueTasksQuery()->findAll(); - } - - /** - * Get a list of overdue tasks by project - * - * @access public - * @param integer $project_id - * @return array - */ - public function getOverdueTasksByProject($project_id) - { - return $this->getOverdueTasksQuery()->eq(Task::TABLE.'.project_id', $project_id)->findAll(); - } - - /** - * Get a list of overdue tasks by user - * - * @access public - * @param integer $user_id - * @return array - */ - public function getOverdueTasksByUser($user_id) - { - return $this->getOverdueTasksQuery()->eq(Task::TABLE.'.owner_id', $user_id)->findAll(); - } - - /** - * Get project id for a given task - * - * @access public - * @param integer $task_id Task id - * @return integer - */ - public function getProjectId($task_id) - { - return (int) $this->db->table(Task::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0; - } - - /** - * Fetch a task by the id - * - * @access public - * @param integer $task_id Task id - * @return array - */ - public function getById($task_id) - { - return $this->db->table(Task::TABLE)->eq('id', $task_id)->findOne(); - } - - /** - * Fetch a task by the reference (external id) - * - * @access public - * @param integer $project_id Project id - * @param string $reference Task reference - * @return array - */ - public function getByReference($project_id, $reference) - { - return $this->db->table(Task::TABLE)->eq('project_id', $project_id)->eq('reference', $reference)->findOne(); - } - - /** - * Get task details (fetch more information from other tables) - * - * @access public - * @param integer $task_id Task id - * @return array - */ - public function getDetails($task_id) - { - $sql = ' - SELECT - tasks.id, - tasks.reference, - tasks.title, - tasks.description, - tasks.date_creation, - tasks.date_completed, - tasks.date_modification, - tasks.date_due, - tasks.date_started, - tasks.time_estimated, - tasks.time_spent, - tasks.color_id, - tasks.project_id, - tasks.column_id, - tasks.owner_id, - tasks.creator_id, - tasks.position, - tasks.is_active, - tasks.score, - tasks.category_id, - tasks.priority, - tasks.swimlane_id, - tasks.date_moved, - tasks.recurrence_status, - tasks.recurrence_trigger, - tasks.recurrence_factor, - tasks.recurrence_timeframe, - tasks.recurrence_basedate, - tasks.recurrence_parent, - tasks.recurrence_child, - project_has_categories.name AS category_name, - swimlanes.name AS swimlane_name, - projects.name AS project_name, - projects.default_swimlane, - columns.title AS column_title, - users.username AS assignee_username, - users.name AS assignee_name, - creators.username AS creator_username, - creators.name AS creator_name - FROM tasks - LEFT JOIN users ON users.id = tasks.owner_id - LEFT JOIN users AS creators ON creators.id = tasks.creator_id - LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id - LEFT JOIN projects ON projects.id = tasks.project_id - LEFT JOIN columns ON columns.id = tasks.column_id - LEFT JOIN swimlanes ON swimlanes.id = tasks.swimlane_id - WHERE tasks.id = ? - '; - - $rq = $this->db->execute($sql, array($task_id)); - return $rq->fetch(PDO::FETCH_ASSOC); - } - - /** - * Get iCal query - * - * @access public - * @return \PicoDb\Table - */ - public function getICalQuery() - { - return $this->db->table(Task::TABLE) - ->left(User::TABLE, 'ua', 'id', Task::TABLE, 'owner_id') - ->left(User::TABLE, 'uc', 'id', Task::TABLE, 'creator_id') - ->columns( - Task::TABLE.'.*', - 'ua.email AS assignee_email', - 'ua.name AS assignee_name', - 'ua.username AS assignee_username', - 'uc.email AS creator_email', - 'uc.username AS creator_username' - ); - } - - /** - * 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(Task::STATUS_OPEN, Task::STATUS_CLOSED)) - { - return $this->db - ->table(Task::TABLE) - ->eq('project_id', $project_id) - ->in('is_active', $status) - ->count(); - } - - /** - * 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 - * @return integer - */ - public function countByColumnId($project_id, $column_id) - { - return $this->db - ->table(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->eq('is_active', 1) - ->count(); - } - - /** - * Count the number of tasks for a given column and swimlane - * - * @access public - * @param integer $project_id Project id - * @param integer $column_id Column id - * @param integer $swimlane_id Swimlane id - * @return integer - */ - public function countByColumnAndSwimlaneId($project_id, $column_id, $swimlane_id) - { - return $this->db - ->table(Task::TABLE) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->eq('swimlane_id', $swimlane_id) - ->eq('is_active', 1) - ->count(); - } - - /** - * Return true if the task exists - * - * @access public - * @param integer $task_id Task id - * @return boolean - */ - public function exists($task_id) - { - return $this->db->table(Task::TABLE)->eq('id', $task_id)->exists(); - } - - /** - * Get project token - * - * @access public - * @param integer $task_id - * @return string - */ - public function getProjectToken($task_id) - { - return $this->db - ->table(Task::TABLE) - ->eq(Task::TABLE.'.id', $task_id) - ->join(Project::TABLE, 'id', 'project_id') - ->findOneColumn(Project::TABLE.'.token'); - } -} diff --git a/app/Model/TaskFinderModel.php b/app/Model/TaskFinderModel.php new file mode 100644 index 00000000..8b636e28 --- /dev/null +++ b/app/Model/TaskFinderModel.php @@ -0,0 +1,476 @@ +db + ->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.date_started', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.priority', + TaskModel::TABLE.'.time_spent', + TaskModel::TABLE.'.time_estimated', + ProjectModel::TABLE.'.name AS project_name', + ColumnModel::TABLE.'.title AS column_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->eq(TaskModel::TABLE.'.is_active', $is_active) + ->in(ProjectModel::TABLE.'.id', $project_ids) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE); + } + + /** + * Get query for assigned user tasks + * + * @access public + * @param integer $user_id User id + * @return \PicoDb\Table + */ + public function getUserQuery($user_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->columns( + 'tasks.id', + 'tasks.title', + 'tasks.date_due', + 'tasks.date_creation', + 'tasks.project_id', + 'tasks.color_id', + 'tasks.priority', + 'tasks.time_spent', + 'tasks.time_estimated', + 'tasks.is_active', + 'tasks.creator_id', + 'projects.name AS project_name', + 'columns.title AS column_title' + ) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(ColumnModel::TABLE, 'id', 'column_id') + ->eq(TaskModel::TABLE.'.owner_id', $user_id) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->eq(ProjectModel::TABLE.'.is_active', ProjectModel::ACTIVE); + } + + /** + * Extended query + * + * @access public + * @return \PicoDb\Table + */ + public function getExtendedQuery() + { + return $this->db + ->table(TaskModel::TABLE) + ->columns( + '(SELECT COUNT(*) FROM '.CommentModel::TABLE.' WHERE task_id=tasks.id) AS nb_comments', + '(SELECT COUNT(*) FROM '.TaskFileModel::TABLE.' WHERE task_id=tasks.id) AS nb_files', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id) AS nb_subtasks', + '(SELECT COUNT(*) FROM '.SubtaskModel::TABLE.' WHERE '.SubtaskModel::TABLE.'.task_id=tasks.id AND status=2) AS nb_completed_subtasks', + '(SELECT COUNT(*) FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id) AS nb_links', + '(SELECT COUNT(*) FROM '.TaskExternalLinkModel::TABLE.' WHERE '.TaskExternalLinkModel::TABLE.'.task_id = tasks.id) AS nb_external_links', + '(SELECT DISTINCT 1 FROM '.TaskLinkModel::TABLE.' WHERE '.TaskLinkModel::TABLE.'.task_id = tasks.id AND '.TaskLinkModel::TABLE.'.link_id = 9) AS is_milestone', + 'tasks.id', + 'tasks.reference', + 'tasks.title', + 'tasks.description', + 'tasks.date_creation', + 'tasks.date_modification', + 'tasks.date_completed', + 'tasks.date_started', + 'tasks.date_due', + 'tasks.color_id', + 'tasks.project_id', + 'tasks.column_id', + 'tasks.swimlane_id', + 'tasks.owner_id', + 'tasks.creator_id', + 'tasks.position', + 'tasks.is_active', + 'tasks.score', + 'tasks.category_id', + 'tasks.priority', + 'tasks.date_moved', + 'tasks.recurrence_status', + 'tasks.recurrence_trigger', + 'tasks.recurrence_factor', + 'tasks.recurrence_timeframe', + 'tasks.recurrence_basedate', + 'tasks.recurrence_parent', + 'tasks.recurrence_child', + 'tasks.time_estimated', + 'tasks.time_spent', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name', + UserModel::TABLE.'.email AS assignee_email', + UserModel::TABLE.'.avatar_path AS assignee_avatar_path', + CategoryModel::TABLE.'.name AS category_name', + CategoryModel::TABLE.'.description AS category_description', + ColumnModel::TABLE.'.title AS column_name', + ColumnModel::TABLE.'.position AS column_position', + SwimlaneModel::TABLE.'.name AS swimlane_name', + ProjectModel::TABLE.'.default_swimlane', + ProjectModel::TABLE.'.name AS project_name' + ) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->join(CategoryModel::TABLE, 'id', 'category_id', TaskModel::TABLE) + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(SwimlaneModel::TABLE, 'id', 'swimlane_id', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE); + } + + /** + * Get all tasks shown on the board (sorted by position) + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param integer $swimlane_id Swimlane id + * @return array + */ + public function getTasksByColumnAndSwimlane($project_id, $column_id, $swimlane_id = 0) + { + return $this->getExtendedQuery() + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->eq(TaskModel::TABLE.'.column_id', $column_id) + ->eq(TaskModel::TABLE.'.swimlane_id', $swimlane_id) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->asc(TaskModel::TABLE.'.position') + ->findAll(); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id Project id + * @param integer $status_id Status id + * @return array + */ + public function getAll($project_id, $status_id = TaskModel::STATUS_OPEN) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->eq(TaskModel::TABLE.'.is_active', $status_id) + ->findAll(); + } + + /** + * Get all tasks for a given project and status + * + * @access public + * @param integer $project_id + * @param array $status + * @return array + */ + public function getAllIds($project_id, array $status = array(TaskModel::STATUS_OPEN)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.project_id', $project_id) + ->in(TaskModel::TABLE.'.is_active', $status) + ->findAllByColumn('id'); + } + + /** + * Get overdue tasks query + * + * @access public + * @return \PicoDb\Table + */ + public function getOverdueTasksQuery() + { + return $this->db->table(TaskModel::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.date_due', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.creator_id', + TaskModel::TABLE.'.owner_id', + ProjectModel::TABLE.'.name AS project_name', + UserModel::TABLE.'.username AS assignee_username', + UserModel::TABLE.'.name AS assignee_name' + ) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->join(UserModel::TABLE, 'id', 'owner_id') + ->eq(ProjectModel::TABLE.'.is_active', 1) + ->eq(TaskModel::TABLE.'.is_active', 1) + ->neq(TaskModel::TABLE.'.date_due', 0) + ->lte(TaskModel::TABLE.'.date_due', mktime(23, 59, 59)); + } + + /** + * Get a list of overdue tasks for all projects + * + * @access public + * @return array + */ + public function getOverdueTasks() + { + return $this->getOverdueTasksQuery()->findAll(); + } + + /** + * Get a list of overdue tasks by project + * + * @access public + * @param integer $project_id + * @return array + */ + public function getOverdueTasksByProject($project_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.project_id', $project_id)->findAll(); + } + + /** + * Get a list of overdue tasks by user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getOverdueTasksByUser($user_id) + { + return $this->getOverdueTasksQuery()->eq(TaskModel::TABLE.'.owner_id', $user_id)->findAll(); + } + + /** + * Get project id for a given task + * + * @access public + * @param integer $task_id Task id + * @return integer + */ + public function getProjectId($task_id) + { + return (int) $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOneColumn('project_id') ?: 0; + } + + /** + * Fetch a task by the id + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getById($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->findOne(); + } + + /** + * Fetch a task by the reference (external id) + * + * @access public + * @param integer $project_id Project id + * @param string $reference Task reference + * @return array + */ + public function getByReference($project_id, $reference) + { + return $this->db->table(TaskModel::TABLE)->eq('project_id', $project_id)->eq('reference', $reference)->findOne(); + } + + /** + * Get task details (fetch more information from other tables) + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getDetails($task_id) + { + $sql = ' + SELECT + tasks.id, + tasks.reference, + tasks.title, + tasks.description, + tasks.date_creation, + tasks.date_completed, + tasks.date_modification, + tasks.date_due, + tasks.date_started, + tasks.time_estimated, + tasks.time_spent, + tasks.color_id, + tasks.project_id, + tasks.column_id, + tasks.owner_id, + tasks.creator_id, + tasks.position, + tasks.is_active, + tasks.score, + tasks.category_id, + tasks.priority, + tasks.swimlane_id, + tasks.date_moved, + tasks.recurrence_status, + tasks.recurrence_trigger, + tasks.recurrence_factor, + tasks.recurrence_timeframe, + tasks.recurrence_basedate, + tasks.recurrence_parent, + tasks.recurrence_child, + project_has_categories.name AS category_name, + swimlanes.name AS swimlane_name, + projects.name AS project_name, + projects.default_swimlane, + columns.title AS column_title, + users.username AS assignee_username, + users.name AS assignee_name, + creators.username AS creator_username, + creators.name AS creator_name + FROM tasks + LEFT JOIN users ON users.id = tasks.owner_id + LEFT JOIN users AS creators ON creators.id = tasks.creator_id + LEFT JOIN project_has_categories ON project_has_categories.id = tasks.category_id + LEFT JOIN projects ON projects.id = tasks.project_id + LEFT JOIN columns ON columns.id = tasks.column_id + LEFT JOIN swimlanes ON swimlanes.id = tasks.swimlane_id + WHERE tasks.id = ? + '; + + $rq = $this->db->execute($sql, array($task_id)); + return $rq->fetch(PDO::FETCH_ASSOC); + } + + /** + * Get iCal query + * + * @access public + * @return \PicoDb\Table + */ + public function getICalQuery() + { + return $this->db->table(TaskModel::TABLE) + ->left(UserModel::TABLE, 'ua', 'id', TaskModel::TABLE, 'owner_id') + ->left(UserModel::TABLE, 'uc', 'id', TaskModel::TABLE, 'creator_id') + ->columns( + TaskModel::TABLE.'.*', + 'ua.email AS assignee_email', + 'ua.name AS assignee_name', + 'ua.username AS assignee_username', + 'uc.email AS creator_email', + 'uc.username AS creator_username' + ); + } + + /** + * 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(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->in('is_active', $status) + ->count(); + } + + /** + * 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 + * @return integer + */ + public function countByColumnId($project_id, $column_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('is_active', 1) + ->count(); + } + + /** + * Count the number of tasks for a given column and swimlane + * + * @access public + * @param integer $project_id Project id + * @param integer $column_id Column id + * @param integer $swimlane_id Swimlane id + * @return integer + */ + public function countByColumnAndSwimlaneId($project_id, $column_id, $swimlane_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->eq('swimlane_id', $swimlane_id) + ->eq('is_active', 1) + ->count(); + } + + /** + * Return true if the task exists + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function exists($task_id) + { + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->exists(); + } + + /** + * Get project token + * + * @access public + * @param integer $task_id + * @return string + */ + public function getProjectToken($task_id) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq(TaskModel::TABLE.'.id', $task_id) + ->join(ProjectModel::TABLE, 'id', 'project_id') + ->findOneColumn(ProjectModel::TABLE.'.token'); + } +} diff --git a/app/Model/TaskLink.php b/app/Model/TaskLink.php deleted file mode 100644 index 9c5b3ca3..00000000 --- a/app/Model/TaskLink.php +++ /dev/null @@ -1,265 +0,0 @@ -db->table(self::TABLE)->eq('id', $task_link_id)->findOne(); - } - - /** - * Get the opposite task link (use the unique index task_has_links_unique) - * - * @access public - * @param array $task_link - * @return array - */ - public function getOppositeTaskLink(array $task_link) - { - $opposite_link_id = $this->link->getOppositeLinkId($task_link['link_id']); - - return $this->db->table(self::TABLE) - ->eq('opposite_task_id', $task_link['task_id']) - ->eq('task_id', $task_link['opposite_task_id']) - ->eq('link_id', $opposite_link_id) - ->findOne(); - } - - /** - * Get all links attached to a 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.'.opposite_task_id AS task_id', - Link::TABLE.'.label', - Task::TABLE.'.title', - Task::TABLE.'.is_active', - Task::TABLE.'.project_id', - Task::TABLE.'.column_id', - Task::TABLE.'.color_id', - Task::TABLE.'.time_spent AS task_time_spent', - Task::TABLE.'.time_estimated AS task_time_estimated', - Task::TABLE.'.owner_id AS task_assignee_id', - User::TABLE.'.username AS task_assignee_username', - User::TABLE.'.name AS task_assignee_name', - Column::TABLE.'.title AS column_title', - Project::TABLE.'.name AS project_name' - ) - ->eq(self::TABLE.'.task_id', $task_id) - ->join(Link::TABLE, 'id', 'link_id') - ->join(Task::TABLE, 'id', 'opposite_task_id') - ->join(Column::TABLE, 'id', 'column_id', Task::TABLE) - ->join(User::TABLE, 'id', 'owner_id', Task::TABLE) - ->join(Project::TABLE, 'id', 'project_id', Task::TABLE) - ->asc(Link::TABLE.'.id') - ->desc(Column::TABLE.'.position') - ->desc(Task::TABLE.'.is_active') - ->asc(Task::TABLE.'.position') - ->asc(Task::TABLE.'.id') - ->findAll(); - } - - /** - * Get all links attached to a task grouped by label - * - * @access public - * @param integer $task_id Task id - * @return array - */ - public function getAllGroupedByLabel($task_id) - { - $links = $this->getAll($task_id); - $result = array(); - - foreach ($links as $link) { - if (! isset($result[$link['label']])) { - $result[$link['label']] = array(); - } - - $result[$link['label']][] = $link; - } - - return $result; - } - - /** - * Publish events - * - * @access private - * @param array $events - */ - private function fireEvents(array $events) - { - foreach ($events as $event) { - $event['project_id'] = $this->taskFinder->getProjectId($event['task_id']); - $this->container['dispatcher']->dispatch(self::EVENT_CREATE_UPDATE, new TaskLinkEvent($event)); - } - } - - /** - * Create a new link - * - * @access public - * @param integer $task_id Task id - * @param integer $opposite_task_id Opposite task id - * @param integer $link_id Link id - * @return integer Task link id - */ - public function create($task_id, $opposite_task_id, $link_id) - { - $events = array(); - $this->db->startTransaction(); - - // Get opposite link - $opposite_link_id = $this->link->getOppositeLinkId($link_id); - - $values = array( - 'task_id' => $task_id, - 'opposite_task_id' => $opposite_task_id, - 'link_id' => $link_id, - ); - - // Create the original task link - $this->db->table(self::TABLE)->insert($values); - $task_link_id = $this->db->getLastId(); - $events[] = $values; - - // Create the opposite task link - $values = array( - 'task_id' => $opposite_task_id, - 'opposite_task_id' => $task_id, - 'link_id' => $opposite_link_id, - ); - - $this->db->table(self::TABLE)->insert($values); - $events[] = $values; - - $this->db->closeTransaction(); - - $this->fireEvents($events); - - return (int) $task_link_id; - } - - /** - * Update a task link - * - * @access public - * @param integer $task_link_id Task link id - * @param integer $task_id Task id - * @param integer $opposite_task_id Opposite task id - * @param integer $link_id Link id - * @return boolean - */ - public function update($task_link_id, $task_id, $opposite_task_id, $link_id) - { - $events = array(); - $this->db->startTransaction(); - - // Get original task link - $task_link = $this->getById($task_link_id); - - // Find opposite task link - $opposite_task_link = $this->getOppositeTaskLink($task_link); - - // Get opposite link - $opposite_link_id = $this->link->getOppositeLinkId($link_id); - - // Update the original task link - $values = array( - 'task_id' => $task_id, - 'opposite_task_id' => $opposite_task_id, - 'link_id' => $link_id, - ); - - $rs1 = $this->db->table(self::TABLE)->eq('id', $task_link_id)->update($values); - $events[] = $values; - - // Update the opposite link - $values = array( - 'task_id' => $opposite_task_id, - 'opposite_task_id' => $task_id, - 'link_id' => $opposite_link_id, - ); - - $rs2 = $this->db->table(self::TABLE)->eq('id', $opposite_task_link['id'])->update($values); - $events[] = $values; - - $this->db->closeTransaction(); - - if ($rs1 && $rs2) { - $this->fireEvents($events); - return true; - } - - return false; - } - - /** - * Remove a link between two tasks - * - * @access public - * @param integer $task_link_id - * @return boolean - */ - public function remove($task_link_id) - { - $this->db->startTransaction(); - - $link = $this->getById($task_link_id); - $link_id = $this->link->getOppositeLinkId($link['link_id']); - - $this->db->table(self::TABLE)->eq('id', $task_link_id)->remove(); - - $this->db - ->table(self::TABLE) - ->eq('opposite_task_id', $link['task_id']) - ->eq('task_id', $link['opposite_task_id']) - ->eq('link_id', $link_id)->remove(); - - $this->db->closeTransaction(); - - return true; - } -} diff --git a/app/Model/TaskLinkModel.php b/app/Model/TaskLinkModel.php new file mode 100644 index 00000000..45225e35 --- /dev/null +++ b/app/Model/TaskLinkModel.php @@ -0,0 +1,265 @@ +db->table(self::TABLE)->eq('id', $task_link_id)->findOne(); + } + + /** + * Get the opposite task link (use the unique index task_has_links_unique) + * + * @access public + * @param array $task_link + * @return array + */ + public function getOppositeTaskLink(array $task_link) + { + $opposite_link_id = $this->linkModel->getOppositeLinkId($task_link['link_id']); + + return $this->db->table(self::TABLE) + ->eq('opposite_task_id', $task_link['task_id']) + ->eq('task_id', $task_link['opposite_task_id']) + ->eq('link_id', $opposite_link_id) + ->findOne(); + } + + /** + * Get all links attached to a 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.'.opposite_task_id AS task_id', + LinkModel::TABLE.'.label', + TaskModel::TABLE.'.title', + TaskModel::TABLE.'.is_active', + TaskModel::TABLE.'.project_id', + TaskModel::TABLE.'.column_id', + TaskModel::TABLE.'.color_id', + TaskModel::TABLE.'.time_spent AS task_time_spent', + TaskModel::TABLE.'.time_estimated AS task_time_estimated', + TaskModel::TABLE.'.owner_id AS task_assignee_id', + UserModel::TABLE.'.username AS task_assignee_username', + UserModel::TABLE.'.name AS task_assignee_name', + ColumnModel::TABLE.'.title AS column_title', + ProjectModel::TABLE.'.name AS project_name' + ) + ->eq(self::TABLE.'.task_id', $task_id) + ->join(LinkModel::TABLE, 'id', 'link_id') + ->join(TaskModel::TABLE, 'id', 'opposite_task_id') + ->join(ColumnModel::TABLE, 'id', 'column_id', TaskModel::TABLE) + ->join(UserModel::TABLE, 'id', 'owner_id', TaskModel::TABLE) + ->join(ProjectModel::TABLE, 'id', 'project_id', TaskModel::TABLE) + ->asc(LinkModel::TABLE.'.id') + ->desc(ColumnModel::TABLE.'.position') + ->desc(TaskModel::TABLE.'.is_active') + ->asc(TaskModel::TABLE.'.position') + ->asc(TaskModel::TABLE.'.id') + ->findAll(); + } + + /** + * Get all links attached to a task grouped by label + * + * @access public + * @param integer $task_id Task id + * @return array + */ + public function getAllGroupedByLabel($task_id) + { + $links = $this->getAll($task_id); + $result = array(); + + foreach ($links as $link) { + if (! isset($result[$link['label']])) { + $result[$link['label']] = array(); + } + + $result[$link['label']][] = $link; + } + + return $result; + } + + /** + * Publish events + * + * @access private + * @param array $events + */ + private function fireEvents(array $events) + { + foreach ($events as $event) { + $event['project_id'] = $this->taskFinderModel->getProjectId($event['task_id']); + $this->container['dispatcher']->dispatch(self::EVENT_CREATE_UPDATE, new TaskLinkEvent($event)); + } + } + + /** + * Create a new link + * + * @access public + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return integer Task link id + */ + public function create($task_id, $opposite_task_id, $link_id) + { + $events = array(); + $this->db->startTransaction(); + + // Get opposite link + $opposite_link_id = $this->linkModel->getOppositeLinkId($link_id); + + $values = array( + 'task_id' => $task_id, + 'opposite_task_id' => $opposite_task_id, + 'link_id' => $link_id, + ); + + // Create the original task link + $this->db->table(self::TABLE)->insert($values); + $task_link_id = $this->db->getLastId(); + $events[] = $values; + + // Create the opposite task link + $values = array( + 'task_id' => $opposite_task_id, + 'opposite_task_id' => $task_id, + 'link_id' => $opposite_link_id, + ); + + $this->db->table(self::TABLE)->insert($values); + $events[] = $values; + + $this->db->closeTransaction(); + + $this->fireEvents($events); + + return (int) $task_link_id; + } + + /** + * Update a task link + * + * @access public + * @param integer $task_link_id Task link id + * @param integer $task_id Task id + * @param integer $opposite_task_id Opposite task id + * @param integer $link_id Link id + * @return boolean + */ + public function update($task_link_id, $task_id, $opposite_task_id, $link_id) + { + $events = array(); + $this->db->startTransaction(); + + // Get original task link + $task_link = $this->getById($task_link_id); + + // Find opposite task link + $opposite_task_link = $this->getOppositeTaskLink($task_link); + + // Get opposite link + $opposite_link_id = $this->linkModel->getOppositeLinkId($link_id); + + // Update the original task link + $values = array( + 'task_id' => $task_id, + 'opposite_task_id' => $opposite_task_id, + 'link_id' => $link_id, + ); + + $rs1 = $this->db->table(self::TABLE)->eq('id', $task_link_id)->update($values); + $events[] = $values; + + // Update the opposite link + $values = array( + 'task_id' => $opposite_task_id, + 'opposite_task_id' => $task_id, + 'link_id' => $opposite_link_id, + ); + + $rs2 = $this->db->table(self::TABLE)->eq('id', $opposite_task_link['id'])->update($values); + $events[] = $values; + + $this->db->closeTransaction(); + + if ($rs1 && $rs2) { + $this->fireEvents($events); + return true; + } + + return false; + } + + /** + * Remove a link between two tasks + * + * @access public + * @param integer $task_link_id + * @return boolean + */ + public function remove($task_link_id) + { + $this->db->startTransaction(); + + $link = $this->getById($task_link_id); + $link_id = $this->linkModel->getOppositeLinkId($link['link_id']); + + $this->db->table(self::TABLE)->eq('id', $task_link_id)->remove(); + + $this->db + ->table(self::TABLE) + ->eq('opposite_task_id', $link['task_id']) + ->eq('task_id', $link['opposite_task_id']) + ->eq('link_id', $link_id)->remove(); + + $this->db->closeTransaction(); + + return true; + } +} diff --git a/app/Model/TaskMetadata.php b/app/Model/TaskMetadata.php deleted file mode 100644 index d9d024bf..00000000 --- a/app/Model/TaskMetadata.php +++ /dev/null @@ -1,35 +0,0 @@ -taskFinderModel->exists($task_id)) { + return false; + } + + $this->taskFileModel->removeAll($task_id); + + return $this->db->table(self::TABLE)->eq('id', $task_id)->remove(); + } + + /** + * Get a the task id from a text + * + * Example: "Fix bug #1234" will return 1234 + * + * @access public + * @param string $message Text + * @return integer + */ + public function getTaskIdFromText($message) + { + if (preg_match('!#(\d+)!i', $message, $matches) && isset($matches[1])) { + return $matches[1]; + } + + return 0; + } + + /** + * Return the list user selectable recurrence status + * + * @access public + * @return array + */ + public function getRecurrenceStatusList() + { + return array( + TaskModel::RECURRING_STATUS_NONE => t('No'), + TaskModel::RECURRING_STATUS_PENDING => t('Yes'), + ); + } + + /** + * Return the list recurrence triggers + * + * @access public + * @return array + */ + public function getRecurrenceTriggerList() + { + return array( + TaskModel::RECURRING_TRIGGER_FIRST_COLUMN => t('When task is moved from first column'), + TaskModel::RECURRING_TRIGGER_LAST_COLUMN => t('When task is moved to last column'), + TaskModel::RECURRING_TRIGGER_CLOSE => t('When task is closed'), + ); + } + + /** + * Return the list options to calculate recurrence due date + * + * @access public + * @return array + */ + public function getRecurrenceBasedateList() + { + return array( + TaskModel::RECURRING_BASEDATE_DUEDATE => t('Existing due date'), + TaskModel::RECURRING_BASEDATE_TRIGGERDATE => t('Action date'), + ); + } + + /** + * Return the list recurrence timeframes + * + * @access public + * @return array + */ + public function getRecurrenceTimeframeList() + { + return array( + TaskModel::RECURRING_TIMEFRAME_DAYS => t('Day(s)'), + TaskModel::RECURRING_TIMEFRAME_MONTHS => t('Month(s)'), + TaskModel::RECURRING_TIMEFRAME_YEARS => t('Year(s)'), + ); + } + + /** + * Get task progress based on the column position + * + * @access public + * @param array $task + * @param array $columns + * @return integer + */ + public function getProgress(array $task, array $columns) + { + if ($task['is_active'] == self::STATUS_CLOSED) { + return 100; + } + + $position = 0; + + foreach ($columns as $column_id => $column_title) { + if ($column_id == $task['column_id']) { + break; + } + + $position++; + } + + return round(($position * 100) / count($columns), 1); + } + + /** + * Helper method to duplicate all tasks to another project + * + * @access public + * @param integer $src_project_id + * @param integer $dst_project_id + * @return boolean + */ + public function duplicate($src_project_id, $dst_project_id) + { + $task_ids = $this->taskFinderModel->getAllIds($src_project_id, array(TaskModel::STATUS_OPEN, TaskModel::STATUS_CLOSED)); + + foreach ($task_ids as $task_id) { + if (! $this->taskDuplicationModel->duplicateToProject($task_id, $dst_project_id)) { + return false; + } + } + + return true; + } +} diff --git a/app/Model/TaskModification.php b/app/Model/TaskModification.php deleted file mode 100644 index 7e0ea8b2..00000000 --- a/app/Model/TaskModification.php +++ /dev/null @@ -1,97 +0,0 @@ -taskFinder->getById($values['id']); - - $this->prepare($values); - $result = $this->db->table(Task::TABLE)->eq('id', $original_task['id'])->update($values); - - if ($fire_events && $result) { - $this->fireEvents($original_task, $values); - } - - return $result; - } - - /** - * Fire events - * - * @access public - * @param array $task - * @param array $new_values - */ - public function fireEvents(array $task, array $new_values) - { - $events = array(); - $event_data = array_merge($task, $new_values, array('task_id' => $task['id'])); - - // Values changed - $event_data['changes'] = array_diff_assoc($new_values, $task); - unset($event_data['changes']['date_modification']); - - if ($this->isFieldModified('owner_id', $event_data['changes'])) { - $events[] = Task::EVENT_ASSIGNEE_CHANGE; - } elseif (! empty($event_data['changes'])) { - $events[] = Task::EVENT_CREATE_UPDATE; - $events[] = Task::EVENT_UPDATE; - } - - foreach ($events as $event) { - $this->logger->debug('Event fired: '.$event); - $this->dispatcher->dispatch($event, new TaskEvent($event_data)); - } - } - - /** - * Return true if the field is the only modified value - * - * @access public - * @param string $field - * @param array $changes - * @return boolean - */ - public function isFieldModified($field, array $changes) - { - return isset($changes[$field]) && count($changes) === 1; - } - - /** - * Prepare data before task modification - * - * @access public - * @param array $values Form values - */ - public function prepare(array &$values) - { - $values = $this->dateParser->convert($values, array('date_due')); - $values = $this->dateParser->convert($values, array('date_started'), true); - - $this->helper->model->removeFields($values, array('another_task', 'id')); - $this->helper->model->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent')); - $this->helper->model->convertIntegerFields($values, array('priority', 'is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); - - $values['date_modification'] = time(); - } -} diff --git a/app/Model/TaskModificationModel.php b/app/Model/TaskModificationModel.php new file mode 100644 index 00000000..762af2c5 --- /dev/null +++ b/app/Model/TaskModificationModel.php @@ -0,0 +1,97 @@ +taskFinderModel->getById($values['id']); + + $this->prepare($values); + $result = $this->db->table(TaskModel::TABLE)->eq('id', $original_task['id'])->update($values); + + if ($fire_events && $result) { + $this->fireEvents($original_task, $values); + } + + return $result; + } + + /** + * Fire events + * + * @access public + * @param array $task + * @param array $new_values + */ + public function fireEvents(array $task, array $new_values) + { + $events = array(); + $event_data = array_merge($task, $new_values, array('task_id' => $task['id'])); + + // Values changed + $event_data['changes'] = array_diff_assoc($new_values, $task); + unset($event_data['changes']['date_modification']); + + if ($this->isFieldModified('owner_id', $event_data['changes'])) { + $events[] = TaskModel::EVENT_ASSIGNEE_CHANGE; + } elseif (! empty($event_data['changes'])) { + $events[] = TaskModel::EVENT_CREATE_UPDATE; + $events[] = TaskModel::EVENT_UPDATE; + } + + foreach ($events as $event) { + $this->logger->debug('Event fired: '.$event); + $this->dispatcher->dispatch($event, new TaskEvent($event_data)); + } + } + + /** + * Return true if the field is the only modified value + * + * @access public + * @param string $field + * @param array $changes + * @return boolean + */ + public function isFieldModified($field, array $changes) + { + return isset($changes[$field]) && count($changes) === 1; + } + + /** + * Prepare data before task modification + * + * @access public + * @param array $values Form values + */ + public function prepare(array &$values) + { + $values = $this->dateParser->convert($values, array('date_due')); + $values = $this->dateParser->convert($values, array('date_started'), true); + + $this->helper->model->removeFields($values, array('another_task', 'id')); + $this->helper->model->resetFields($values, array('date_due', 'date_started', 'score', 'category_id', 'time_estimated', 'time_spent')); + $this->helper->model->convertIntegerFields($values, array('priority', 'is_active', 'recurrence_status', 'recurrence_trigger', 'recurrence_factor', 'recurrence_timeframe', 'recurrence_basedate')); + + $values['date_modification'] = time(); + } +} diff --git a/app/Model/TaskPosition.php b/app/Model/TaskPosition.php deleted file mode 100644 index 5b794795..00000000 --- a/app/Model/TaskPosition.php +++ /dev/null @@ -1,239 +0,0 @@ -= 1) - * @param integer $swimlane_id Swimlane id - * @param boolean $fire_events Fire events - * @return boolean - */ - public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true) - { - if ($position < 1) { - return false; - } - - $task = $this->taskFinder->getById($task_id); - - if ($task['is_active'] == Task::STATUS_CLOSED) { - return true; - } - - $result = false; - - if ($task['swimlane_id'] != $swimlane_id) { - $result = $this->saveSwimlaneChange($project_id, $task_id, $position, $task['column_id'], $column_id, $task['swimlane_id'], $swimlane_id); - } elseif ($task['column_id'] != $column_id) { - $result = $this->saveColumnChange($project_id, $task_id, $position, $swimlane_id, $task['column_id'], $column_id); - } elseif ($task['position'] != $position) { - $result = $this->savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id); - } - - if ($result && $fire_events) { - $this->fireEvents($task, $column_id, $position, $swimlane_id); - } - - return $result; - } - - /** - * Move a task to another swimlane - * - * @access private - * @param integer $project_id - * @param integer $task_id - * @param integer $position - * @param integer $original_column_id - * @param integer $new_column_id - * @param integer $original_swimlane_id - * @param integer $new_swimlane_id - * @return boolean - */ - private function saveSwimlaneChange($project_id, $task_id, $position, $original_column_id, $new_column_id, $original_swimlane_id, $new_swimlane_id) - { - $this->db->startTransaction(); - $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $original_swimlane_id); - $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $new_swimlane_id); - $this->db->closeTransaction(); - - return $r1 && $r2; - } - - /** - * Move a task to another column - * - * @access private - * @param integer $project_id - * @param integer $task_id - * @param integer $position - * @param integer $swimlane_id - * @param integer $original_column_id - * @param integer $new_column_id - * @return boolean - */ - private function saveColumnChange($project_id, $task_id, $position, $swimlane_id, $original_column_id, $new_column_id) - { - $this->db->startTransaction(); - $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $swimlane_id); - $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $swimlane_id); - $this->db->closeTransaction(); - - return $r1 && $r2; - } - - /** - * Move a task to another position in the same column - * - * @access private - * @param integer $project_id - * @param integer $task_id - * @param integer $position - * @param integer $column_id - * @param integer $swimlane_id - * @return boolean - */ - private function savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id) - { - $this->db->startTransaction(); - $result = $this->saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id); - $this->db->closeTransaction(); - - return $result; - } - - /** - * Save all task positions for one column - * - * @access private - * @param integer $project_id - * @param integer $task_id - * @param integer $position - * @param integer $column_id - * @param integer $swimlane_id - * @return boolean - */ - private function saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id) - { - $tasks_ids = $this->db->table(Task::TABLE) - ->eq('is_active', 1) - ->eq('swimlane_id', $swimlane_id) - ->eq('project_id', $project_id) - ->eq('column_id', $column_id) - ->neq('id', $task_id) - ->asc('position') - ->asc('id') - ->findAllByColumn('id'); - - $offset = 1; - - foreach ($tasks_ids as $current_task_id) { - - // Insert the new task - if ($position == $offset) { - if (! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { - return false; - } - $offset++; - } - - // Rewrite other tasks position - if (! $this->saveTaskPosition($current_task_id, $offset, $column_id, $swimlane_id)) { - return false; - } - - $offset++; - } - - // Insert the new task at the bottom and normalize bad position - if ($position >= $offset && ! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { - return false; - } - - $now = time(); - - return $this->db->table(Task::TABLE)->eq('id', $task_id)->update(array( - 'date_moved' => $now, - 'date_modification' => $now, - )); - } - - /** - * Save new task position - * - * @access private - * @param integer $task_id - * @param integer $position - * @param integer $column_id - * @param integer $swimlane_id - * @return boolean - */ - private function saveTaskPosition($task_id, $position, $column_id, $swimlane_id) - { - $result = $this->db->table(Task::TABLE)->eq('id', $task_id)->update(array( - 'position' => $position, - 'column_id' => $column_id, - 'swimlane_id' => $swimlane_id, - )); - - if (! $result) { - $this->db->cancelTransaction(); - return false; - } - - return true; - } - - /** - * Fire events - * - * @access private - * @param array $task - * @param integer $new_column_id - * @param integer $new_position - * @param integer $new_swimlane_id - */ - private function fireEvents(array $task, $new_column_id, $new_position, $new_swimlane_id) - { - $event_data = array( - 'task_id' => $task['id'], - 'project_id' => $task['project_id'], - 'position' => $new_position, - 'column_id' => $new_column_id, - 'swimlane_id' => $new_swimlane_id, - 'src_column_id' => $task['column_id'], - 'dst_column_id' => $new_column_id, - 'date_moved' => $task['date_moved'], - 'recurrence_status' => $task['recurrence_status'], - 'recurrence_trigger' => $task['recurrence_trigger'], - ); - - if ($task['swimlane_id'] != $new_swimlane_id) { - $this->logger->debug('Event fired: '.Task::EVENT_MOVE_SWIMLANE); - $this->dispatcher->dispatch(Task::EVENT_MOVE_SWIMLANE, new TaskEvent($event_data)); - } elseif ($task['column_id'] != $new_column_id) { - $this->logger->debug('Event fired: '.Task::EVENT_MOVE_COLUMN); - $this->dispatcher->dispatch(Task::EVENT_MOVE_COLUMN, new TaskEvent($event_data)); - } elseif ($task['position'] != $new_position) { - $this->logger->debug('Event fired: '.Task::EVENT_MOVE_POSITION); - $this->dispatcher->dispatch(Task::EVENT_MOVE_POSITION, new TaskEvent($event_data)); - } - } -} diff --git a/app/Model/TaskPositionModel.php b/app/Model/TaskPositionModel.php new file mode 100644 index 00000000..9fdb8f7d --- /dev/null +++ b/app/Model/TaskPositionModel.php @@ -0,0 +1,239 @@ += 1) + * @param integer $swimlane_id Swimlane id + * @param boolean $fire_events Fire events + * @return boolean + */ + public function movePosition($project_id, $task_id, $column_id, $position, $swimlane_id = 0, $fire_events = true) + { + if ($position < 1) { + return false; + } + + $task = $this->taskFinderModel->getById($task_id); + + if ($task['is_active'] == TaskModel::STATUS_CLOSED) { + return true; + } + + $result = false; + + if ($task['swimlane_id'] != $swimlane_id) { + $result = $this->saveSwimlaneChange($project_id, $task_id, $position, $task['column_id'], $column_id, $task['swimlane_id'], $swimlane_id); + } elseif ($task['column_id'] != $column_id) { + $result = $this->saveColumnChange($project_id, $task_id, $position, $swimlane_id, $task['column_id'], $column_id); + } elseif ($task['position'] != $position) { + $result = $this->savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id); + } + + if ($result && $fire_events) { + $this->fireEvents($task, $column_id, $position, $swimlane_id); + } + + return $result; + } + + /** + * Move a task to another swimlane + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $original_column_id + * @param integer $new_column_id + * @param integer $original_swimlane_id + * @param integer $new_swimlane_id + * @return boolean + */ + private function saveSwimlaneChange($project_id, $task_id, $position, $original_column_id, $new_column_id, $original_swimlane_id, $new_swimlane_id) + { + $this->db->startTransaction(); + $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $original_swimlane_id); + $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $new_swimlane_id); + $this->db->closeTransaction(); + + return $r1 && $r2; + } + + /** + * Move a task to another column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $swimlane_id + * @param integer $original_column_id + * @param integer $new_column_id + * @return boolean + */ + private function saveColumnChange($project_id, $task_id, $position, $swimlane_id, $original_column_id, $new_column_id) + { + $this->db->startTransaction(); + $r1 = $this->saveTaskPositions($project_id, $task_id, 0, $original_column_id, $swimlane_id); + $r2 = $this->saveTaskPositions($project_id, $task_id, $position, $new_column_id, $swimlane_id); + $this->db->closeTransaction(); + + return $r1 && $r2; + } + + /** + * Move a task to another position in the same column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function savePositionChange($project_id, $task_id, $position, $column_id, $swimlane_id) + { + $this->db->startTransaction(); + $result = $this->saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id); + $this->db->closeTransaction(); + + return $result; + } + + /** + * Save all task positions for one column + * + * @access private + * @param integer $project_id + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function saveTaskPositions($project_id, $task_id, $position, $column_id, $swimlane_id) + { + $tasks_ids = $this->db->table(TaskModel::TABLE) + ->eq('is_active', 1) + ->eq('swimlane_id', $swimlane_id) + ->eq('project_id', $project_id) + ->eq('column_id', $column_id) + ->neq('id', $task_id) + ->asc('position') + ->asc('id') + ->findAllByColumn('id'); + + $offset = 1; + + foreach ($tasks_ids as $current_task_id) { + + // Insert the new task + if ($position == $offset) { + if (! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + $offset++; + } + + // Rewrite other tasks position + if (! $this->saveTaskPosition($current_task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + + $offset++; + } + + // Insert the new task at the bottom and normalize bad position + if ($position >= $offset && ! $this->saveTaskPosition($task_id, $offset, $column_id, $swimlane_id)) { + return false; + } + + $now = time(); + + return $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array( + 'date_moved' => $now, + 'date_modification' => $now, + )); + } + + /** + * Save new task position + * + * @access private + * @param integer $task_id + * @param integer $position + * @param integer $column_id + * @param integer $swimlane_id + * @return boolean + */ + private function saveTaskPosition($task_id, $position, $column_id, $swimlane_id) + { + $result = $this->db->table(TaskModel::TABLE)->eq('id', $task_id)->update(array( + 'position' => $position, + 'column_id' => $column_id, + 'swimlane_id' => $swimlane_id, + )); + + if (! $result) { + $this->db->cancelTransaction(); + return false; + } + + return true; + } + + /** + * Fire events + * + * @access private + * @param array $task + * @param integer $new_column_id + * @param integer $new_position + * @param integer $new_swimlane_id + */ + private function fireEvents(array $task, $new_column_id, $new_position, $new_swimlane_id) + { + $event_data = array( + 'task_id' => $task['id'], + 'project_id' => $task['project_id'], + 'position' => $new_position, + 'column_id' => $new_column_id, + 'swimlane_id' => $new_swimlane_id, + 'src_column_id' => $task['column_id'], + 'dst_column_id' => $new_column_id, + 'date_moved' => $task['date_moved'], + 'recurrence_status' => $task['recurrence_status'], + 'recurrence_trigger' => $task['recurrence_trigger'], + ); + + if ($task['swimlane_id'] != $new_swimlane_id) { + $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_SWIMLANE); + $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_SWIMLANE, new TaskEvent($event_data)); + } elseif ($task['column_id'] != $new_column_id) { + $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_COLUMN); + $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_COLUMN, new TaskEvent($event_data)); + } elseif ($task['position'] != $new_position) { + $this->logger->debug('Event fired: '.TaskModel::EVENT_MOVE_POSITION); + $this->dispatcher->dispatch(TaskModel::EVENT_MOVE_POSITION, new TaskEvent($event_data)); + } + } +} diff --git a/app/Model/TaskStatus.php b/app/Model/TaskStatus.php deleted file mode 100644 index 4ba13a60..00000000 --- a/app/Model/TaskStatus.php +++ /dev/null @@ -1,146 +0,0 @@ -checkStatus($task_id, Task::STATUS_CLOSED); - } - - /** - * Return true if the task is open - * - * @access public - * @param integer $task_id Task id - * @return boolean - */ - public function isOpen($task_id) - { - return $this->checkStatus($task_id, Task::STATUS_OPEN); - } - - /** - * Mark a task closed - * - * @access public - * @param integer $task_id Task id - * @return boolean - */ - public function close($task_id) - { - $this->subtask->closeAll($task_id); - return $this->changeStatus($task_id, Task::STATUS_CLOSED, time(), Task::EVENT_CLOSE); - } - - /** - * Mark a task open - * - * @access public - * @param integer $task_id Task id - * @return boolean - */ - public function open($task_id) - { - return $this->changeStatus($task_id, Task::STATUS_OPEN, 0, Task::EVENT_OPEN); - } - - /** - * Close multiple tasks - * - * @access public - * @param array $task_ids - */ - public function closeMultipleTasks(array $task_ids) - { - foreach ($task_ids as $task_id) { - $this->close($task_id); - } - } - - /** - * Close all tasks within a column/swimlane - * - * @access public - * @param integer $swimlane_id - * @param integer $column_id - */ - public function closeTasksBySwimlaneAndColumn($swimlane_id, $column_id) - { - $task_ids = $this->db - ->table(Task::TABLE) - ->eq('swimlane_id', $swimlane_id) - ->eq('column_id', $column_id) - ->eq(Task::TABLE.'.is_active', Task::STATUS_OPEN) - ->findAllByColumn('id'); - - $this->closeMultipleTasks($task_ids); - } - - /** - * Common method to change the status of task - * - * @access private - * @param integer $task_id Task id - * @param integer $status Task status - * @param integer $date_completed Timestamp - * @param string $event Event name - * @return boolean - */ - private function changeStatus($task_id, $status, $date_completed, $event) - { - if (! $this->taskFinder->exists($task_id)) { - return false; - } - - $result = $this->db - ->table(Task::TABLE) - ->eq('id', $task_id) - ->update(array( - 'is_active' => $status, - 'date_completed' => $date_completed, - 'date_modification' => time(), - )); - - if ($result) { - $this->logger->debug('Event fired: '.$event); - $this->dispatcher->dispatch($event, new TaskEvent(array('task_id' => $task_id) + $this->taskFinder->getById($task_id))); - } - - return $result; - } - - /** - * Check the status of a task - * - * @access private - * @param integer $task_id Task id - * @param integer $status Task status - * @return boolean - */ - private function checkStatus($task_id, $status) - { - return $this->db - ->table(Task::TABLE) - ->eq('id', $task_id) - ->eq('is_active', $status) - ->count() === 1; - } -} diff --git a/app/Model/TaskStatusModel.php b/app/Model/TaskStatusModel.php new file mode 100644 index 00000000..4d573f0e --- /dev/null +++ b/app/Model/TaskStatusModel.php @@ -0,0 +1,146 @@ +checkStatus($task_id, TaskModel::STATUS_CLOSED); + } + + /** + * Return true if the task is open + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function isOpen($task_id) + { + return $this->checkStatus($task_id, TaskModel::STATUS_OPEN); + } + + /** + * Mark a task closed + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function close($task_id) + { + $this->subtaskModel->closeAll($task_id); + return $this->changeStatus($task_id, TaskModel::STATUS_CLOSED, time(), TaskModel::EVENT_CLOSE); + } + + /** + * Mark a task open + * + * @access public + * @param integer $task_id Task id + * @return boolean + */ + public function open($task_id) + { + return $this->changeStatus($task_id, TaskModel::STATUS_OPEN, 0, TaskModel::EVENT_OPEN); + } + + /** + * Close multiple tasks + * + * @access public + * @param array $task_ids + */ + public function closeMultipleTasks(array $task_ids) + { + foreach ($task_ids as $task_id) { + $this->close($task_id); + } + } + + /** + * Close all tasks within a column/swimlane + * + * @access public + * @param integer $swimlane_id + * @param integer $column_id + */ + public function closeTasksBySwimlaneAndColumn($swimlane_id, $column_id) + { + $task_ids = $this->db + ->table(TaskModel::TABLE) + ->eq('swimlane_id', $swimlane_id) + ->eq('column_id', $column_id) + ->eq(TaskModel::TABLE.'.is_active', TaskModel::STATUS_OPEN) + ->findAllByColumn('id'); + + $this->closeMultipleTasks($task_ids); + } + + /** + * Common method to change the status of task + * + * @access private + * @param integer $task_id Task id + * @param integer $status Task status + * @param integer $date_completed Timestamp + * @param string $event Event name + * @return boolean + */ + private function changeStatus($task_id, $status, $date_completed, $event) + { + if (! $this->taskFinderModel->exists($task_id)) { + return false; + } + + $result = $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->update(array( + 'is_active' => $status, + 'date_completed' => $date_completed, + 'date_modification' => time(), + )); + + if ($result) { + $this->logger->debug('Event fired: '.$event); + $this->dispatcher->dispatch($event, new TaskEvent(array('task_id' => $task_id) + $this->taskFinderModel->getById($task_id))); + } + + return $result; + } + + /** + * Check the status of a task + * + * @access private + * @param integer $task_id Task id + * @param integer $status Task status + * @return boolean + */ + private function checkStatus($task_id, $status) + { + return $this->db + ->table(TaskModel::TABLE) + ->eq('id', $task_id) + ->eq('is_active', $status) + ->count() === 1; + } +} diff --git a/app/Model/Timezone.php b/app/Model/Timezone.php deleted file mode 100644 index de426e79..00000000 --- a/app/Model/Timezone.php +++ /dev/null @@ -1,58 +0,0 @@ - t('Application default')) + $listing; - } - - return $listing; - } - - /** - * Get current timezone - * - * @access public - * @return string - */ - public function getCurrentTimezone() - { - if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['timezone'])) { - return $this->sessionStorage->user['timezone']; - } - - return $this->config->get('application_timezone', 'UTC'); - } - - /** - * Set timezone - * - * @access public - */ - public function setCurrentTimezone() - { - date_default_timezone_set($this->getCurrentTimezone()); - } -} diff --git a/app/Model/TimezoneModel.php b/app/Model/TimezoneModel.php new file mode 100644 index 00000000..8b3e895a --- /dev/null +++ b/app/Model/TimezoneModel.php @@ -0,0 +1,58 @@ + t('Application default')) + $listing; + } + + return $listing; + } + + /** + * Get current timezone + * + * @access public + * @return string + */ + public function getCurrentTimezone() + { + if ($this->userSession->isLogged() && ! empty($this->sessionStorage->user['timezone'])) { + return $this->sessionStorage->user['timezone']; + } + + return $this->configModel->get('application_timezone', 'UTC'); + } + + /** + * Set timezone + * + * @access public + */ + public function setCurrentTimezone() + { + date_default_timezone_set($this->getCurrentTimezone()); + } +} diff --git a/app/Model/Transition.php b/app/Model/Transition.php deleted file mode 100644 index 2bc622b0..00000000 --- a/app/Model/Transition.php +++ /dev/null @@ -1,130 +0,0 @@ -db->table(self::TABLE)->insert(array( - 'user_id' => $user_id, - 'project_id' => $task_event['project_id'], - 'task_id' => $task_event['task_id'], - 'src_column_id' => $task_event['src_column_id'], - 'dst_column_id' => $task_event['dst_column_id'], - 'date' => $time, - 'time_spent' => $time - $task_event['date_moved'] - )); - } - - /** - * Get time spent by task for each column - * - * @access public - * @param integer $task_id - * @return array - */ - public function getTimeSpentByTask($task_id) - { - return $this->db - ->hashtable(self::TABLE) - ->groupBy('src_column_id') - ->eq('task_id', $task_id) - ->getAll('src_column_id', 'SUM(time_spent) AS time_spent'); - } - - /** - * Get all transitions by task - * - * @access public - * @param integer $task_id - * @return array - */ - public function getAllByTask($task_id) - { - return $this->db->table(self::TABLE) - ->columns( - 'src.title as src_column', - 'dst.title as dst_column', - User::TABLE.'.name', - User::TABLE.'.username', - self::TABLE.'.user_id', - self::TABLE.'.date', - self::TABLE.'.time_spent' - ) - ->eq('task_id', $task_id) - ->desc('date') - ->join(User::TABLE, 'id', 'user_id') - ->join(Column::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') - ->join(Column::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') - ->findAll(); - } - - /** - * Get all transitions by project - * - * @access public - * @param integer $project_id - * @param mixed $from Start date (timestamp or user formatted date) - * @param mixed $to End date (timestamp or user formatted date) - * @return array - */ - public function getAllByProjectAndDate($project_id, $from, $to) - { - if (! is_numeric($from)) { - $from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from)); - } - - if (! is_numeric($to)) { - $to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to))); - } - - return $this->db->table(self::TABLE) - ->columns( - Task::TABLE.'.id', - Task::TABLE.'.title', - 'src.title as src_column', - 'dst.title as dst_column', - User::TABLE.'.name', - User::TABLE.'.username', - self::TABLE.'.user_id', - self::TABLE.'.date', - self::TABLE.'.time_spent' - ) - ->gte('date', $from) - ->lte('date', $to) - ->eq(self::TABLE.'.project_id', $project_id) - ->desc('date') - ->desc(self::TABLE.'.id') - ->join(Task::TABLE, 'id', 'task_id') - ->join(User::TABLE, 'id', 'user_id') - ->join(Column::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') - ->join(Column::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') - ->findAll(); - } -} diff --git a/app/Model/TransitionModel.php b/app/Model/TransitionModel.php new file mode 100644 index 00000000..a4a58472 --- /dev/null +++ b/app/Model/TransitionModel.php @@ -0,0 +1,130 @@ +db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $task_event['project_id'], + 'task_id' => $task_event['task_id'], + 'src_column_id' => $task_event['src_column_id'], + 'dst_column_id' => $task_event['dst_column_id'], + 'date' => $time, + 'time_spent' => $time - $task_event['date_moved'] + )); + } + + /** + * Get time spent by task for each column + * + * @access public + * @param integer $task_id + * @return array + */ + public function getTimeSpentByTask($task_id) + { + return $this->db + ->hashtable(self::TABLE) + ->groupBy('src_column_id') + ->eq('task_id', $task_id) + ->getAll('src_column_id', 'SUM(time_spent) AS time_spent'); + } + + /** + * Get all transitions by task + * + * @access public + * @param integer $task_id + * @return array + */ + public function getAllByTask($task_id) + { + return $this->db->table(self::TABLE) + ->columns( + 'src.title as src_column', + 'dst.title as dst_column', + UserModel::TABLE.'.name', + UserModel::TABLE.'.username', + self::TABLE.'.user_id', + self::TABLE.'.date', + self::TABLE.'.time_spent' + ) + ->eq('task_id', $task_id) + ->desc('date') + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ColumnModel::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') + ->join(ColumnModel::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') + ->findAll(); + } + + /** + * Get all transitions by project + * + * @access public + * @param integer $project_id + * @param mixed $from Start date (timestamp or user formatted date) + * @param mixed $to End date (timestamp or user formatted date) + * @return array + */ + public function getAllByProjectAndDate($project_id, $from, $to) + { + if (! is_numeric($from)) { + $from = $this->dateParser->removeTimeFromTimestamp($this->dateParser->getTimestamp($from)); + } + + if (! is_numeric($to)) { + $to = $this->dateParser->removeTimeFromTimestamp(strtotime('+1 day', $this->dateParser->getTimestamp($to))); + } + + return $this->db->table(self::TABLE) + ->columns( + TaskModel::TABLE.'.id', + TaskModel::TABLE.'.title', + 'src.title as src_column', + 'dst.title as dst_column', + UserModel::TABLE.'.name', + UserModel::TABLE.'.username', + self::TABLE.'.user_id', + self::TABLE.'.date', + self::TABLE.'.time_spent' + ) + ->gte('date', $from) + ->lte('date', $to) + ->eq(self::TABLE.'.project_id', $project_id) + ->desc('date') + ->desc(self::TABLE.'.id') + ->join(TaskModel::TABLE, 'id', 'task_id') + ->join(UserModel::TABLE, 'id', 'user_id') + ->join(ColumnModel::TABLE.' as src', 'id', 'src_column_id', self::TABLE, 'src') + ->join(ColumnModel::TABLE.' as dst', 'id', 'dst_column_id', self::TABLE, 'dst') + ->findAll(); + } +} diff --git a/app/Model/User.php b/app/Model/User.php deleted file mode 100644 index cbfca0ac..00000000 --- a/app/Model/User.php +++ /dev/null @@ -1,390 +0,0 @@ -db->table(self::TABLE)->eq('id', $user_id)->exists(); - } - - /** - * Return true if the user is active - * - * @access public - * @param integer $user_id User id - * @return boolean - */ - public function isActive($user_id) - { - return $this->db->table(self::TABLE)->eq('id', $user_id)->eq('is_active', 1)->exists(); - } - - /** - * Get query to fetch all users - * - * @access public - * @return \PicoDb\Table - */ - public function getQuery() - { - return $this->db->table(self::TABLE); - } - - /** - * Return the full name - * - * @param array $user User properties - * @return string - */ - public function getFullname(array $user) - { - return $user['name'] ?: $user['username']; - } - - /** - * Return true is the given user id is administrator - * - * @access public - * @param integer $user_id User id - * @return boolean - */ - public function isAdmin($user_id) - { - return $this->userSession->isAdmin() || // Avoid SQL query if connected - $this->db - ->table(User::TABLE) - ->eq('id', $user_id) - ->eq('role', Role::APP_ADMIN) - ->exists(); - } - - /** - * 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 $column - * @param string $id - * @return array|boolean - */ - public function getByExternalId($column, $id) - { - if (empty($id)) { - return false; - } - - return $this->db->table(self::TABLE)->eq($column, $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 user_id by username - * - * @access public - * @param string $username Username - * @return integer - */ - public function getIdByUsername($username) - { - return $this->db->table(self::TABLE)->eq('username', $username)->findOneColumn('id'); - } - - /** - * Get a specific user by the email address - * - * @access public - * @param string $email Email - * @return array|boolean - */ - public function getByEmail($email) - { - if (empty($email)) { - return false; - } - - return $this->db->table(self::TABLE)->eq('email', $email)->findOne(); - } - - /** - * Fetch user by using the token - * - * @access public - * @param string $token Token - * @return array|boolean - */ - public function getByToken($token) - { - if (empty($token)) { - return false; - } - - return $this->db->table(self::TABLE)->eq('token', $token)->findOne(); - } - - /** - * Get all users - * - * @access public - * @return array - */ - public function getAll() - { - return $this->getQuery()->asc('username')->findAll(); - } - - /** - * Get the number of users - * - * @access public - * @return integer - */ - public function count() - { - return $this->db->table(self::TABLE)->count(); - } - - /** - * List all users (key-value pairs with id/username) - * - * @access public - * @param boolean $prepend Prepend "All users" - * @return array - */ - public function getActiveUsersList($prepend = false) - { - $users = $this->db->table(self::TABLE)->eq('is_active', 1)->columns('id', 'username', 'name')->findAll(); - $listing = $this->prepareList($users); - - if ($prepend) { - return array(User::EVERYBODY_ID => t('Everybody')) + $listing; - } - - return $listing; - } - - /** - * Common method to prepare a user list - * - * @access public - * @param array $users Users list (from database) - * @return array Formated list - */ - public function prepareList(array $users) - { - $result = array(); - - foreach ($users as $user) { - $result[$user['id']] = $this->getFullname($user); - } - - asort($result); - - return $result; - } - - /** - * Prepare values before an update or a create - * - * @access public - * @param array $values Form values - */ - public function prepare(array &$values) - { - if (isset($values['password'])) { - if (! empty($values['password'])) { - $values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT); - } else { - unset($values['password']); - } - } - - $this->helper->model->removeFields($values, array('confirmation', 'current_password')); - $this->helper->model->resetFields($values, array('is_ldap_user', 'disable_login_form')); - $this->helper->model->convertNullFields($values, array('gitlab_id')); - $this->helper->model->convertIntegerFields($values, array('gitlab_id')); - } - - /** - * Add a new user in the database - * - * @access public - * @param array $values Form values - * @return boolean|integer - */ - public function create(array $values) - { - $this->prepare($values); - return $this->db->table(self::TABLE)->persist($values); - } - - /** - * Modify a new user - * - * @access public - * @param array $values Form values - * @return boolean - */ - public function update(array $values) - { - $this->prepare($values); - $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); - $this->userSession->refresh($values['id']); - return $result; - } - - /** - * Disable a specific user - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function disable($user_id) - { - return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 0)); - } - - /** - * Enable a specific user - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function enable($user_id) - { - return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 1)); - } - - /** - * Remove a specific user - * - * @access public - * @param integer $user_id User id - * @return boolean - */ - public function remove($user_id) - { - $this->avatarFile->remove($user_id); - - return $this->db->transaction(function (Database $db) use ($user_id) { - - // All assigned tasks are now unassigned (no foreign key) - if (! $db->table(Task::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0))) { - return false; - } - - // All assigned subtasks are now unassigned (no foreign key) - if (! $db->table(Subtask::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { - return false; - } - - // All comments are not assigned anymore (no foreign key) - if (! $db->table(Comment::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { - return false; - } - - // All private projects are removed - $project_ids = $db->table(Project::TABLE) - ->eq('is_private', 1) - ->eq(ProjectUserRole::TABLE.'.user_id', $user_id) - ->join(ProjectUserRole::TABLE, 'project_id', 'id') - ->findAllByColumn(Project::TABLE.'.id'); - - if (! empty($project_ids)) { - $db->table(Project::TABLE)->in('id', $project_ids)->remove(); - } - - // Finally remove the user - if (! $db->table(User::TABLE)->eq('id', $user_id)->remove()) { - return false; - } - }); - } - - /** - * Enable public access for a user - * - * @access public - * @param integer $user_id User id - * @return bool - */ - public function enablePublicAccess($user_id) - { - return $this->db - ->table(self::TABLE) - ->eq('id', $user_id) - ->save(array('token' => Token::getToken())); - } - - /** - * Disable public access for a user - * - * @access public - * @param integer $user_id User id - * @return bool - */ - public function disablePublicAccess($user_id) - { - return $this->db - ->table(self::TABLE) - ->eq('id', $user_id) - ->save(array('token' => '')); - } -} diff --git a/app/Model/UserLocking.php b/app/Model/UserLocking.php deleted file mode 100644 index ccaf402c..00000000 --- a/app/Model/UserLocking.php +++ /dev/null @@ -1,105 +0,0 @@ -db->table(User::TABLE) - ->eq('username', $username) - ->findOneColumn('nb_failed_login'); - } - - /** - * Reset to 0 the counter of failed login - * - * @access public - * @param string $username - * @return boolean - */ - public function resetFailedLogin($username) - { - return $this->db->table(User::TABLE) - ->eq('username', $username) - ->update(array( - 'nb_failed_login' => 0, - 'lock_expiration_date' => 0, - )); - } - - /** - * Increment failed login counter - * - * @access public - * @param string $username - * @return boolean - */ - public function incrementFailedLogin($username) - { - return $this->db->table(User::TABLE) - ->eq('username', $username) - ->increment('nb_failed_login', 1); - } - - /** - * Check if the account is locked - * - * @access public - * @param string $username - * @return boolean - */ - public function isLocked($username) - { - return $this->db->table(User::TABLE) - ->eq('username', $username) - ->neq('lock_expiration_date', 0) - ->gte('lock_expiration_date', time()) - ->exists(); - } - - /** - * Lock the account for the specified duration - * - * @access public - * @param string $username Username - * @param integer $duration Duration in minutes - * @return boolean - */ - public function lock($username, $duration = 15) - { - return $this->db->table(User::TABLE) - ->eq('username', $username) - ->update(array( - 'lock_expiration_date' => time() + $duration * 60 - )); - } - - /** - * Return true if the captcha must be shown - * - * @access public - * @param string $username - * @param integer $tries - * @return boolean - */ - public function hasCaptcha($username, $tries = BRUTEFORCE_CAPTCHA) - { - return $this->getFailedLogin($username) >= $tries; - } -} diff --git a/app/Model/UserLockingModel.php b/app/Model/UserLockingModel.php new file mode 100644 index 00000000..1d4d994c --- /dev/null +++ b/app/Model/UserLockingModel.php @@ -0,0 +1,105 @@ +db->table(UserModel::TABLE) + ->eq('username', $username) + ->findOneColumn('nb_failed_login'); + } + + /** + * Reset to 0 the counter of failed login + * + * @access public + * @param string $username + * @return boolean + */ + public function resetFailedLogin($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->update(array( + 'nb_failed_login' => 0, + 'lock_expiration_date' => 0, + )); + } + + /** + * Increment failed login counter + * + * @access public + * @param string $username + * @return boolean + */ + public function incrementFailedLogin($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->increment('nb_failed_login', 1); + } + + /** + * Check if the account is locked + * + * @access public + * @param string $username + * @return boolean + */ + public function isLocked($username) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->neq('lock_expiration_date', 0) + ->gte('lock_expiration_date', time()) + ->exists(); + } + + /** + * Lock the account for the specified duration + * + * @access public + * @param string $username Username + * @param integer $duration Duration in minutes + * @return boolean + */ + public function lock($username, $duration = 15) + { + return $this->db->table(UserModel::TABLE) + ->eq('username', $username) + ->update(array( + 'lock_expiration_date' => time() + $duration * 60 + )); + } + + /** + * Return true if the captcha must be shown + * + * @access public + * @param string $username + * @param integer $tries + * @return boolean + */ + public function hasCaptcha($username, $tries = BRUTEFORCE_CAPTCHA) + { + return $this->getFailedLogin($username) >= $tries; + } +} diff --git a/app/Model/UserMention.php b/app/Model/UserMention.php deleted file mode 100644 index 42b81840..00000000 --- a/app/Model/UserMention.php +++ /dev/null @@ -1,62 +0,0 @@ -db->table(User::TABLE) - ->columns('id', 'username', 'name', 'email', 'language') - ->eq('notifications_enabled', 1) - ->neq('id', $this->userSession->getId()) - ->in('username', array_unique($matches[1])) - ->findAll(); - } - - return $users; - } - - /** - * Fire events for user mentions - * - * @access public - * @param string $content - * @param string $eventName - * @param GenericEvent $event - */ - public function fireEvents($content, $eventName, GenericEvent $event) - { - if (empty($event['project_id'])) { - $event['project_id'] = $this->taskFinder->getProjectId($event['task_id']); - } - - $users = $this->getMentionedUsers($content); - - foreach ($users as $user) { - if ($this->projectPermission->isMember($event['project_id'], $user['id'])) { - $event['mention'] = $user; - $this->dispatcher->dispatch($eventName, $event); - } - } - } -} diff --git a/app/Model/UserMentionModel.php b/app/Model/UserMentionModel.php new file mode 100644 index 00000000..cdb9949e --- /dev/null +++ b/app/Model/UserMentionModel.php @@ -0,0 +1,62 @@ +db->table(UserModel::TABLE) + ->columns('id', 'username', 'name', 'email', 'language') + ->eq('notifications_enabled', 1) + ->neq('id', $this->userSession->getId()) + ->in('username', array_unique($matches[1])) + ->findAll(); + } + + return $users; + } + + /** + * Fire events for user mentions + * + * @access public + * @param string $content + * @param string $eventName + * @param GenericEvent $event + */ + public function fireEvents($content, $eventName, GenericEvent $event) + { + if (empty($event['project_id'])) { + $event['project_id'] = $this->taskFinderModel->getProjectId($event['task_id']); + } + + $users = $this->getMentionedUsers($content); + + foreach ($users as $user) { + if ($this->projectPermissionModel->isMember($event['project_id'], $user['id'])) { + $event['mention'] = $user; + $this->dispatcher->dispatch($eventName, $event); + } + } + } +} diff --git a/app/Model/UserMetadata.php b/app/Model/UserMetadata.php deleted file mode 100644 index 491c1575..00000000 --- a/app/Model/UserMetadata.php +++ /dev/null @@ -1,37 +0,0 @@ -db->table(self::TABLE)->eq('id', $user_id)->exists(); + } + + /** + * Return true if the user is active + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function isActive($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->eq('is_active', 1)->exists(); + } + + /** + * Get query to fetch all users + * + * @access public + * @return \PicoDb\Table + */ + public function getQuery() + { + return $this->db->table(self::TABLE); + } + + /** + * Return the full name + * + * @param array $user User properties + * @return string + */ + public function getFullname(array $user) + { + return $user['name'] ?: $user['username']; + } + + /** + * Return true is the given user id is administrator + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function isAdmin($user_id) + { + return $this->userSession->isAdmin() || // Avoid SQL query if connected + $this->db + ->table(UserModel::TABLE) + ->eq('id', $user_id) + ->eq('role', Role::APP_ADMIN) + ->exists(); + } + + /** + * 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 $column + * @param string $id + * @return array|boolean + */ + public function getByExternalId($column, $id) + { + if (empty($id)) { + return false; + } + + return $this->db->table(self::TABLE)->eq($column, $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 user_id by username + * + * @access public + * @param string $username Username + * @return integer + */ + public function getIdByUsername($username) + { + return $this->db->table(self::TABLE)->eq('username', $username)->findOneColumn('id'); + } + + /** + * Get a specific user by the email address + * + * @access public + * @param string $email Email + * @return array|boolean + */ + public function getByEmail($email) + { + if (empty($email)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('email', $email)->findOne(); + } + + /** + * Fetch user by using the token + * + * @access public + * @param string $token Token + * @return array|boolean + */ + public function getByToken($token) + { + if (empty($token)) { + return false; + } + + return $this->db->table(self::TABLE)->eq('token', $token)->findOne(); + } + + /** + * Get all users + * + * @access public + * @return array + */ + public function getAll() + { + return $this->getQuery()->asc('username')->findAll(); + } + + /** + * Get the number of users + * + * @access public + * @return integer + */ + public function count() + { + return $this->db->table(self::TABLE)->count(); + } + + /** + * List all users (key-value pairs with id/username) + * + * @access public + * @param boolean $prepend Prepend "All users" + * @return array + */ + public function getActiveUsersList($prepend = false) + { + $users = $this->db->table(self::TABLE)->eq('is_active', 1)->columns('id', 'username', 'name')->findAll(); + $listing = $this->prepareList($users); + + if ($prepend) { + return array(UserModel::EVERYBODY_ID => t('Everybody')) + $listing; + } + + return $listing; + } + + /** + * Common method to prepare a user list + * + * @access public + * @param array $users Users list (from database) + * @return array Formated list + */ + public function prepareList(array $users) + { + $result = array(); + + foreach ($users as $user) { + $result[$user['id']] = $this->getFullname($user); + } + + asort($result); + + return $result; + } + + /** + * Prepare values before an update or a create + * + * @access public + * @param array $values Form values + */ + public function prepare(array &$values) + { + if (isset($values['password'])) { + if (! empty($values['password'])) { + $values['password'] = \password_hash($values['password'], PASSWORD_BCRYPT); + } else { + unset($values['password']); + } + } + + $this->helper->model->removeFields($values, array('confirmation', 'current_password')); + $this->helper->model->resetFields($values, array('is_ldap_user', 'disable_login_form')); + $this->helper->model->convertNullFields($values, array('gitlab_id')); + $this->helper->model->convertIntegerFields($values, array('gitlab_id')); + } + + /** + * Add a new user in the database + * + * @access public + * @param array $values Form values + * @return boolean|integer + */ + public function create(array $values) + { + $this->prepare($values); + return $this->db->table(self::TABLE)->persist($values); + } + + /** + * Modify a new user + * + * @access public + * @param array $values Form values + * @return boolean + */ + public function update(array $values) + { + $this->prepare($values); + $result = $this->db->table(self::TABLE)->eq('id', $values['id'])->update($values); + $this->userSession->refresh($values['id']); + return $result; + } + + /** + * Disable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disable($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 0)); + } + + /** + * Enable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function enable($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 1)); + } + + /** + * Remove a specific user + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function remove($user_id) + { + $this->avatarFileModel->remove($user_id); + + return $this->db->transaction(function (Database $db) use ($user_id) { + + // All assigned tasks are now unassigned (no foreign key) + if (! $db->table(TaskModel::TABLE)->eq('owner_id', $user_id)->update(array('owner_id' => 0))) { + return false; + } + + // All assigned subtasks are now unassigned (no foreign key) + if (! $db->table(SubtaskModel::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { + return false; + } + + // All comments are not assigned anymore (no foreign key) + if (! $db->table(CommentModel::TABLE)->eq('user_id', $user_id)->update(array('user_id' => 0))) { + return false; + } + + // All private projects are removed + $project_ids = $db->table(ProjectModel::TABLE) + ->eq('is_private', 1) + ->eq(ProjectUserRoleModel::TABLE.'.user_id', $user_id) + ->join(ProjectUserRoleModel::TABLE, 'project_id', 'id') + ->findAllByColumn(ProjectModel::TABLE.'.id'); + + if (! empty($project_ids)) { + $db->table(ProjectModel::TABLE)->in('id', $project_ids)->remove(); + } + + // Finally remove the user + if (! $db->table(UserModel::TABLE)->eq('id', $user_id)->remove()) { + return false; + } + }); + } + + /** + * Enable public access for a user + * + * @access public + * @param integer $user_id User id + * @return bool + */ + public function enablePublicAccess($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $user_id) + ->save(array('token' => Token::getToken())); + } + + /** + * Disable public access for a user + * + * @access public + * @param integer $user_id User id + * @return bool + */ + public function disablePublicAccess($user_id) + { + return $this->db + ->table(self::TABLE) + ->eq('id', $user_id) + ->save(array('token' => '')); + } +} diff --git a/app/Model/UserNotification.php b/app/Model/UserNotification.php deleted file mode 100644 index ef90bb63..00000000 --- a/app/Model/UserNotification.php +++ /dev/null @@ -1,204 +0,0 @@ -getUsersWithNotificationEnabled($event_data['task']['project_id'], $this->userSession->getId()); - - foreach ($users as $user) { - if ($this->userNotificationFilter->shouldReceiveNotification($user, $event_data)) { - $this->sendUserNotification($user, $event_name, $event_data); - } - } - } - - /** - * Send notification to someone - * - * @access public - * @param array $user User - * @param string $event_name - * @param array $event_data - */ - public function sendUserNotification(array $user, $event_name, array $event_data) - { - Translator::unload(); - - // Use the user language otherwise use the application language (do not use the session language) - if (! empty($user['language'])) { - Translator::load($user['language']); - } else { - Translator::load($this->config->get('application_language', 'en_US')); - } - - foreach ($this->userNotificationType->getSelectedTypes($user['id']) as $type) { - $this->userNotificationType->getType($type)->notifyUser($user, $event_name, $event_data); - } - - // Restore locales - $this->language->loadCurrentLanguage(); - } - - /** - * Get a list of people with notifications enabled - * - * @access public - * @param integer $project_id Project id - * @param integer $exclude_user_id User id to exclude - * @return array - */ - public function getUsersWithNotificationEnabled($project_id, $exclude_user_id = 0) - { - if ($this->projectPermission->isEverybodyAllowed($project_id)) { - return $this->getEverybodyWithNotificationEnabled($exclude_user_id); - } - - $users = array(); - $members = $this->getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id); - $groups = $this->getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id); - - foreach (array_merge($members, $groups) as $user) { - if (! isset($users[$user['id']])) { - $users[$user['id']] = $user; - } - } - - return array_values($users); - } - - /** - * Enable notification for someone - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function enableNotification($user_id) - { - return $this->db->table(User::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 1)); - } - - /** - * Disable notification for someone - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function disableNotification($user_id) - { - return $this->db->table(User::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 0)); - } - - /** - * Save settings for the given user - * - * @access public - * @param integer $user_id User id - * @param array $values Form values - */ - public function saveSettings($user_id, array $values) - { - $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); - - if (! empty($types)) { - $this->enableNotification($user_id); - } else { - $this->disableNotification($user_id); - } - - $filter = empty($values['notifications_filter']) ? UserNotificationFilter::FILTER_BOTH : $values['notifications_filter']; - $project_ids = empty($values['notification_projects']) ? array() : array_keys($values['notification_projects']); - - $this->userNotificationFilter->saveFilter($user_id, $filter); - $this->userNotificationFilter->saveSelectedProjects($user_id, $project_ids); - $this->userNotificationType->saveSelectedTypes($user_id, $types); - } - - /** - * Read user settings to display the form - * - * @access public - * @param integer $user_id User id - * @return array - */ - public function readSettings($user_id) - { - $values = $this->db->table(User::TABLE)->eq('id', $user_id)->columns('notifications_enabled', 'notifications_filter')->findOne(); - $values['notification_types'] = $this->userNotificationType->getSelectedTypes($user_id); - $values['notification_projects'] = $this->userNotificationFilter->getSelectedProjects($user_id); - return $values; - } - - /** - * Get a list of group members with notification enabled - * - * @access private - * @param integer $project_id Project id - * @param integer $exclude_user_id User id to exclude - * @return array - */ - private function getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id) - { - return $this->db - ->table(ProjectUserRole::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', User::TABLE.'.email', User::TABLE.'.language', User::TABLE.'.notifications_filter') - ->join(User::TABLE, 'id', 'user_id') - ->eq(ProjectUserRole::TABLE.'.project_id', $project_id) - ->eq(User::TABLE.'.notifications_enabled', '1') - ->eq(User::TABLE.'.is_active', 1) - ->neq(User::TABLE.'.id', $exclude_user_id) - ->findAll(); - } - - private function getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id) - { - return $this->db - ->table(ProjectGroupRole::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', User::TABLE.'.email', User::TABLE.'.language', User::TABLE.'.notifications_filter') - ->join(GroupMember::TABLE, 'group_id', 'group_id', ProjectGroupRole::TABLE) - ->join(User::TABLE, 'id', 'user_id', GroupMember::TABLE) - ->eq(ProjectGroupRole::TABLE.'.project_id', $project_id) - ->eq(User::TABLE.'.notifications_enabled', '1') - ->neq(User::TABLE.'.id', $exclude_user_id) - ->eq(User::TABLE.'.is_active', 1) - ->findAll(); - } - - /** - * Get a list of project members with notification enabled - * - * @access private - * @param integer $exclude_user_id User id to exclude - * @return array - */ - private function getEverybodyWithNotificationEnabled($exclude_user_id) - { - return $this->db - ->table(User::TABLE) - ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name', User::TABLE.'.email', User::TABLE.'.language', User::TABLE.'.notifications_filter') - ->eq('notifications_enabled', '1') - ->neq(User::TABLE.'.id', $exclude_user_id) - ->eq(User::TABLE.'.is_active', 1) - ->findAll(); - } -} diff --git a/app/Model/UserNotificationFilter.php b/app/Model/UserNotificationFilter.php deleted file mode 100644 index d885da04..00000000 --- a/app/Model/UserNotificationFilter.php +++ /dev/null @@ -1,206 +0,0 @@ - t('All tasks'), - self::FILTER_ASSIGNEE => t('Only for tasks assigned to me'), - self::FILTER_CREATOR => t('Only for tasks created by me'), - self::FILTER_BOTH => t('Only for tasks created by me and assigned to me'), - ); - } - - /** - * Get user selected filter - * - * @access public - * @param integer $user_id - * @return integer - */ - public function getSelectedFilter($user_id) - { - return $this->db->table(User::TABLE)->eq('id', $user_id)->findOneColumn('notifications_filter'); - } - - /** - * Save selected filter for a user - * - * @access public - * @param integer $user_id - * @param string $filter - * @return boolean - */ - public function saveFilter($user_id, $filter) - { - return $this->db->table(User::TABLE)->eq('id', $user_id)->update(array( - 'notifications_filter' => $filter, - )); - } - - /** - * Get user selected projects - * - * @access public - * @param integer $user_id - * @return array - */ - public function getSelectedProjects($user_id) - { - return $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id'); - } - - /** - * Save selected projects for a user - * - * @access public - * @param integer $user_id - * @param integer[] $project_ids - * @return boolean - */ - public function saveSelectedProjects($user_id, array $project_ids) - { - $results = array(); - $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->remove(); - - foreach ($project_ids as $project_id) { - $results[] = $this->db->table(self::PROJECT_TABLE)->insert(array( - 'user_id' => $user_id, - 'project_id' => $project_id, - )); - } - - return !in_array(false, $results, true); - } - - /** - * Return true if the user should receive notification - * - * @access public - * @param array $user - * @param array $event_data - * @return boolean - */ - public function shouldReceiveNotification(array $user, array $event_data) - { - $filters = array( - 'filterNone', - 'filterAssignee', - 'filterCreator', - 'filterBoth', - ); - - foreach ($filters as $filter) { - if ($this->$filter($user, $event_data)) { - return $this->filterProject($user, $event_data); - } - } - - return false; - } - - /** - * Return true if the user will receive all notifications - * - * @access public - * @param array $user - * @return boolean - */ - public function filterNone(array $user) - { - return $user['notifications_filter'] == self::FILTER_NONE; - } - - /** - * Return true if the user is the assignee and selected the filter "assignee" - * - * @access public - * @param array $user - * @param array $event_data - * @return boolean - */ - public function filterAssignee(array $user, array $event_data) - { - return $user['notifications_filter'] == self::FILTER_ASSIGNEE && $event_data['task']['owner_id'] == $user['id']; - } - - /** - * Return true if the user is the creator and enabled the filter "creator" - * - * @access public - * @param array $user - * @param array $event_data - * @return boolean - */ - public function filterCreator(array $user, array $event_data) - { - return $user['notifications_filter'] == self::FILTER_CREATOR && $event_data['task']['creator_id'] == $user['id']; - } - - /** - * Return true if the user is the assignee or the creator and selected the filter "both" - * - * @access public - * @param array $user - * @param array $event_data - * @return boolean - */ - public function filterBoth(array $user, array $event_data) - { - return $user['notifications_filter'] == self::FILTER_BOTH && - ($event_data['task']['creator_id'] == $user['id'] || $event_data['task']['owner_id'] == $user['id']); - } - - /** - * Return true if the user want to receive notification for the selected project - * - * @access public - * @param array $user - * @param array $event_data - * @return boolean - */ - public function filterProject(array $user, array $event_data) - { - $projects = $this->getSelectedProjects($user['id']); - - if (! empty($projects)) { - return in_array($event_data['task']['project_id'], $projects); - } - - return true; - } -} diff --git a/app/Model/UserNotificationFilterModel.php b/app/Model/UserNotificationFilterModel.php new file mode 100644 index 00000000..112ba290 --- /dev/null +++ b/app/Model/UserNotificationFilterModel.php @@ -0,0 +1,206 @@ + t('All tasks'), + self::FILTER_ASSIGNEE => t('Only for tasks assigned to me'), + self::FILTER_CREATOR => t('Only for tasks created by me'), + self::FILTER_BOTH => t('Only for tasks created by me and assigned to me'), + ); + } + + /** + * Get user selected filter + * + * @access public + * @param integer $user_id + * @return integer + */ + public function getSelectedFilter($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->findOneColumn('notifications_filter'); + } + + /** + * Save selected filter for a user + * + * @access public + * @param integer $user_id + * @param string $filter + * @return boolean + */ + public function saveFilter($user_id, $filter) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array( + 'notifications_filter' => $filter, + )); + } + + /** + * Get user selected projects + * + * @access public + * @param integer $user_id + * @return array + */ + public function getSelectedProjects($user_id) + { + return $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->findAllByColumn('project_id'); + } + + /** + * Save selected projects for a user + * + * @access public + * @param integer $user_id + * @param integer[] $project_ids + * @return boolean + */ + public function saveSelectedProjects($user_id, array $project_ids) + { + $results = array(); + $this->db->table(self::PROJECT_TABLE)->eq('user_id', $user_id)->remove(); + + foreach ($project_ids as $project_id) { + $results[] = $this->db->table(self::PROJECT_TABLE)->insert(array( + 'user_id' => $user_id, + 'project_id' => $project_id, + )); + } + + return !in_array(false, $results, true); + } + + /** + * Return true if the user should receive notification + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function shouldReceiveNotification(array $user, array $event_data) + { + $filters = array( + 'filterNone', + 'filterAssignee', + 'filterCreator', + 'filterBoth', + ); + + foreach ($filters as $filter) { + if ($this->$filter($user, $event_data)) { + return $this->filterProject($user, $event_data); + } + } + + return false; + } + + /** + * Return true if the user will receive all notifications + * + * @access public + * @param array $user + * @return boolean + */ + public function filterNone(array $user) + { + return $user['notifications_filter'] == self::FILTER_NONE; + } + + /** + * Return true if the user is the assignee and selected the filter "assignee" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterAssignee(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_ASSIGNEE && $event_data['task']['owner_id'] == $user['id']; + } + + /** + * Return true if the user is the creator and enabled the filter "creator" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterCreator(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_CREATOR && $event_data['task']['creator_id'] == $user['id']; + } + + /** + * Return true if the user is the assignee or the creator and selected the filter "both" + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterBoth(array $user, array $event_data) + { + return $user['notifications_filter'] == self::FILTER_BOTH && + ($event_data['task']['creator_id'] == $user['id'] || $event_data['task']['owner_id'] == $user['id']); + } + + /** + * Return true if the user want to receive notification for the selected project + * + * @access public + * @param array $user + * @param array $event_data + * @return boolean + */ + public function filterProject(array $user, array $event_data) + { + $projects = $this->getSelectedProjects($user['id']); + + if (! empty($projects)) { + return in_array($event_data['task']['project_id'], $projects); + } + + return true; + } +} diff --git a/app/Model/UserNotificationModel.php b/app/Model/UserNotificationModel.php new file mode 100644 index 00000000..d77526f6 --- /dev/null +++ b/app/Model/UserNotificationModel.php @@ -0,0 +1,204 @@ +getUsersWithNotificationEnabled($event_data['task']['project_id'], $this->userSession->getId()); + + foreach ($users as $user) { + if ($this->userNotificationFilterModel->shouldReceiveNotification($user, $event_data)) { + $this->sendUserNotification($user, $event_name, $event_data); + } + } + } + + /** + * Send notification to someone + * + * @access public + * @param array $user User + * @param string $event_name + * @param array $event_data + */ + public function sendUserNotification(array $user, $event_name, array $event_data) + { + Translator::unload(); + + // Use the user language otherwise use the application language (do not use the session language) + if (! empty($user['language'])) { + Translator::load($user['language']); + } else { + Translator::load($this->configModel->get('application_language', 'en_US')); + } + + foreach ($this->userNotificationTypeModel->getSelectedTypes($user['id']) as $type) { + $this->userNotificationTypeModel->getType($type)->notifyUser($user, $event_name, $event_data); + } + + // Restore locales + $this->languageModel->loadCurrentLanguage(); + } + + /** + * Get a list of people with notifications enabled + * + * @access public + * @param integer $project_id Project id + * @param integer $exclude_user_id User id to exclude + * @return array + */ + public function getUsersWithNotificationEnabled($project_id, $exclude_user_id = 0) + { + if ($this->projectPermissionModel->isEverybodyAllowed($project_id)) { + return $this->getEverybodyWithNotificationEnabled($exclude_user_id); + } + + $users = array(); + $members = $this->getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id); + $groups = $this->getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id); + + foreach (array_merge($members, $groups) as $user) { + if (! isset($users[$user['id']])) { + $users[$user['id']] = $user; + } + } + + return array_values($users); + } + + /** + * Enable notification for someone + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function enableNotification($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 1)); + } + + /** + * Disable notification for someone + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disableNotification($user_id) + { + return $this->db->table(UserModel::TABLE)->eq('id', $user_id)->update(array('notifications_enabled' => 0)); + } + + /** + * Save settings for the given user + * + * @access public + * @param integer $user_id User id + * @param array $values Form values + */ + public function saveSettings($user_id, array $values) + { + $types = empty($values['notification_types']) ? array() : array_keys($values['notification_types']); + + if (! empty($types)) { + $this->enableNotification($user_id); + } else { + $this->disableNotification($user_id); + } + + $filter = empty($values['notifications_filter']) ? UserNotificationFilterModel::FILTER_BOTH : $values['notifications_filter']; + $project_ids = empty($values['notification_projects']) ? array() : array_keys($values['notification_projects']); + + $this->userNotificationFilterModel->saveFilter($user_id, $filter); + $this->userNotificationFilterModel->saveSelectedProjects($user_id, $project_ids); + $this->userNotificationTypeModel->saveSelectedTypes($user_id, $types); + } + + /** + * Read user settings to display the form + * + * @access public + * @param integer $user_id User id + * @return array + */ + public function readSettings($user_id) + { + $values = $this->db->table(UserModel::TABLE)->eq('id', $user_id)->columns('notifications_enabled', 'notifications_filter')->findOne(); + $values['notification_types'] = $this->userNotificationTypeModel->getSelectedTypes($user_id); + $values['notification_projects'] = $this->userNotificationFilterModel->getSelectedProjects($user_id); + return $values; + } + + /** + * Get a list of group members with notification enabled + * + * @access private + * @param integer $project_id Project id + * @param integer $exclude_user_id User id to exclude + * @return array + */ + private function getProjectUserMembersWithNotificationEnabled($project_id, $exclude_user_id) + { + return $this->db + ->table(ProjectUserRoleModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') + ->join(UserModel::TABLE, 'id', 'user_id') + ->eq(ProjectUserRoleModel::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.notifications_enabled', '1') + ->eq(UserModel::TABLE.'.is_active', 1) + ->neq(UserModel::TABLE.'.id', $exclude_user_id) + ->findAll(); + } + + private function getProjectGroupMembersWithNotificationEnabled($project_id, $exclude_user_id) + { + return $this->db + ->table(ProjectGroupRoleModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') + ->join(GroupMemberModel::TABLE, 'group_id', 'group_id', ProjectGroupRoleModel::TABLE) + ->join(UserModel::TABLE, 'id', 'user_id', GroupMemberModel::TABLE) + ->eq(ProjectGroupRoleModel::TABLE.'.project_id', $project_id) + ->eq(UserModel::TABLE.'.notifications_enabled', '1') + ->neq(UserModel::TABLE.'.id', $exclude_user_id) + ->eq(UserModel::TABLE.'.is_active', 1) + ->findAll(); + } + + /** + * Get a list of project members with notification enabled + * + * @access private + * @param integer $exclude_user_id User id to exclude + * @return array + */ + private function getEverybodyWithNotificationEnabled($exclude_user_id) + { + return $this->db + ->table(UserModel::TABLE) + ->columns(UserModel::TABLE.'.id', UserModel::TABLE.'.username', UserModel::TABLE.'.name', UserModel::TABLE.'.email', UserModel::TABLE.'.language', UserModel::TABLE.'.notifications_filter') + ->eq('notifications_enabled', '1') + ->neq(UserModel::TABLE.'.id', $exclude_user_id) + ->eq(UserModel::TABLE.'.is_active', 1) + ->findAll(); + } +} diff --git a/app/Model/UserNotificationType.php b/app/Model/UserNotificationType.php deleted file mode 100644 index d84cc690..00000000 --- a/app/Model/UserNotificationType.php +++ /dev/null @@ -1,54 +0,0 @@ -db->table(self::TABLE)->eq('user_id', $user_id)->asc('notification_type')->findAllByColumn('notification_type'); - return $this->filterTypes($types); - } - - /** - * Save notification types for a given user - * - * @access public - * @param integer $user_id - * @param string[] $types - * @return boolean - */ - public function saveSelectedTypes($user_id, array $types) - { - $results = array(); - $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); - - foreach ($types as $type) { - $results[] = $this->db->table(self::TABLE)->insert(array('user_id' => $user_id, 'notification_type' => $type)); - } - - return ! in_array(false, $results, true); - } -} diff --git a/app/Model/UserNotificationTypeModel.php b/app/Model/UserNotificationTypeModel.php new file mode 100644 index 00000000..0f377220 --- /dev/null +++ b/app/Model/UserNotificationTypeModel.php @@ -0,0 +1,52 @@ +db->table(self::TABLE)->eq('user_id', $user_id)->asc('notification_type')->findAllByColumn('notification_type'); + return $this->filterTypes($types); + } + + /** + * Save notification types for a given user + * + * @access public + * @param integer $user_id + * @param string[] $types + * @return boolean + */ + public function saveSelectedTypes($user_id, array $types) + { + $results = array(); + $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); + + foreach ($types as $type) { + $results[] = $this->db->table(self::TABLE)->insert(array('user_id' => $user_id, 'notification_type' => $type)); + } + + return ! in_array(false, $results, true); + } +} diff --git a/app/Model/UserUnreadNotification.php b/app/Model/UserUnreadNotification.php deleted file mode 100644 index f3fcd601..00000000 --- a/app/Model/UserUnreadNotification.php +++ /dev/null @@ -1,117 +0,0 @@ -db->table(self::TABLE)->insert(array( - 'user_id' => $user_id, - 'date_creation' => time(), - 'event_name' => $event_name, - 'event_data' => json_encode($event_data), - )); - } - - /** - * Get one notification - * - * @param integer $notification_id - * @return array|null - */ - public function getById($notification_id) - { - $notification = $this->db->table(self::TABLE)->eq('id', $notification_id)->findOne(); - - if (! empty($notification)) { - $this->unserialize($notification); - } - - return $notification; - } - - /** - * Get all notifications for a user - * - * @access public - * @param integer $user_id - * @return array - */ - public function getAll($user_id) - { - $events = $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('date_creation')->findAll(); - - foreach ($events as &$event) { - $this->unserialize($event); - } - - return $events; - } - - /** - * Mark a notification as read - * - * @access public - * @param integer $user_id - * @param integer $notification_id - * @return boolean - */ - public function markAsRead($user_id, $notification_id) - { - return $this->db->table(self::TABLE)->eq('id', $notification_id)->eq('user_id', $user_id)->remove(); - } - - /** - * Mark all notifications as read for a user - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function markAllAsRead($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); - } - - /** - * Return true if the user as unread notifications - * - * @access public - * @param integer $user_id - * @return boolean - */ - public function hasNotifications($user_id) - { - return $this->db->table(self::TABLE)->eq('user_id', $user_id)->exists(); - } - - private function unserialize(&$event) - { - $event['event_data'] = json_decode($event['event_data'], true); - $event['title'] = $this->notification->getTitleWithoutAuthor($event['event_name'], $event['event_data']); - } -} diff --git a/app/Model/UserUnreadNotificationModel.php b/app/Model/UserUnreadNotificationModel.php new file mode 100644 index 00000000..6c930eef --- /dev/null +++ b/app/Model/UserUnreadNotificationModel.php @@ -0,0 +1,117 @@ +db->table(self::TABLE)->insert(array( + 'user_id' => $user_id, + 'date_creation' => time(), + 'event_name' => $event_name, + 'event_data' => json_encode($event_data), + )); + } + + /** + * Get one notification + * + * @param integer $notification_id + * @return array|null + */ + public function getById($notification_id) + { + $notification = $this->db->table(self::TABLE)->eq('id', $notification_id)->findOne(); + + if (! empty($notification)) { + $this->unserialize($notification); + } + + return $notification; + } + + /** + * Get all notifications for a user + * + * @access public + * @param integer $user_id + * @return array + */ + public function getAll($user_id) + { + $events = $this->db->table(self::TABLE)->eq('user_id', $user_id)->asc('date_creation')->findAll(); + + foreach ($events as &$event) { + $this->unserialize($event); + } + + return $events; + } + + /** + * Mark a notification as read + * + * @access public + * @param integer $user_id + * @param integer $notification_id + * @return boolean + */ + public function markAsRead($user_id, $notification_id) + { + return $this->db->table(self::TABLE)->eq('id', $notification_id)->eq('user_id', $user_id)->remove(); + } + + /** + * Mark all notifications as read for a user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function markAllAsRead($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->remove(); + } + + /** + * Return true if the user as unread notifications + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function hasNotifications($user_id) + { + return $this->db->table(self::TABLE)->eq('user_id', $user_id)->exists(); + } + + private function unserialize(&$event) + { + $event['event_data'] = json_decode($event['event_data'], true); + $event['title'] = $this->notificationModel->getTitleWithoutAuthor($event['event_name'], $event['event_data']); + } +} -- cgit v1.2.3