From d138834dcf902534f78237939926e97fd9a0eebe Mon Sep 17 00:00:00 2001 From: Frédéric Guillot Date: Sun, 5 Oct 2014 19:40:57 -0400 Subject: Regular users are able to create private projects --- app/Controller/Action.php | 13 ++- app/Controller/Base.php | 29 ++++- app/Controller/Board.php | 78 +++++-------- app/Controller/Category.php | 14 +-- app/Controller/Project.php | 221 +++++++++++++----------------------- app/Locales/de_DE/translations.php | 4 +- app/Locales/es_ES/translations.php | 4 +- app/Locales/fi_FI/translations.php | 4 +- app/Locales/fr_FR/translations.php | 6 +- app/Locales/it_IT/translations.php | 4 +- app/Locales/pl_PL/translations.php | 4 +- app/Locales/pt_BR/translations.php | 4 +- app/Locales/ru_RU/translations.php | 4 +- app/Locales/sv_SE/translations.php | 4 +- app/Locales/zh_CN/translations.php | 4 +- app/Model/Acl.php | 6 +- app/Model/Board.php | 23 ++++ app/Model/Project.php | 68 ++++++----- app/Model/ProjectPermission.php | 19 +++- app/Schema/Mysql.php | 7 +- app/Schema/Postgres.php | 7 +- app/Schema/Sqlite.php | 7 +- app/Templates/action_remove.php | 4 +- app/Templates/board_edit.php | 6 +- app/Templates/board_remove.php | 4 +- app/Templates/project_disable.php | 4 +- app/Templates/project_duplicate.php | 4 +- app/Templates/project_edit.php | 4 +- app/Templates/project_enable.php | 4 +- app/Templates/project_index.php | 17 ++- app/Templates/project_layout.php | 4 +- app/Templates/project_new.php | 9 +- app/Templates/project_remove.php | 4 +- app/Templates/project_share.php | 12 +- app/Templates/project_show.php | 12 +- app/Templates/project_sidebar.php | 22 ++-- app/Templates/project_users.php | 6 +- 37 files changed, 338 insertions(+), 312 deletions(-) (limited to 'app') diff --git a/app/Controller/Action.php b/app/Controller/Action.php index 44ab6430..714c87f3 100644 --- a/app/Controller/Action.php +++ b/app/Controller/Action.php @@ -17,7 +17,7 @@ class Action extends Base */ public function index() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $this->response->html($this->projectLayout('action_index', array( 'values' => array('project_id' => $project['id']), @@ -43,7 +43,7 @@ class Action extends Base */ public function event() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id'])) { @@ -66,7 +66,7 @@ class Action extends Base */ public function params() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $values = $this->request->getValues(); if (empty($values['action_name']) || empty($values['project_id']) || empty($values['event_name'])) { @@ -104,7 +104,7 @@ class Action extends Base */ public function create() { - $this->doCreation($this->getProject(), $this->request->getValues()); + $this->doCreation($this->getProjectManagement(), $this->request->getValues()); } /** @@ -138,7 +138,7 @@ class Action extends Base */ public function confirm() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $this->response->html($this->projectLayout('action_remove', array( 'action' => $this->action->getById($this->request->getIntegerParam('action_id')), @@ -158,6 +158,7 @@ class Action extends Base public function remove() { $this->checkCSRFParam(); + $project = $this->getProjectManagement(); $action = $this->action->getById($this->request->getIntegerParam('action_id')); if ($action && $this->action->remove($action['id'])) { @@ -166,6 +167,6 @@ class Action extends Base $this->session->flashError(t('Unable to remove this action.')); } - $this->response->redirect('?controller=action&action=index&project_id='.$action['project_id']); + $this->response->redirect('?controller=action&action=index&project_id='.$project['id']); } } diff --git a/app/Controller/Base.php b/app/Controller/Base.php index 9c1416fc..f24ea8ff 100644 --- a/app/Controller/Base.php +++ b/app/Controller/Base.php @@ -209,11 +209,8 @@ abstract class Base */ protected function checkProjectPermissions($project_id) { - if ($this->acl->isRegularUser()) { - - if ($project_id > 0 && ! $this->projectPermission->isUserAllowed($project_id, $this->acl->getUserId())) { - $this->forbidden(); - } + if ($this->acl->isRegularUser() && ! $this->projectPermission->isUserAllowed($project_id, $this->acl->getUserId())) { + $this->forbidden(); } } @@ -260,6 +257,7 @@ abstract class Base { $content = $this->template->load($template, $params); $params['project_content_for_layout'] = $content; + $params['menu'] = 'projects'; return $this->template->layout('project_layout', $params); } @@ -304,4 +302,25 @@ abstract class Base return $project; } + + /** + * Common method to get a project with administration rights + * + * @access protected + * @return array + */ + protected function getProjectManagement() + { + $project = $this->project->getById($this->request->getIntegerParam('project_id')); + + if (! $project) { + $this->notfound(); + } + + if ($this->acl->isRegularUser() && ! $this->projectPermission->adminAllowed($project['id'], $this->acl->getUserId())) { + $this->forbidden(); + } + + return $project; + } } diff --git a/app/Controller/Board.php b/app/Controller/Board.php index 0e1dd3e1..d49ad021 100644 --- a/app/Controller/Board.php +++ b/app/Controller/Board.php @@ -15,35 +15,22 @@ use Core\Security; class Board extends Base { /** - * Move a column up + * Move a column down or up * * @access public */ - public function moveUp() + public function moveColumn() { $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); $column_id = $this->request->getIntegerParam('column_id'); + $direction = $this->request->getStringParam('direction'); - $this->board->moveUp($project_id, $column_id); - - $this->response->redirect('?controller=board&action=edit&project_id='.$project_id); - } - - /** - * Move a column down - * - * @access public - */ - public function moveDown() - { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); - $column_id = $this->request->getIntegerParam('column_id'); - - $this->board->moveDown($project_id, $column_id); + if ($direction === 'up' || $direction === 'down') { + $this->board->{'move'.$direction}($project['id'], $column_id); + } - $this->response->redirect('?controller=board&action=edit&project_id='.$project_id); + $this->response->redirect('?controller=board&action=edit&project_id='.$project['id']); } /** @@ -232,11 +219,11 @@ class Board extends Base 'filters' => array('user_id' => UserModel::EVERYBODY_ID), 'projects' => $projects, 'current_project_id' => $project['id'], - 'current_project_name' => $projects[$project['id']], + 'current_project_name' => $project['name'], 'board' => $this->board->get($project['id']), 'categories' => $this->category->getList($project['id'], true, true), 'menu' => 'boards', - 'title' => $projects[$project['id']], + 'title' => $project['name'], 'board_selector' => $board_selector, 'board_private_refresh_interval' => $this->config->get('board_private_refresh_interval'), 'board_highlight_period' => $this->config->get('board_highlight_period'), @@ -250,7 +237,7 @@ class Board extends Base */ public function edit() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $columns = $this->board->getColumns($project['id']); $values = array(); @@ -276,7 +263,7 @@ class Board extends Base */ public function update() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $columns = $this->board->getColumns($project['id']); $data = $this->request->getValues(); $values = $columns_list = array(); @@ -317,7 +304,7 @@ class Board extends Base */ public function add() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $columns = $this->board->getColumnsList($project['id']); $data = $this->request->getValues(); $values = array(); @@ -350,13 +337,27 @@ class Board extends Base } /** - * Confirmation dialog before removing a column + * Remove a column * * @access public */ - public function confirm() + public function remove() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); + + if ($this->request->getStringParam('remove') === 'yes') { + + $this->checkCSRFParam(); + $column = $this->board->getColumn($this->request->getIntegerParam('column_id')); + + if ($column && $this->board->removeColumn($column['id'])) { + $this->session->flash(t('Column removed successfully.')); + } else { + $this->session->flashError(t('Unable to remove this column.')); + } + + $this->response->redirect('?controller=board&action=edit&project_id='.$project['id']); + } $this->response->html($this->projectLayout('board_remove', array( 'column' => $this->board->getColumn($this->request->getIntegerParam('column_id')), @@ -366,25 +367,6 @@ class Board extends Base ))); } - /** - * Remove a column - * - * @access public - */ - public function remove() - { - $this->checkCSRFParam(); - $column = $this->board->getColumn($this->request->getIntegerParam('column_id')); - - if ($column && $this->board->removeColumn($column['id'])) { - $this->session->flash(t('Column removed successfully.')); - } else { - $this->session->flashError(t('Unable to remove this column.')); - } - - $this->response->redirect('?controller=board&action=edit&project_id='.$column['project_id']); - } - /** * Save the board (Ajax request made by the drag and drop) * diff --git a/app/Controller/Category.php b/app/Controller/Category.php index 3c9d0523..38322294 100644 --- a/app/Controller/Category.php +++ b/app/Controller/Category.php @@ -3,7 +3,7 @@ namespace Controller; /** - * Categories management + * Category management * * @package controller * @author Frederic Guillot @@ -36,7 +36,7 @@ class Category extends Base */ public function index() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $this->response->html($this->projectLayout('category_index', array( 'categories' => $this->category->getList($project['id'], false), @@ -55,7 +55,7 @@ class Category extends Base */ public function save() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $values = $this->request->getValues(); list($valid, $errors) = $this->category->validateCreation($values); @@ -88,7 +88,7 @@ class Category extends Base */ public function edit() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $category = $this->getCategory($project['id']); $this->response->html($this->projectLayout('category_edit', array( @@ -107,7 +107,7 @@ class Category extends Base */ public function update() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $values = $this->request->getValues(); list($valid, $errors) = $this->category->validateModification($values); @@ -139,7 +139,7 @@ class Category extends Base */ public function confirm() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $category = $this->getCategory($project['id']); $this->response->html($this->projectLayout('category_remove', array( @@ -158,7 +158,7 @@ class Category extends Base public function remove() { $this->checkCSRFParam(); - $project = $this->getProject(); + $project = $this->getProjectManagement(); $category = $this->getCategory($project['id']); if ($this->category->remove($category['id'])) { diff --git a/app/Controller/Project.php b/app/Controller/Project.php index 48f6f10e..b1c67960 100644 --- a/app/Controller/Project.php +++ b/app/Controller/Project.php @@ -3,7 +3,6 @@ namespace Controller; use Model\Task as TaskModel; -use Core\Translator; /** * Project controller @@ -55,7 +54,6 @@ class Project extends Base $this->response->html($this->projectLayout('project_show', array( 'project' => $project, 'stats' => $this->project->getStats($project['id']), - 'menu' => 'projects', 'title' => $project['name'], ))); } @@ -67,7 +65,7 @@ class Project extends Base */ public function export() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $from = $this->request->getStringParam('from'); $to = $this->request->getStringParam('to'); @@ -88,7 +86,6 @@ class Project extends Base 'errors' => array(), 'date_format' => $this->config->get('application_date_format'), 'date_formats' => $this->dateParser->getAvailableFormats(), - 'menu' => 'projects', 'project' => $project, 'title' => t('Tasks Export') ))); @@ -101,51 +98,26 @@ class Project extends Base */ public function share() { - $project = $this->getProject(); - - $this->response->html($this->projectLayout('project_share', array( - 'project' => $project, - 'menu' => 'projects', - 'title' => t('Public access'), - ))); - } - - /** - * Enable public access for a project - * - * @access public - */ - public function enablePublic() - { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); + $switch = $this->request->getStringParam('switch'); - if ($project_id && $this->project->enablePublicAccess($project_id)) { - $this->session->flash(t('Project updated successfully.')); - } else { - $this->session->flashError(t('Unable to update this project.')); - } + if ($switch === 'enable' || $switch === 'disable') { - $this->response->redirect('?controller=project&action=share&project_id='.$project_id); - } + $this->checkCSRFParam(); - /** - * Disable public access for a project - * - * @access public - */ - public function disablePublic() - { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + if ($this->project->{$switch.'PublicAccess'}($project['id'])) { + $this->session->flash(t('Project updated successfully.')); + } else { + $this->session->flashError(t('Unable to update this project.')); + } - if ($project_id && $this->project->disablePublicAccess($project_id)) { - $this->session->flash(t('Project updated successfully.')); - } else { - $this->session->flashError(t('Unable to update this project.')); + $this->response->redirect('?controller=project&action=share&project_id='.$project['id']); } - $this->response->redirect('?controller=project&action=share&project_id='.$project_id); + $this->response->html($this->projectLayout('project_share', array( + 'project' => $project, + 'title' => t('Public access'), + ))); } /** @@ -155,13 +127,12 @@ class Project extends Base */ public function edit() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $this->response->html($this->projectLayout('project_edit', array( 'errors' => array(), 'values' => $project, 'project' => $project, - 'menu' => 'projects', 'title' => t('Edit project') ))); } @@ -173,7 +144,7 @@ class Project extends Base */ public function update() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $values = $this->request->getValues() + array('is_active' => 0); list($valid, $errors) = $this->project->validateModification($values); @@ -192,30 +163,28 @@ class Project extends Base 'errors' => $errors, 'values' => $values, 'project' => $project, - 'menu' => 'projects', 'title' => t('Edit Project') ))); } - /** + /** * Users list for the selected project * * @access public */ public function users() { - $project = $this->getProject(); + $project = $this->getProjectManagement(); $this->response->html($this->projectLayout('project_users', array( 'project' => $project, 'users' => $this->projectPermission->getAllUsers($project['id']), - 'menu' => 'projects', 'title' => t('Edit project access list') ))); } /** - * Allow a specific user for the selected project + * Allow a specific user (admin only) * * @access public */ @@ -238,7 +207,7 @@ class Project extends Base } /** - * Revoke user access + * Revoke user access (admin only) * * @access public */ @@ -266,22 +235,6 @@ class Project extends Base $this->response->redirect('?controller=project&action=users&project_id='.$values['project_id']); } - /** - * Confirmation dialog before to remove a project - * - * @access public - */ - public function confirmRemove() - { - $project = $this->getProject(); - - $this->response->html($this->projectLayout('project_remove', array( - 'project' => $project, - 'menu' => 'projects', - 'title' => t('Remove project') - ))); - } - /** * Remove a project * @@ -289,31 +242,24 @@ class Project extends Base */ public function remove() { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); - if ($project_id && $this->project->remove($project_id)) { - $this->session->flash(t('Project removed successfully.')); - } else { - $this->session->flashError(t('Unable to remove this project.')); - } + if ($this->request->getStringParam('remove') === 'yes') { - $this->response->redirect('?controller=project'); - } + $this->checkCSRFParam(); - /** - * Confirmation dialog before to clone a project - * - * @access public - */ - public function confirmDuplicate() - { - $project = $this->getProject(); + if ($this->project->remove($project['id'])) { + $this->session->flash(t('Project removed successfully.')); + } else { + $this->session->flashError(t('Unable to remove this project.')); + } - $this->response->html($this->projectLayout('project_duplicate', array( + $this->response->redirect('?controller=project'); + } + + $this->response->html($this->projectLayout('project_remove', array( 'project' => $project, - 'menu' => 'projects', - 'title' => t('Clone this project') + 'title' => t('Remove project') ))); } @@ -325,31 +271,24 @@ class Project extends Base */ public function duplicate() { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); - if ($project_id && $this->project->duplicate($project_id)) { - $this->session->flash(t('Project cloned successfully.')); - } else { - $this->session->flashError(t('Unable to clone this project.')); - } + if ($this->request->getStringParam('duplicate') === 'yes') { - $this->response->redirect('?controller=project'); - } + $this->checkCSRFParam(); - /** - * Confirmation dialog before to disable a project - * - * @access public - */ - public function confirmDisable() - { - $project = $this->getProject(); + if ($this->project->duplicate($project['id'])) { + $this->session->flash(t('Project cloned successfully.')); + } else { + $this->session->flashError(t('Unable to clone this project.')); + } - $this->response->html($this->projectLayout('project_disable', array( + $this->response->redirect('?controller=project'); + } + + $this->response->html($this->projectLayout('project_duplicate', array( 'project' => $project, - 'menu' => 'projects', - 'title' => t('Project activation') + 'title' => t('Clone this project') ))); } @@ -360,30 +299,23 @@ class Project extends Base */ public function disable() { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); - if ($project_id && $this->project->disable($project_id)) { - $this->session->flash(t('Project disabled successfully.')); - } else { - $this->session->flashError(t('Unable to disable this project.')); - } + if ($this->request->getStringParam('disable') === 'yes') { - $this->response->redirect('?controller=project&action=show&project_id='.$project_id); - } + $this->checkCSRFParam(); - /** - * Confirmation dialog before to enable a project - * - * @access public - */ - public function confirmEnable() - { - $project = $this->getProject(); + if ($this->project->disable($project['id'])) { + $this->session->flash(t('Project disabled successfully.')); + } else { + $this->session->flashError(t('Unable to disable this project.')); + } - $this->response->html($this->projectLayout('project_enable', array( + $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); + } + + $this->response->html($this->projectLayout('project_disable', array( 'project' => $project, - 'menu' => 'projects', 'title' => t('Project activation') ))); } @@ -395,20 +327,29 @@ class Project extends Base */ public function enable() { - $this->checkCSRFParam(); - $project_id = $this->request->getIntegerParam('project_id'); + $project = $this->getProjectManagement(); - if ($project_id && $this->project->enable($project_id)) { - $this->session->flash(t('Project activated successfully.')); - } else { - $this->session->flashError(t('Unable to activate this project.')); + if ($this->request->getStringParam('enable') === 'yes') { + + $this->checkCSRFParam(); + + if ($this->project->enable($project['id'])) { + $this->session->flash(t('Project activated successfully.')); + } else { + $this->session->flashError(t('Unable to activate this project.')); + } + + $this->response->redirect('?controller=project&action=show&project_id='.$project['id']); } - $this->response->redirect('?controller=project&action=show&project_id='.$project_id); + $this->response->html($this->projectLayout('project_enable', array( + 'project' => $project, + 'title' => t('Project activation') + ))); } /** - * RSS feed for a project + * RSS feed for a project (public) * * @access public */ @@ -480,7 +421,6 @@ class Project extends Base 'action' => 'search', 'project_id' => $project['id'], ), - 'menu' => 'projects', 'project' => $project, 'columns' => $this->board->getColumnsList($project['id']), 'categories' => $this->category->getList($project['id'], false), @@ -506,7 +446,6 @@ class Project extends Base $nb_tasks = count($tasks); $this->response->html($this->template->layout('project_tasks', array( - 'menu' => 'projects', 'project' => $project, 'columns' => $this->board->getColumnsList($project['id']), 'categories' => $this->category->getList($project['id'], false), @@ -525,8 +464,9 @@ class Project extends Base { $this->response->html($this->template->layout('project_new', array( 'errors' => array(), - 'values' => array(), - 'menu' => 'projects', + 'values' => array( + 'is_private' => $this->request->getIntegerParam('private', $this->acl->isRegularUser()), + ), 'title' => t('New project') ))); } @@ -543,7 +483,7 @@ class Project extends Base if ($valid) { - if ($this->project->create($values)) { + if ($this->project->create($values, $this->acl->getUserId())) { $this->session->flash(t('Your project have been created successfully.')); $this->response->redirect('?controller=project'); } @@ -555,7 +495,6 @@ class Project extends Base $this->response->html($this->template->layout('project_new', array( 'errors' => $errors, 'values' => $values, - 'menu' => 'projects', 'title' => t('New Project') ))); } diff --git a/app/Locales/de_DE/translations.php b/app/Locales/de_DE/translations.php index db6e83ed..664e1c0e 100644 --- a/app/Locales/de_DE/translations.php +++ b/app/Locales/de_DE/translations.php @@ -420,7 +420,7 @@ return array( // 'I want to receive notifications only for those projects:' => '', // 'view the task on Kanboard' => '', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/es_ES/translations.php b/app/Locales/es_ES/translations.php index cd611bf8..218eef07 100644 --- a/app/Locales/es_ES/translations.php +++ b/app/Locales/es_ES/translations.php @@ -420,7 +420,7 @@ return array( 'I want to receive notifications only for those projects:' => 'Quiero recibir notificaciones sólo de estos proyectos:', 'view the task on Kanboard' => 'ver la tarea en Kanboard', 'Public access' => 'Acceso público', - 'Categories management' => 'Gestión de Categorías', + 'Category management' => 'Gestión de Categorías', 'User management' => 'Gestión de Usuarios', 'Active tasks' => 'Tareas activas', 'Disable public access' => 'Desactivar acceso público', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/fi_FI/translations.php b/app/Locales/fi_FI/translations.php index f832de66..cd384433 100644 --- a/app/Locales/fi_FI/translations.php +++ b/app/Locales/fi_FI/translations.php @@ -420,7 +420,7 @@ return array( // 'I want to receive notifications only for those projects:' => '', // 'view the task on Kanboard' => '', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/fr_FR/translations.php b/app/Locales/fr_FR/translations.php index dc7fcc72..03f00474 100644 --- a/app/Locales/fr_FR/translations.php +++ b/app/Locales/fr_FR/translations.php @@ -420,7 +420,7 @@ return array( 'I want to receive notifications only for those projects:' => 'Je souhaite reçevoir les notifications uniquement pour les projets sélectionnés :', 'view the task on Kanboard' => 'voir la tâche sur Kanboard', 'Public access' => 'Accès public', - 'Categories management' => 'Gestion des catégories', + 'Category management' => 'Gestion des catégories', 'User management' => 'Gestion des utilisateurs', 'Active tasks' => 'Tâches actives', 'Disable public access' => 'Désactiver l\'accès public', @@ -532,6 +532,8 @@ return array( 'Application URL' => 'URL de l\'application', 'Example: http://example.kanboard.net/ (used by email notifications)' => 'Exemple : http://exemple.kanboard.net/ (utilisé pour les notifications)', 'Token regenerated.' => 'Jeton de sécurité regénéré.', - 'Date format' => 'Format de date', + 'Date format' => 'Format des dates', 'ISO format is always accepted, example: "%s" and "%s"' => 'Le format ISO est toujours accepté, exemple : « %s » et « %s »', + 'New private project' => 'Nouveau projet privé', + 'This project is private' => 'Ce projet est privé', ); diff --git a/app/Locales/it_IT/translations.php b/app/Locales/it_IT/translations.php index 8e9343f6..f63221dc 100644 --- a/app/Locales/it_IT/translations.php +++ b/app/Locales/it_IT/translations.php @@ -420,7 +420,7 @@ return array( 'I want to receive notifications only for those projects:' => 'Vorrei ricevere le notifiche solo da questi progetti:', 'view the task on Kanboard' => 'vedi il compito su Kanboard', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/pl_PL/translations.php b/app/Locales/pl_PL/translations.php index e0e25f28..39df7b76 100644 --- a/app/Locales/pl_PL/translations.php +++ b/app/Locales/pl_PL/translations.php @@ -420,7 +420,7 @@ return array( // 'I want to receive notifications only for those projects:' => '', // 'view the task on Kanboard' => '', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/pt_BR/translations.php b/app/Locales/pt_BR/translations.php index b53650a7..cb158072 100644 --- a/app/Locales/pt_BR/translations.php +++ b/app/Locales/pt_BR/translations.php @@ -420,7 +420,7 @@ return array( // 'I want to receive notifications only for those projects:' => '', // 'view the task on Kanboard' => '', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/ru_RU/translations.php b/app/Locales/ru_RU/translations.php index 7783b781..fc0240ce 100644 --- a/app/Locales/ru_RU/translations.php +++ b/app/Locales/ru_RU/translations.php @@ -420,7 +420,7 @@ return array( 'I want to receive notifications only for those projects:' => 'Я хочу получать уведомления только по этим проектам :', 'view the task on Kanboard' => 'посмотреть задачу на Kanboard', 'Public access' => 'Общий доступ', - 'Categories management' => 'Управление категориями', + 'Category management' => 'Управление категориями', 'User management' => 'Управление пользователями', 'Active tasks' => 'Активные задачи', 'Disable public access' => 'Отключить общий доступ', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/sv_SE/translations.php b/app/Locales/sv_SE/translations.php index e8994fbb..98f208d0 100644 --- a/app/Locales/sv_SE/translations.php +++ b/app/Locales/sv_SE/translations.php @@ -420,7 +420,7 @@ return array( 'I want to receive notifications only for those projects:' => 'Jag vill endast få notiser för dessa projekt:', 'view the task on Kanboard' => 'Visa uppgiften på Kanboard', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Locales/zh_CN/translations.php b/app/Locales/zh_CN/translations.php index 8d4b3678..ed45befc 100644 --- a/app/Locales/zh_CN/translations.php +++ b/app/Locales/zh_CN/translations.php @@ -420,7 +420,7 @@ return array( // 'I want to receive notifications only for those projects:' => '', // 'view the task on Kanboard' => '', // 'Public access' => '', - // 'Categories management' => '', + // 'Category management' => '', // 'User management' => '', // 'Active tasks' => '', // 'Disable public access' => '', @@ -534,4 +534,6 @@ return array( // 'Token regenerated.' => '', // 'Date format' => '', // 'ISO format is always accepted, example: "%s" and "%s"' => '', + // 'New private project' => '', + // 'This project is private' => '', ); diff --git a/app/Model/Acl.php b/app/Model/Acl.php index 8c57425d..33fb13b7 100644 --- a/app/Model/Acl.php +++ b/app/Model/Acl.php @@ -32,13 +32,15 @@ class Acl extends Base */ private $user_actions = array( 'app' => array('index'), - 'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory'), - 'project' => array('tasks', 'index', 'forbidden', 'search', 'export', 'show', 'activity'), + 'board' => array('index', 'show', 'save', 'check', 'changeassignee', 'updateassignee', 'changecategory', 'updatecategory', 'movecolumn', 'edit', 'update', 'add', 'confirm', 'remove'), + 'project' => array('index', 'show', 'export', 'share', 'edit', 'update', 'users', 'remove', 'duplicate', 'disable', 'enable', 'activity', 'search', 'tasks', 'create', 'save'), 'user' => array('edit', 'forbidden', 'logout', 'show', 'external', 'unlinkgoogle', 'unlinkgithub', 'sessions', 'removesession', 'last', 'notifications', 'password'), 'comment' => array('create', 'save', 'confirm', 'remove', 'update', 'edit', 'forbidden'), 'file' => array('create', 'save', 'download', 'confirm', 'remove', 'open', 'image'), 'subtask' => array('create', 'save', 'edit', 'update', 'confirm', 'remove'), 'task' => array('show', 'create', 'save', 'edit', 'update', 'close', 'open', 'duplicate', 'remove', 'description', 'move', 'copy'), + 'category' => array('index', 'save', 'edit', 'update', 'confirm', 'remove'), + 'action' => array('index', 'event', 'params', 'create', 'confirm', 'remove'), ); /** diff --git a/app/Model/Board.php b/app/Model/Board.php index ac9cbdf9..728d9d29 100644 --- a/app/Model/Board.php +++ b/app/Model/Board.php @@ -31,6 +31,29 @@ class Board extends Base return array(t('Backlog'), t('Ready'), t('Work in progress'), t('Done')); } + /** + * Get user default columns + * + * @access public + * @return array + */ + public function getUserColumns() + { + $column_names = explode(',', $this->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); + } + } + + return $columns; + } + /** * Create a board with default columns, must be executed inside a transaction * diff --git a/app/Model/Project.php b/app/Model/Project.php index d2b769ed..b60ba567 100644 --- a/app/Model/Project.php +++ b/app/Model/Project.php @@ -83,6 +83,18 @@ class Project extends Base 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 (bool) $this->db->table(self::TABLE)->eq('id', $project_id)->eq('is_private', 1)->count(); + } + /** * Get all projects, optionaly fetch stats for each project and can check users permissions * @@ -204,16 +216,18 @@ class Project extends Base */ public function createProjectFromAnotherProject($project_id) { - $project_name = $this->db->table(self::TABLE)->eq('id', $project_id)->findOneColumn('name'); + $project = $this->getById($project_id); - $project = array( - 'name' => $project_name.' ('.t('Clone').')', + $values = array( + 'name' => $project['name'].' ('.t('Clone').')', 'is_active' => true, 'last_modified' => 0, 'token' => '', + 'is_public' => 0, + 'is_private' => empty($project['is_private']) ? 0 : 1, ); - if (! $this->db->table(self::TABLE)->save($project)) { + if (! $this->db->table(self::TABLE)->save($values)) { return false; } @@ -233,33 +247,18 @@ class Project extends Base // Get the cloned project Id $clone_project_id = $this->createProjectFromAnotherProject($project_id); - if (! $clone_project_id) { - $this->db->cancelTransaction(); - return false; - } - - // Clone Board - if (! $this->board->duplicate($project_id, $clone_project_id)) { - $this->db->cancelTransaction(); - return false; - } - // Clone Categories - if (! $this->category->duplicate($project_id, $clone_project_id)) { + if (! $clone_project_id) { $this->db->cancelTransaction(); return false; } - // Clone Allowed Users - if (! $this->projectPermission->duplicate($project_id, $clone_project_id)) { - $this->db->cancelTransaction(); - return false; - } + foreach (array('board', 'category', 'projectPermission', 'action') as $model) { - // Clone Actions - if (! $this->action->duplicate($project_id, $clone_project_id)) { - $this->db->cancelTransaction(); - return false; + if (! $this->$model->duplicate($project_id, $clone_project_id)) { + $this->db->cancelTransaction(); + return false; + } } $this->db->closeTransaction(); @@ -272,14 +271,16 @@ class Project extends Base * * @access public * @param array $values Form values + * @param integer $user_id User who create the project * @return integer Project id */ - public function create(array $values) + public function create(array $values, $user_id = 0) { $this->db->startTransaction(); $values['token'] = ''; $values['last_modified'] = time(); + $values['is_private'] = empty($values['is_private']) ? 0 : 1; if (! $this->db->table(self::TABLE)->save($values)) { $this->db->cancelTransaction(); @@ -287,19 +288,16 @@ class Project extends Base } $project_id = $this->db->getConnection()->getLastId(); - $column_names = explode(',', $this->config->get('board_columns', implode(',', $this->board->getDefaultColumns()))); - $columns = array(); - foreach ($column_names as $column_name) { - - $column_name = trim($column_name); + if (! $this->board->create($project_id, $this->board->getUserColumns())) { + $this->db->cancelTransaction(); + return false; + } - if (! empty($column_name)) { - $columns[] = array('title' => $column_name, 'task_limit' => 0); - } + if ($values['is_private'] && $user_id) { + $this->projectPermission->allowUser($project_id, $user_id); } - $this->board->create($project_id, $columns); $this->db->closeTransaction(); return (int) $project_id; diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php index 9d339f4d..b4466c20 100644 --- a/app/Model/ProjectPermission.php +++ b/app/Model/ProjectPermission.php @@ -142,12 +142,10 @@ class ProjectPermission extends Base */ public function isUserAllowed($project_id, $user_id) { - // Check if the user has admin rights if ($this->user->isAdmin($user_id)) { return true; } - // Otherwise, allow only specific users return (bool) $this->db ->table(self::TABLE) ->eq('project_id', $project_id) @@ -155,6 +153,23 @@ class ProjectPermission extends Base ->count(); } + /** + * Check if a specific user is allowed to manage a project + * + * @access public + * @param integer $project_id Project id + * @param integer $user_id User id + * @return bool + */ + public function adminAllowed($project_id, $user_id) + { + if ($this->isUserAllowed($project_id, $user_id) && $this->project->isPrivate($project_id)) { + return true; + } + + return false; + } + /** * Filter a list of projects for a given user * diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 8b6ba5ae..ab618635 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -5,7 +5,12 @@ namespace Schema; use PDO; use Core\Security; -const VERSION = 30; +const VERSION = 31; + +function version_31($pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_private TINYINT(1) DEFAULT '0'"); +} function version_30($pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 90a3da40..fe2cce54 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -5,7 +5,12 @@ namespace Schema; use PDO; use Core\Security; -const VERSION = 11; +const VERSION = 12; + +function version_12($pdo) +{ + $pdo->exec("ALTER TABLE projects ADD COLUMN is_private BOOLEAN DEFAULT '0'"); +} function version_11($pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index b39532f1..cefe8ab5 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -5,7 +5,12 @@ namespace Schema; use Core\Security; use PDO; -const VERSION = 30; +const VERSION = 31; + +function version_31($pdo) +{ + $pdo->exec('ALTER TABLE projects ADD COLUMN is_private INTEGER DEFAULT "0"'); +} function version_30($pdo) { diff --git a/app/Templates/action_remove.php b/app/Templates/action_remove.php index 4b574f11..668067da 100644 --- a/app/Templates/action_remove.php +++ b/app/Templates/action_remove.php @@ -8,7 +8,7 @@

- - + $project['id'], 'action_id' => $action['id']), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/board_edit.php b/app/Templates/board_edit.php index 8832e71d..cfaebc50 100644 --- a/app/Templates/board_edit.php +++ b/app/Templates/board_edit.php @@ -23,16 +23,16 @@ diff --git a/app/Templates/board_remove.php b/app/Templates/board_remove.php index d6fa9a88..4529063b 100644 --- a/app/Templates/board_remove.php +++ b/app/Templates/board_remove.php @@ -9,7 +9,7 @@

- - + $project['id'], 'column_id' => $column['id'], 'remove' => 'yes'), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/project_disable.php b/app/Templates/project_disable.php index 39f55570..7a729fa3 100644 --- a/app/Templates/project_disable.php +++ b/app/Templates/project_disable.php @@ -8,7 +8,7 @@

- - + $project['id'], 'disable' => 'yes'), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/project_duplicate.php b/app/Templates/project_duplicate.php index 32cbd5d8..a926dcd1 100644 --- a/app/Templates/project_duplicate.php +++ b/app/Templates/project_duplicate.php @@ -8,7 +8,7 @@

- - + $project['id'], 'duplicate' => 'yes'), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/project_edit.php b/app/Templates/project_edit.php index 4c9420f0..8eb2110d 100644 --- a/app/Templates/project_edit.php +++ b/app/Templates/project_edit.php @@ -1,7 +1,7 @@ -
+ @@ -9,8 +9,6 @@ -
-
diff --git a/app/Templates/project_enable.php b/app/Templates/project_enable.php index d2fce9f3..f2a1b0e7 100644 --- a/app/Templates/project_enable.php +++ b/app/Templates/project_enable.php @@ -8,7 +8,7 @@

- - + $project['id'], 'enable' => 'yes'), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/project_index.php b/app/Templates/project_index.php index 8b103c52..044ab95f 100644 --- a/app/Templates/project_index.php +++ b/app/Templates/project_index.php @@ -1,11 +1,12 @@
@@ -17,7 +18,10 @@
  • - + + + + $project['id'])) ?>
