diff options
39 files changed, 374 insertions, 314 deletions
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')), @@ -367,25 +368,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) * * @access public 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 */ @@ -267,53 +236,30 @@ class Project extends Base } /** - * 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 * * @access public */ 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 @@ -32,6 +32,29 @@ class Board extends Base } /** + * 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 * * @access public 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 @@ -84,6 +84,18 @@ class Project extends Base } /** + * 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 * * @access public @@ -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) @@ -156,6 +154,23 @@ class ProjectPermission extends Base } /** + * 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 * * @access public 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 @@ </p> <div class="form-actions"> - <a href="?controller=action&action=remove&action_id=<?= $action['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=action&action=index&project_id=<?= $action['project_id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'action', 'remove', array('project_id' => $project['id'], 'action_id' => $action['id']), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'action', 'index', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ <ul> <?php if ($column['position'] != 1): ?> <li> - <a href="?controller=board&action=moveUp&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Up') ?></a> + <?= Helper\a(t('Move Up'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'up'), true) ?> </li> <?php endif ?> <?php if ($column['position'] != count($columns)): ?> <li> - <a href="?controller=board&action=moveDown&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'].Helper\param_csrf() ?>"><?= t('Move Down') ?></a> + <?= Helper\a(t('Move Down'), 'board', 'moveColumn', array('project_id' => $project['id'], 'column_id' => $column['id'], 'direction' => 'down'), true) ?> </li> <?php endif ?> <li> - <a href="?controller=board&action=confirm&project_id=<?= $project['id'] ?>&column_id=<?= $column['id'] ?>"><?= t('Remove') ?></a> + <?= Helper\a(t('Remove'), 'board', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id'])) ?> </li> </ul> </td> 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 @@ </p> <div class="form-actions"> - <a href="?controller=board&action=remove&column_id=<?= $column['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=board&action=edit&project_id=<?= $column['project_id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'board', 'remove', array('project_id' => $project['id'], 'column_id' => $column['id'], 'remove' => 'yes'), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'board', 'edit', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ </p> <div class="form-actions"> - <a href="?controller=project&action=disable&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'project', 'disable', array('project_id' => $project['id'], 'disable' => 'yes'), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ </p> <div class="form-actions"> - <a href="?controller=project&action=duplicate&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'project', 'duplicate', array('project_id' => $project['id'], 'duplicate' => 'yes'), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ <div class="page-header"> <h2><?= t('Edit project') ?></h2> </div> -<form method="post" action="?controller=project&action=update&project_id=<?= $values['id'] ?>" autocomplete="off"> +<form method="post" action="<?= Helper\u('project', 'update', array('project_id' => $values['id'])) ?>" autocomplete="off"> <?= Helper\form_csrf() ?> <?= Helper\form_hidden('id', $values) ?> @@ -9,8 +9,6 @@ <?= Helper\form_label(t('Name'), 'name') ?> <?= Helper\form_text('name', $values, $errors, array('required')) ?> - <?= Helper\form_checkbox('is_active', t('Activated'), 1, isset($values['is_active']) && $values['is_active'] == 1) ?><br/> - <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> </div> 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 @@ </p> <div class="form-actions"> - <a href="?controller=project&action=enable&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'project', 'enable', array('project_id' => $project['id'], 'enable' => 'yes'), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ <section id="main"> <div class="page-header"> <h2><?= t('Projects') ?><span id="page-counter"> (<?= $nb_projects ?>)</span></h2> - <?php if (Helper\is_admin()): ?> <ul> - <li><a href="?controller=project&action=create"><?= t('New project') ?></a></li> + <?php if (Helper\is_admin()): ?> + <li><?= Helper\a(t('New project'), 'project', 'create') ?></li> + <?php endif ?> + <li><?= Helper\a(t('New private project'), 'project', 'create', array('private' => 1)) ?></li> </ul> - <?php endif ?> </div> <section> <?php if (empty($active_projects) && empty($inactive_projects)): ?> @@ -17,7 +18,10 @@ <ul class="project-listing"> <?php foreach ($active_projects as $project): ?> <li> - <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= Helper\escape($project['name']) ?></a> + <?php if ($project['is_private']): ?> + <i class="fa fa-lock"></i> + <?php endif ?> + <?= Helper\a(Helper\escape($project['name']), 'project', 'show', array('project_id' => $project['id'])) ?> </li> <?php endforeach ?> </ul> @@ -28,7 +32,10 @@ <ul class="project-listing"> <?php foreach ($inactive_projects as $project): ?> <li> - <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= Helper\escape($project['name']) ?></a> + <?php if ($project['is_private']): ?> + <i class="fa fa-lock"></i> + <?php endif ?> + <?= Helper\a(Helper\escape($project['name']), 'project', 'show', array('project_id' => $project['id'])) ?> </li> <?php endforeach ?> </ul> 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 @@ <div class="page-header"> <h2><?= t('Project "%s"', $project['name']) ?> (#<?= $project['id'] ?>)</h2> <ul> - <li><a href="?controller=board&action=show&project_id=<?= $project['id'] ?>"><?= t('Back to the board') ?></a></li> - <li><a href="?controller=project"><?= t('All projects') ?></a></li> + <li><?= Helper\a(t('Back to the board'), 'board', 'show', array('project_id' => $project['id'])) ?></li> + <li><?= Helper\a(t('All projects'), 'project', 'index') ?></li> </ul> </div> <section class="project-show" id="project-section"> 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 @@ <section id="main"> <div class="page-header"> - <h2><?= t('New project') ?></h2> + <h2><?= empty($values['is_private']) ? t('New project') : t('New private project') ?></h2> <ul> - <li><a href="?controller=project"><?= t('All projects') ?></a></li> + <li><?= Helper\a(t('All projects'), 'project', 'index') ?></li> </ul> </div> <section> - <form method="post" action="?controller=project&action=save" autocomplete="off"> + <form method="post" action="<?= Helper\u('project', 'save') ?>" autocomplete="off"> <?= Helper\form_csrf() ?> + <?= Helper\form_hidden('is_private', $values) ?> <?= Helper\form_label(t('Name'), 'name') ?> <?= Helper\form_text('name', $values, $errors, array('autofocus', 'required')) ?> <div class="form-actions"> <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"/> - <?= t('or') ?> <a href="?controller=project"><?= t('cancel') ?></a> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'project', 'index') ?> </div> </form> </section> 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 @@ </p> <div class="form-actions"> - <a href="?controller=project&action=remove&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Yes') ?></a> - <?= t('or') ?> <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= t('cancel') ?></a> + <?= Helper\a(t('Yes'), 'project', 'remove', array('project_id' => $project['id'], 'remove' => 'yes'), true, 'btn btn-red') ?> + <?= t('or') ?> <?= Helper\a(t('cancel'), 'project', 'show', array('project_id' => $project['id'])) ?> </div> </div>
\ 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 @@ <div class="listing"> <ul class="no-bullet"> - <li><strong><i class="fa fa-share-alt"></i> <a href="?controller=board&action=readonly&token=<?= $project['token'] ?>" target="_blank"><?= t('Public link') ?></a></strong></li> - <li><strong><i class="fa fa-rss-square"></i> <a href="?controller=project&action=feed&token=<?= $project['token'] ?>" target="_blank"><?= t('RSS feed') ?></a></strong></li> + <li><strong><i class="fa fa-share-alt"></i> <?= Helper\a(t('Public link'), 'board', 'readonly', array('token' => $project['token'])) ?></strong></li> + <li><strong><i class="fa fa-rss-square"></i> <?= Helper\a(t('RSS feed'), 'project', 'feed', array('token' => $project['token'])) ?></strong></li> </ul> - <input type="text" readonly="readonly" value="<?= Helper\get_current_base_url() ?>?controller=board&action=readonly&token=<?= $project['token'] ?>"/> + <input type="text" readonly="readonly" value="<?= Helper\get_current_base_url().Helper\u('board', 'readonly', array('token' => $project['token'])) ?>"/> </div> - <a href="?controller=project&action=disablePublic&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-red"><?= t('Disable public access') ?></a> + <?= Helper\a(t('Disable public access'), 'project', 'share', array('project_id' => $project['id'], 'switch' => 'disable'), true, 'btn btn-red') ?> <?php else: ?> - - <a href="?controller=project&action=enablePublic&project_id=<?= $project['id'].Helper\param_csrf() ?>" class="btn btn-blue"><?= t('Enable public access') ?></a> - + <?= Helper\a(t('Enable public access'), 'project', 'share', array('project_id' => $project['id'], 'switch' => 'enable'), true, 'btn btn-blue') ?> <?php endif ?> 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 @@ <ul class="listing"> <li><strong><?= $project['is_active'] ? t('Active') : t('Inactive') ?></strong></li> + <?php if ($project['is_private']): ?> + <li><i class="fa fa-lock"></i> <?= t('This project is private') ?></li> + <?php endif ?> + <?php if ($project['is_public']): ?> - <li><i class="fa fa-share-alt"></i> <a href="?controller=board&action=readonly&token=<?= $project['token'] ?>" target="_blank"><?= t('Public link') ?></a></li> - <li><i class="fa fa-rss-square"></i> <a href="?controller=project&action=feed&token=<?= $project['token'] ?>" target="_blank"><?= t('RSS feed') ?></a></li> + <li><i class="fa fa-share-alt"></i> <?= Helper\a(t('Public link'), 'board', 'readonly', array('token' => $project['token'])) ?></li> + <li><i class="fa fa-rss-square"></i> <?= Helper\a(t('RSS feed'), 'project', 'feed', array('token' => $project['token'])) ?></li> <?php else: ?> <li><?= t('Public access disabled') ?></li> <?php endif ?> @@ -18,11 +22,11 @@ <?php if ($stats['nb_tasks'] > 0): ?> <?php if ($stats['nb_active_tasks'] > 0): ?> - <li><a href="?controller=board&action=show&project_id=<?= $project['id'] ?>"><?= t('%d tasks on the board', $stats['nb_active_tasks']) ?></a></li> + <li><?= Helper\a(t('%d tasks on the board', $stats['nb_active_tasks']), 'board', 'show', array('project_id' => $project['id'])) ?></li> <?php endif ?> <?php if ($stats['nb_inactive_tasks'] > 0): ?> - <li><a href="?controller=project&action=tasks&project_id=<?= $project['id'] ?>"><?= t('%d closed tasks', $stats['nb_inactive_tasks']) ?></a></li> + <li><?= Helper\a(t('%d closed tasks', $stats['nb_inactive_tasks']), 'project', 'tasks', array('project_id' => $project['id'])) ?></li> <?php endif ?> <li><?= t('%d tasks in total', $stats['nb_tasks']) ?></li> 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 @@ <li> <a href="?controller=project&action=show&project_id=<?= $project['id'] ?>"><?= t('Summary') ?></a> </li> + + <?php if (Helper\is_admin() || $project['is_private']): ?> <li> <a href="?controller=project&action=export&project_id=<?= $project['id'] ?>"><?= t('Tasks Export') ?></a> </li> - - <?php if (Helper\is_admin()): ?> <li> <a href="?controller=project&action=share&project_id=<?= $project['id'] ?>"><?= t('Public access') ?></a> </li> @@ -20,26 +20,28 @@ <a href="?controller=board&action=edit&project_id=<?= $project['id'] ?>"><?= t('Edit board') ?></a> </li> <li> - <a href="?controller=category&action=index&project_id=<?= $project['id'] ?>"><?= t('Categories management') ?></a> - </li> - <li> - <a href="?controller=project&action=users&project_id=<?= $project['id'] ?>"><?= t('User management') ?></a> + <a href="?controller=category&action=index&project_id=<?= $project['id'] ?>"><?= t('Category management') ?></a> </li> + <?php if (Helper\is_admin()): ?> + <li> + <a href="?controller=project&action=users&project_id=<?= $project['id'] ?>"><?= t('User management') ?></a> + </li> + <?php endif ?> <li> <a href="?controller=action&action=index&project_id=<?= $project['id'] ?>"><?= t('Automatic actions') ?></a> </li> <li> - <a href="?controller=project&action=confirmDuplicate&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Duplicate') ?></a> + <a href="?controller=project&action=duplicate&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Duplicate') ?></a> </li> <li> <?php if ($project['is_active']): ?> - <a href="?controller=project&action=confirmDisable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Disable') ?></a> + <a href="?controller=project&action=disable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Disable') ?></a> <?php else: ?> - <a href="?controller=project&action=confirmEnable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Enable') ?></a> + <a href="?controller=project&action=enable&project_id=<?= $project['id'].Helper\param_csrf() ?>"><?= t('Enable') ?></a> <?php endif ?> </li> <li> - <a href="?controller=project&action=confirmRemove&project_id=<?= $project['id'] ?>"><?= t('Remove') ?></a> + <a href="?controller=project&action=remove&project_id=<?= $project['id'] ?>"><?= t('Remove') ?></a> </li> <?php endif ?> </ul> 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 @@ <?php foreach ($users['allowed'] as $user_id => $username): ?> <li> <strong><?= Helper\escape($username) ?></strong> - (<a href="?controller=project&action=revoke&project_id=<?= $project['id'] ?>&user_id=<?= $user_id.Helper\param_csrf() ?>"><?= t('revoke') ?></a>) + <?php if ($project['is_private'] == 0): ?> + (<a href="?controller=project&action=revoke&project_id=<?= $project['id'] ?>&user_id=<?= $user_id.Helper\param_csrf() ?>"><?= t('revoke') ?></a>) + <?php endif ?> </li> <?php endforeach ?> </ul> @@ -19,7 +21,7 @@ </div> <?php endif ?> -<?php if (! empty($users['not_allowed'])): ?> +<?php if ($project['is_private'] == 0 && ! empty($users['not_allowed'])): ?> <form method="post" action="?controller=project&action=allow&project_id=<?= $project['id'] ?>" autocomplete="off"> <?= Helper\form_csrf() ?> diff --git a/tests/functionals/ApiTest.php b/tests/functionals/ApiTest.php index 272e9b35..da5aeaee 100644 --- a/tests/functionals/ApiTest.php +++ b/tests/functionals/ApiTest.php @@ -299,7 +299,7 @@ class Api extends PHPUnit_Framework_TestCase { $users = $this->client->getAllowedUsers(1); $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin', 2 => 'Titi'), $users); + $this->assertEquals(array(), $users); } public function testAllowedUser() @@ -317,7 +317,7 @@ class Api extends PHPUnit_Framework_TestCase $users = $this->client->getAllowedUsers(1); $this->assertNotFalse($users); - $this->assertEquals(array(1 => 'admin', 2 => 'Titi'), $users); + $this->assertEquals(array(), $users); } public function testCreateComment() diff --git a/tests/units/ProjectTest.php b/tests/units/ProjectTest.php index be4267ca..0085711a 100644 --- a/tests/units/ProjectTest.php +++ b/tests/units/ProjectTest.php @@ -3,6 +3,7 @@ require_once __DIR__.'/Base.php'; use Model\Project; +use Model\ProjectPermission; use Model\User; use Model\Task; use Model\Acl; @@ -20,6 +21,7 @@ class ProjectTest extends Base $this->assertNotEmpty($project); $this->assertEquals(1, $project['is_active']); $this->assertEquals(0, $project['is_public']); + $this->assertEquals(0, $project['is_private']); $this->assertEquals(time(), $project['last_modified']); $this->assertEmpty($project['token']); } @@ -136,4 +138,36 @@ class ProjectTest extends Base $this->assertFalse($p->disablePublicAccess(123)); } + + public function testDuplicate() + { + $p = new Project($this->registry); + + // Clone public project + $this->assertEquals(1, $p->create(array('name' => 'Public'))); + $this->assertEquals(2, $p->duplicate(1)); + + $project = $p->getById(2); + $this->assertNotEmpty($project); + $this->assertEquals('Public (Clone)', $project['name']); + $this->assertEquals(0, $project['is_private']); + $this->assertEquals(0, $project['is_public']); + $this->assertEmpty($project['token']); + + // Clone private project + $this->assertEquals(3, $p->create(array('name' => 'Private', 'is_private' => 1), 1)); + $this->assertEquals(4, $p->duplicate(3)); + + $project = $p->getById(4); + $this->assertNotEmpty($project); + $this->assertEquals('Private (Clone)', $project['name']); + $this->assertEquals(1, $project['is_private']); + $this->assertEquals(0, $project['is_public']); + $this->assertEmpty($project['token']); + + $pp = new ProjectPermission($this->registry); + + $this->assertEquals(array(1 => 'admin'), $pp->getAllowedUsers(3)); + $this->assertEquals(array(1 => 'admin'), $pp->getAllowedUsers(4)); + } } |