@@ -28,7 +32,10 @@
  • - + + + + $project['id'])) ?>
diff --git a/app/Templates/project_layout.php b/app/Templates/project_layout.php index 3af77a09..d69bbd53 100644 --- a/app/Templates/project_layout.php +++ b/app/Templates/project_layout.php @@ -2,8 +2,8 @@
diff --git a/app/Templates/project_new.php b/app/Templates/project_new.php index b4ed9990..e1ea5af7 100644 --- a/app/Templates/project_new.php +++ b/app/Templates/project_new.php @@ -1,20 +1,21 @@
- + +
- +
diff --git a/app/Templates/project_remove.php b/app/Templates/project_remove.php index 00771b5f..a98f94eb 100644 --- a/app/Templates/project_remove.php +++ b/app/Templates/project_remove.php @@ -8,7 +8,7 @@

- - + $project['id'], 'remove' => 'yes'), true, 'btn btn-red') ?> + $project['id'])) ?>
\ No newline at end of file diff --git a/app/Templates/project_share.php b/app/Templates/project_share.php index f06d7671..8edcbbc0 100644 --- a/app/Templates/project_share.php +++ b/app/Templates/project_share.php @@ -6,16 +6,14 @@
    -
  • -
  • +
  • $project['token'])) ?>
  • +
  • $project['token'])) ?>
- +
- + $project['id'], 'switch' => 'disable'), true, 'btn btn-red') ?> - - - + $project['id'], 'switch' => 'enable'), true, 'btn btn-blue') ?> diff --git a/app/Templates/project_show.php b/app/Templates/project_show.php index 96b9e404..facdc60a 100644 --- a/app/Templates/project_show.php +++ b/app/Templates/project_show.php @@ -4,9 +4,13 @@
  • + +
  • + + -
  • -
  • +
  • $project['token'])) ?>
  • +
  • $project['token'])) ?>
  • @@ -18,11 +22,11 @@ 0): ?> 0): ?> -
  • +
  • $project['id'])) ?>
  • 0): ?> -
  • +
  • $project['id'])) ?>
  • diff --git a/app/Templates/project_sidebar.php b/app/Templates/project_sidebar.php index 41f1d447..7bad1f0e 100644 --- a/app/Templates/project_sidebar.php +++ b/app/Templates/project_sidebar.php @@ -5,11 +5,11 @@
  • + +
  • - -
  • @@ -20,26 +20,28 @@
  • - -
  • -
  • - +
  • + +
  • + +
  • +
  • - +
  • - + - +
  • - +
diff --git a/app/Templates/project_users.php b/app/Templates/project_users.php index 8d75f39f..3b0de7f0 100644 --- a/app/Templates/project_users.php +++ b/app/Templates/project_users.php @@ -11,7 +11,9 @@ $username): ?>
  • - () + + () +
  • @@ -19,7 +21,7 @@ - +
    -- cgit v1.2.3