diff options
Diffstat (limited to 'app')
54 files changed, 411 insertions, 80 deletions
diff --git a/app/Api/Me.php b/app/Api/Me.php index df8ec070..ccc809ed 100644 --- a/app/Api/Me.php +++ b/app/Api/Me.php @@ -38,6 +38,10 @@ class Me extends Base public function createMyPrivateProject($name, $description = null) { + if ($this->config->get('disable_private_project', 0) == 1) { + return false; + } + $values = array( 'name' => $name, 'description' => $description, diff --git a/app/Api/User.php b/app/Api/User.php index 9f26615d..48337ac6 100644 --- a/app/Api/User.php +++ b/app/Api/User.php @@ -36,6 +36,21 @@ class User extends \Kanboard\Core\Base return $this->user->remove($user_id); } + public function disableUser($user_id) + { + return $this->user->disable($user_id); + } + + public function enableUser($user_id) + { + return $this->user->enable($user_id); + } + + public function isActiveUser($user_id) + { + return $this->user->isActive($user_id); + } + public function createUser($username, $password, $name = '', $email = '', $role = Role::APP_USER) { $values = array( diff --git a/app/Auth/DatabaseAuth.php b/app/Auth/DatabaseAuth.php index 5a8ee64d..c13af687 100644 --- a/app/Auth/DatabaseAuth.php +++ b/app/Auth/DatabaseAuth.php @@ -65,6 +65,7 @@ class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterfa ->eq('username', $this->username) ->eq('disable_login_form', 0) ->eq('is_ldap_user', 0) + ->eq('is_active', 1) ->findOne(); if (! empty($user) && password_verify($this->password, $user['password'])) { @@ -83,7 +84,7 @@ class DatabaseAuth extends Base implements PasswordAuthenticationProviderInterfa */ public function isValidSession() { - return $this->user->exists($this->userSession->getId()); + return $this->user->isActive($this->userSession->getId()); } /** diff --git a/app/Controller/Config.php b/app/Controller/Config.php index 80522bbe..e811f870 100644 --- a/app/Controller/Config.php +++ b/app/Controller/Config.php @@ -26,7 +26,12 @@ class Config extends Base $values += array('password_reset' => 0); break; case 'project': - $values += array('subtask_restriction' => 0, 'subtask_time_tracking' => 0, 'cfd_include_closed_tasks' => 0); + $values += array( + 'subtask_restriction' => 0, + 'subtask_time_tracking' => 0, + 'cfd_include_closed_tasks' => 0, + 'disable_private_project' => 0, + ); break; case 'integrations': $values += array('integration_gravatar' => 0); diff --git a/app/Controller/File.php b/app/Controller/File.php index 50db3865..4ac45fbd 100644 --- a/app/Controller/File.php +++ b/app/Controller/File.php @@ -42,7 +42,7 @@ class File extends Base $this->response->html($this->helper->layout->task('file/new', array( 'task' => $task, - 'max_size' => ini_get('upload_max_filesize'), + 'max_size' => $this->helper->text->phpToBytes(ini_get('upload_max_filesize')), ))); } diff --git a/app/Controller/Projectuser.php b/app/Controller/Projectuser.php index 9cd21021..a6d4fe4e 100644 --- a/app/Controller/Projectuser.php +++ b/app/Controller/Projectuser.php @@ -24,7 +24,7 @@ class Projectuser extends Base $project_ids = $this->projectPermission->getActiveProjectIds($this->userSession->getId()); } - return array($user_id, $project_ids, $this->user->getList(true)); + return array($user_id, $project_ids, $this->user->getActiveUsersList(true)); } private function role($role, $action, $title, $title_user) @@ -33,7 +33,7 @@ class Projectuser extends Base $query = $this->projectPermission->getQueryByRole($project_ids, $role)->callback(array($this->project, 'applyColumnStats')); - if ($user_id !== UserModel::EVERYBODY_ID) { + if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { $query->eq(UserModel::TABLE.'.id', $user_id); $title = t($title_user, $users[$user_id]); } @@ -59,7 +59,7 @@ class Projectuser extends Base $query = $this->taskFinder->getProjectUserOverviewQuery($project_ids, $is_active); - if ($user_id !== UserModel::EVERYBODY_ID) { + if ($user_id !== UserModel::EVERYBODY_ID && isset($users[$user_id])) { $query->eq(TaskModel::TABLE.'.owner_id', $user_id); $title = t($title_user, $users[$user_id]); } diff --git a/app/Controller/User.php b/app/Controller/User.php index 881266d4..f7d7d2e0 100644 --- a/app/Controller/User.php +++ b/app/Controller/User.php @@ -32,7 +32,8 @@ class User extends Base $this->helper->layout->app('user/index', array( 'title' => t('Users').' ('.$paginator->getTotal().')', 'paginator' => $paginator, - ))); + ) + )); } /** @@ -404,30 +405,4 @@ class User extends Base 'user' => $user, ))); } - - /** - * Remove a user - * - * @access public - */ - public function remove() - { - $user = $this->getUser(); - - if ($this->request->getStringParam('confirmation') === 'yes') { - $this->checkCSRFParam(); - - if ($this->user->remove($user['id'])) { - $this->flash->success(t('User removed successfully.')); - } else { - $this->flash->failure(t('Unable to remove this user.')); - } - - $this->response->redirect($this->helper->url->to('user', 'index')); - } - - $this->response->html($this->helper->layout->user('user/remove', array( - 'user' => $user, - ))); - } } diff --git a/app/Controller/UserStatus.php b/app/Controller/UserStatus.php new file mode 100644 index 00000000..b8ee5c91 --- /dev/null +++ b/app/Controller/UserStatus.php @@ -0,0 +1,111 @@ +<?php + +namespace Kanboard\Controller; + +/** + * User Status Controller + * + * @package controller + * @author Frederic Guillot + */ +class UserStatus extends Base +{ + /** + * Confirm remove a user + * + * @access public + */ + public function confirmRemove() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->user('user_status/remove', array( + 'user' => $user, + ))); + } + + /** + * Remove a user + * + * @access public + */ + public function remove() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->user->remove($user['id'])) { + $this->flash->success(t('User removed successfully.')); + } else { + $this->flash->failure(t('Unable to remove this user.')); + } + + $this->response->redirect($this->helper->url->to('user', 'index')); + } + + /** + * Confirm enable a user + * + * @access public + */ + public function confirmEnable() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->user('user_status/enable', array( + 'user' => $user, + ))); + } + + /** + * Enable a user + * + * @access public + */ + public function enable() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->user->enable($user['id'])) { + $this->flash->success(t('User activated successfully.')); + } else { + $this->flash->failure(t('Unable to enable this user.')); + } + + $this->response->redirect($this->helper->url->to('user', 'index')); + } + + /** + * Confirm disable a user + * + * @access public + */ + public function confirmDisable() + { + $user = $this->getUser(); + + $this->response->html($this->helper->layout->user('user_status/disable', array( + 'user' => $user, + ))); + } + + /** + * Disable a user + * + * @access public + */ + public function disable() + { + $user = $this->getUser(); + $this->checkCSRFParam(); + + if ($this->user->disable($user['id'])) { + $this->flash->success(t('User disabled successfully.')); + } else { + $this->flash->failure(t('Unable to disable this user.')); + } + + $this->response->redirect($this->helper->url->to('user', 'index')); + } +} diff --git a/app/Core/User/UserProfile.php b/app/Core/User/UserProfile.php index ccbc7f06..ef325801 100644 --- a/app/Core/User/UserProfile.php +++ b/app/Core/User/UserProfile.php @@ -52,7 +52,7 @@ class UserProfile extends Base $this->groupSync->synchronize($profile['id'], $user->getExternalGroupIds()); } - if (! empty($profile)) { + if (! empty($profile) && $profile['is_active'] == 1) { $this->userSession->initialize($profile); return true; } diff --git a/app/Helper/App.php b/app/Helper/App.php index 0593795f..79afa5b9 100644 --- a/app/Helper/App.php +++ b/app/Helper/App.php @@ -17,11 +17,12 @@ class App extends Base * * @access public * @param string $param + * @param mixed $default_value * @return mixed */ - public function config($param) + public function config($param, $default_value = '') { - return $this->config->get($param); + return $this->config->get($param, $default_value); } /** diff --git a/app/Helper/Text.php b/app/Helper/Text.php index 59bfd997..83f1e3f9 100644 --- a/app/Helper/Text.php +++ b/app/Helper/Text.php @@ -43,6 +43,29 @@ class Text extends Base } /** + * Get the number of bytes from PHP size + * + * @param integer $val PHP size (example: 2M) + * @return integer + */ + public function phpToBytes($val) + { + $val = trim($val); + $last = strtolower($val[strlen($val)-1]); + + switch ($last) { + case 'g': + $val *= 1024; + case 'm': + $val *= 1024; + case 'k': + $val *= 1024; + } + + return $val; + } + + /** * Return true if needle is contained in the haystack * * @param string $haystack Haystack diff --git a/app/Locale/bs_BA/translations.php b/app/Locale/bs_BA/translations.php index e331d4a1..20a6272a 100644 --- a/app/Locale/bs_BA/translations.php +++ b/app/Locale/bs_BA/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/cs_CZ/translations.php b/app/Locale/cs_CZ/translations.php index a495f01e..86222680 100644 --- a/app/Locale/cs_CZ/translations.php +++ b/app/Locale/cs_CZ/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/da_DK/translations.php b/app/Locale/da_DK/translations.php index 537b6349..3b1bc972 100644 --- a/app/Locale/da_DK/translations.php +++ b/app/Locale/da_DK/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/de_DE/translations.php b/app/Locale/de_DE/translations.php index ba6a1fd4..5d9582d4 100644 --- a/app/Locale/de_DE/translations.php +++ b/app/Locale/de_DE/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/el_GR/translations.php b/app/Locale/el_GR/translations.php index 01b31819..f51a71db 100644 --- a/app/Locale/el_GR/translations.php +++ b/app/Locale/el_GR/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/es_ES/translations.php b/app/Locale/es_ES/translations.php index 9c7c6e41..f3e16784 100644 --- a/app/Locale/es_ES/translations.php +++ b/app/Locale/es_ES/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/fi_FI/translations.php b/app/Locale/fi_FI/translations.php index d50765b6..613f04e2 100644 --- a/app/Locale/fi_FI/translations.php +++ b/app/Locale/fi_FI/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/fr_FR/translations.php b/app/Locale/fr_FR/translations.php index 90d6dc3d..a36a5e6c 100644 --- a/app/Locale/fr_FR/translations.php +++ b/app/Locale/fr_FR/translations.php @@ -1121,4 +1121,7 @@ return array( 'End date: ' => 'Date de fin : ', 'New due date: ' => 'Nouvelle date d\'échéance : ', 'Start date changed: ' => 'Date de début modifiée : ', + 'Disable private projects' => 'Désactiver les projets privés', + 'Do you really want to remove this custom filter: "%s"?' => 'Voulez-vous vraiment supprimer ce filtre personalisé : « %s » ?', + 'Remove a custom filter' => 'Supprimer un filtre personalisé', ); diff --git a/app/Locale/hu_HU/translations.php b/app/Locale/hu_HU/translations.php index 447dafc9..89a47d3e 100644 --- a/app/Locale/hu_HU/translations.php +++ b/app/Locale/hu_HU/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/id_ID/translations.php b/app/Locale/id_ID/translations.php index 8c6db849..e58ff9a8 100644 --- a/app/Locale/id_ID/translations.php +++ b/app/Locale/id_ID/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/it_IT/translations.php b/app/Locale/it_IT/translations.php index 661e6c86..914a12d2 100644 --- a/app/Locale/it_IT/translations.php +++ b/app/Locale/it_IT/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/ja_JP/translations.php b/app/Locale/ja_JP/translations.php index 3609836b..ed52d68f 100644 --- a/app/Locale/ja_JP/translations.php +++ b/app/Locale/ja_JP/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/my_MY/translations.php b/app/Locale/my_MY/translations.php index 5b1da7cd..98c8d3b0 100644 --- a/app/Locale/my_MY/translations.php +++ b/app/Locale/my_MY/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/nb_NO/translations.php b/app/Locale/nb_NO/translations.php index 335c699b..d361e23f 100644 --- a/app/Locale/nb_NO/translations.php +++ b/app/Locale/nb_NO/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/nl_NL/translations.php b/app/Locale/nl_NL/translations.php index 0d9d9608..ba4307be 100644 --- a/app/Locale/nl_NL/translations.php +++ b/app/Locale/nl_NL/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/pl_PL/translations.php b/app/Locale/pl_PL/translations.php index 28ed2b3a..6e9be6a3 100644 --- a/app/Locale/pl_PL/translations.php +++ b/app/Locale/pl_PL/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/pt_BR/translations.php b/app/Locale/pt_BR/translations.php index d29f9f88..81eb789e 100644 --- a/app/Locale/pt_BR/translations.php +++ b/app/Locale/pt_BR/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/pt_PT/translations.php b/app/Locale/pt_PT/translations.php index b197a1dc..bbd28fa3 100644 --- a/app/Locale/pt_PT/translations.php +++ b/app/Locale/pt_PT/translations.php @@ -1118,4 +1118,7 @@ return array( 'End date: ' => 'Data final: ', 'New due date: ' => 'Nova data estimada: ', 'Start date changed: ' => 'Data inicio alterada: ', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/ru_RU/translations.php b/app/Locale/ru_RU/translations.php index f971acc1..48ad334d 100644 --- a/app/Locale/ru_RU/translations.php +++ b/app/Locale/ru_RU/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/sr_Latn_RS/translations.php b/app/Locale/sr_Latn_RS/translations.php index 3551c281..f218fb07 100644 --- a/app/Locale/sr_Latn_RS/translations.php +++ b/app/Locale/sr_Latn_RS/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/sv_SE/translations.php b/app/Locale/sv_SE/translations.php index c0aa883c..4d1ca6d2 100644 --- a/app/Locale/sv_SE/translations.php +++ b/app/Locale/sv_SE/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/th_TH/translations.php b/app/Locale/th_TH/translations.php index 1ccc2d90..dde1e56a 100644 --- a/app/Locale/th_TH/translations.php +++ b/app/Locale/th_TH/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Locale/tr_TR/translations.php b/app/Locale/tr_TR/translations.php index 10153331..747db7c3 100644 --- a/app/Locale/tr_TR/translations.php +++ b/app/Locale/tr_TR/translations.php @@ -1118,4 +1118,7 @@ return array( // 'End date: ' => '', // 'New due date: ' => '', // 'Start date changed: ' => '', + // 'Disable private projects' => '', + // 'Do you really want to remove this custom filter: "%s"?' => '', + // 'Remove a custom filter' => '', ); diff --git a/app/Model/ProjectGroupRole.php b/app/Model/ProjectGroupRole.php index 591b28c6..750ba7fb 100644 --- a/app/Model/ProjectGroupRole.php +++ b/app/Model/ProjectGroupRole.php @@ -106,6 +106,7 @@ class ProjectGroupRole extends Base ->join(GroupMember::TABLE, 'user_id', 'id', User::TABLE) ->join(self::TABLE, 'group_id', 'group_id', GroupMember::TABLE) ->eq(self::TABLE.'.project_id', $project_id) + ->eq(User::TABLE.'.is_active', 1) ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) ->asc(User::TABLE.'.username') ->findAll(); diff --git a/app/Model/ProjectPermission.php b/app/Model/ProjectPermission.php index cea62e13..db1573ae 100644 --- a/app/Model/ProjectPermission.php +++ b/app/Model/ProjectPermission.php @@ -107,7 +107,8 @@ class ProjectPermission extends Base */ public function isAssignable($project_id, $user_id) { - return in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER)); + return $this->user->isActive($user_id) && + in_array($this->projectUserRole->getUserRole($project_id, $user_id), array(Role::PROJECT_MEMBER, Role::PROJECT_MANAGER)); } /** diff --git a/app/Model/ProjectUserRole.php b/app/Model/ProjectUserRole.php index 8149a253..56da679c 100644 --- a/app/Model/ProjectUserRole.php +++ b/app/Model/ProjectUserRole.php @@ -152,13 +152,14 @@ class ProjectUserRole extends Base public function getAssignableUsers($project_id) { if ($this->projectPermission->isEverybodyAllowed($project_id)) { - return $this->user->getList(); + return $this->user->getActiveUsersList(); } $userMembers = $this->db->table(self::TABLE) ->columns(User::TABLE.'.id', User::TABLE.'.username', User::TABLE.'.name') ->join(User::TABLE, 'id', 'user_id') - ->eq('project_id', $project_id) + ->eq(User::TABLE.'.is_active', 1) + ->eq(self::TABLE.'.project_id', $project_id) ->in(self::TABLE.'.role', array(Role::PROJECT_MANAGER, Role::PROJECT_MEMBER)) ->findAll(); diff --git a/app/Model/User.php b/app/Model/User.php index dd622207..e2494c4c 100644 --- a/app/Model/User.php +++ b/app/Model/User.php @@ -41,6 +41,18 @@ class User extends Base } /** + * Return true if the user is active + * + * @access public + * @param integer $user_id User id + * @return boolean + */ + public function isActive($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->eq('is_active', 1)->exists(); + } + + /** * Get query to fetch all users * * @access public @@ -193,9 +205,9 @@ class User extends Base * @param boolean $prepend Prepend "All users" * @return array */ - public function getList($prepend = false) + public function getActiveUsersList($prepend = false) { - $users = $this->db->table(self::TABLE)->columns('id', 'username', 'name')->findAll(); + $users = $this->db->table(self::TABLE)->eq('is_active', 1)->columns('id', 'username', 'name')->findAll(); $listing = $this->prepareList($users); if ($prepend) { @@ -281,6 +293,30 @@ class User extends Base } /** + * Disable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function disable($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 0)); + } + + /** + * Enable a specific user + * + * @access public + * @param integer $user_id + * @return boolean + */ + public function enable($user_id) + { + return $this->db->table(self::TABLE)->eq('id', $user_id)->update(array('is_active' => 1)); + } + + /** * Remove a specific user * * @access public diff --git a/app/Schema/Mysql.php b/app/Schema/Mysql.php index 036958b6..1cebbd22 100644 --- a/app/Schema/Mysql.php +++ b/app/Schema/Mysql.php @@ -6,7 +6,12 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 104; +const VERSION = 105; + +function version_105(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active TINYINT(1) DEFAULT 1"); +} function version_104(PDO $pdo) { diff --git a/app/Schema/Postgres.php b/app/Schema/Postgres.php index 363b633b..b0b89a7c 100644 --- a/app/Schema/Postgres.php +++ b/app/Schema/Postgres.php @@ -6,7 +6,12 @@ use PDO; use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; -const VERSION = 84; +const VERSION = 85; + +function version_85(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active BOOLEAN DEFAULT '1'"); +} function version_84(PDO $pdo) { diff --git a/app/Schema/Sqlite.php b/app/Schema/Sqlite.php index bc701341..aa10e58b 100644 --- a/app/Schema/Sqlite.php +++ b/app/Schema/Sqlite.php @@ -6,7 +6,12 @@ use Kanboard\Core\Security\Token; use Kanboard\Core\Security\Role; use PDO; -const VERSION = 96; +const VERSION = 97; + +function version_97(PDO $pdo) +{ + $pdo->exec("ALTER TABLE users ADD COLUMN is_active INTEGER DEFAULT 1"); +} function version_96(PDO $pdo) { diff --git a/app/ServiceProvider/AuthenticationProvider.php b/app/ServiceProvider/AuthenticationProvider.php index 9b5cdbe9..c2f7a5c4 100644 --- a/app/ServiceProvider/AuthenticationProvider.php +++ b/app/ServiceProvider/AuthenticationProvider.php @@ -134,7 +134,8 @@ class AuthenticationProvider implements ServiceProviderInterface $acl->add('Projectuser', '*', Role::APP_MANAGER); $acl->add('Twofactor', 'disable', Role::APP_ADMIN); $acl->add('UserImport', '*', Role::APP_ADMIN); - $acl->add('User', array('index', 'create', 'save', 'authentication', 'remove'), Role::APP_ADMIN); + $acl->add('User', array('index', 'create', 'save', 'authentication'), Role::APP_ADMIN); + $acl->add('UserStatus', '*', Role::APP_ADMIN); return $acl; } diff --git a/app/ServiceProvider/RouteProvider.php b/app/ServiceProvider/RouteProvider.php index 683ea1c1..5003eb88 100644 --- a/app/ServiceProvider/RouteProvider.php +++ b/app/ServiceProvider/RouteProvider.php @@ -157,7 +157,6 @@ class RouteProvider implements ServiceProviderInterface $container['route']->addRoute('user/:user_id/accounts', 'user', 'external'); $container['route']->addRoute('user/:user_id/integrations', 'user', 'integrations'); $container['route']->addRoute('user/:user_id/authentication', 'user', 'authentication'); - $container['route']->addRoute('user/:user_id/remove', 'user', 'remove'); $container['route']->addRoute('user/:user_id/2fa', 'twofactor', 'index'); // Groups diff --git a/app/Template/app/layout.php b/app/Template/app/layout.php index 0550cef4..200cb0d7 100644 --- a/app/Template/app/layout.php +++ b/app/Template/app/layout.php @@ -7,10 +7,12 @@ <?= $this->url->link(t('New project'), 'ProjectCreation', 'create', array(), false, 'popover') ?> </li> <?php endif ?> + <?php if ($this->app->config('disable_private_project', 0) == 0): ?> <li> <i class="fa fa-lock fa-fw"></i> <?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate', array(), false, 'popover') ?> </li> + <?php endif ?> <li> <i class="fa fa-search fa-fw"></i> <?= $this->url->link(t('Search'), 'search', 'index') ?> diff --git a/app/Template/config/project.php b/app/Template/config/project.php index a212f65f..1d32a14f 100644 --- a/app/Template/config/project.php +++ b/app/Template/config/project.php @@ -16,6 +16,7 @@ <?= $this->form->text('project_categories', $values, $errors) ?> <p class="form-help"><?= t('Example: "Bug, Feature Request, Improvement"') ?></p> + <?= $this->form->checkbox('disable_private_project', t('Disable private projects'), 1, isset($values['disable_private_project']) && $values['disable_private_project'] == 1) ?> <?= $this->form->checkbox('subtask_restriction', t('Allow only one subtask in progress at the same time for a user'), 1, $values['subtask_restriction'] == 1) ?> <?= $this->form->checkbox('subtask_time_tracking', t('Trigger automatically subtask time tracking'), 1, $values['subtask_time_tracking'] == 1) ?> <?= $this->form->checkbox('cfd_include_closed_tasks', t('Include closed tasks in the cumulative flow diagram'), 1, $values['cfd_include_closed_tasks'] == 1) ?> diff --git a/app/Template/file/new.php b/app/Template/file/new.php index 1702638d..e84ef839 100644 --- a/app/Template/file/new.php +++ b/app/Template/file/new.php @@ -1,14 +1,33 @@ <div class="page-header"> <h2><?= t('Attach a document') ?></h2> </div> +<div id="file-done" style="display:none"> + <p class="alert alert-success"> + <?= t('All files have been uploaded successfully.') ?> + <?= $this->url->link(t('View uploaded files'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?> + </p> +</div> + +<div id="file-error-max-size" style="display:none"> + <p class="alert alert-error"> + <?= t('The maximum allowed file size is %sB.', $this->text->bytes($max_size)) ?> + <a href="#" id="file-browser"><?= t('Choose files again') ?></a> + </p> +</div> -<form action="<?= $this->url->href('file', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>" method="post" enctype="multipart/form-data"> - <?= $this->form->csrf() ?> - <input type="file" name="files[]" multiple /> - <div class="form-help"><?= t('Maximum size: ') ?><?= is_integer($max_size) ? $this->text->bytes($max_size) : $max_size ?></div> - <div class="form-actions"> - <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue"> - <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> +<div + id="file-dropzone" + data-max-size="<?= $max_size ?>" + data-url="<?= $this->url->href('file', 'save', array('task_id' => $task['id'], 'project_id' => $task['project_id'])) ?>"> + <div id="file-dropzone-inner"> + <?= t('Drag and drop your files here') ?> <?= t('or') ?> <a href="#" id="file-browser"><?= t('choose files') ?></a> </div> -</form>
\ No newline at end of file +</div> + +<input type="file" name="files[]" multiple style="display:none" id="file-form-element"> + +<div class="form-actions"> + <input type="submit" value="<?= t('Save') ?>" class="btn btn-blue" id="file-upload-button" disabled> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'task', 'show', array('task_id' => $task['id'], 'project_id' => $task['project_id']), false, 'close-popover') ?> +</div> diff --git a/app/Template/header.php b/app/Template/header.php index 72d89b80..a945411e 100644 --- a/app/Template/header.php +++ b/app/Template/header.php @@ -44,9 +44,11 @@ <?php if ($this->user->hasAccess('ProjectCreation', 'create')): ?> <li><i class="fa fa-plus fa-fw"></i><?= $this->url->link(t('New project'), 'ProjectCreation', 'create', array(), false, 'popover') ?></li> <?php endif ?> + <?php if ($this->app->config('disable_private_project', 0) == 0): ?> <li> <i class="fa fa-lock fa-fw"></i><?= $this->url->link(t('New private project'), 'ProjectCreation', 'createPrivate', array(), false, 'popover') ?> </li> + <?php endif ?> </ul> </div> diff --git a/app/Template/user/dropdown.php b/app/Template/user/dropdown.php new file mode 100644 index 00000000..b74ed6e0 --- /dev/null +++ b/app/Template/user/dropdown.php @@ -0,0 +1,27 @@ +<div class="dropdown"> + <a href="#" class="dropdown-menu dropdown-menu-link-icon"><i class="fa fa-cog fa-fw"></i><i class="fa fa-caret-down"></i></a> + <ul> + <li> + <i class="fa fa-user fa-fw"></i> + <?= $this->url->link(t('View profile'), 'user', 'show', array('user_id' => $user['id'])) ?> + </li> + <?php if ($user['is_active'] == 1 && $this->user->hasAccess('UserStatus', 'disable') && ! $this->user->isCurrentUser($user['id'])): ?> + <li> + <i class="fa fa-times fa-fw"></i> + <?= $this->url->link(t('Disable'), 'UserStatus', 'confirmDisable', array('user_id' => $user['id']), false, 'popover') ?> + </li> + <?php endif ?> + <?php if ($user['is_active'] == 0 && $this->user->hasAccess('UserStatus', 'enable') && ! $this->user->isCurrentUser($user['id'])): ?> + <li> + <i class="fa fa-check-square-o fa-fw"></i> + <?= $this->url->link(t('Enable'), 'UserStatus', 'confirmEnable', array('user_id' => $user['id']), false, 'popover') ?> + </li> + <?php endif ?> + <?php if ($this->user->hasAccess('UserStatus', 'remove') && ! $this->user->isCurrentUser($user['id'])): ?> + <li> + <i class="fa fa-trash-o fa-fw"></i> + <?= $this->url->link(t('Remove'), 'UserStatus', 'confirmRemove', array('user_id' => $user['id']), false, 'popover') ?> + </li> + <?php endif ?> + </ul> +</div>
\ No newline at end of file diff --git a/app/Template/user/index.php b/app/Template/user/index.php index cb7416d6..494c1465 100644 --- a/app/Template/user/index.php +++ b/app/Template/user/index.php @@ -12,23 +12,21 @@ <?php if ($paginator->isEmpty()): ?> <p class="alert"><?= t('No user') ?></p> <?php else: ?> - <table> + <table class="table-stripped"> <tr> - <th><?= $paginator->order(t('Id'), 'id') ?></th> - <th><?= $paginator->order(t('Username'), 'username') ?></th> - <th><?= $paginator->order(t('Name'), 'name') ?></th> - <th><?= $paginator->order(t('Email'), 'email') ?></th> - <th><?= $paginator->order(t('Role'), 'role') ?></th> - <th><?= $paginator->order(t('Two factor authentication'), 'twofactor_activated') ?></th> - <th><?= $paginator->order(t('Notifications'), 'notifications_enabled') ?></th> - <th><?= $paginator->order(t('Account type'), 'is_ldap_user') ?></th> + <th class="column-18"><?= $paginator->order(t('Username'), 'username') ?></th> + <th class="column-18"><?= $paginator->order(t('Name'), 'name') ?></th> + <th class="column-15"><?= $paginator->order(t('Email'), 'email') ?></th> + <th class="column-15"><?= $paginator->order(t('Role'), 'role') ?></th> + <th class="column-10"><?= $paginator->order(t('Two Factor'), 'twofactor_activated') ?></th> + <th class="column-10"><?= $paginator->order(t('Account type'), 'is_ldap_user') ?></th> + <th class="column-10"><?= $paginator->order(t('Status'), 'is_active') ?></th> + <th class="column-5"><?= t('Actions') ?></th> </tr> <?php foreach ($paginator->getCollection() as $user): ?> <tr> <td> - <?= $this->url->link('#'.$user['id'], 'user', 'show', array('user_id' => $user['id'])) ?> - </td> - <td> + <?= '#'.$user['id'] ?> <?= $this->url->link($this->e($user['username']), 'user', 'show', array('user_id' => $user['id'])) ?> </td> <td> @@ -44,14 +42,17 @@ <?= $user['twofactor_activated'] ? t('Yes') : t('No') ?> </td> <td> - <?php if ($user['notifications_enabled'] == 1): ?> - <?= t('Enabled') ?> + <?= $user['is_ldap_user'] ? t('Remote') : t('Local') ?> + </td> + <td> + <?php if ($user['is_active'] == 1): ?> + <?= t('Active') ?> <?php else: ?> - <?= t('Disabled') ?> + <?= t('Inactive') ?> <?php endif ?> </td> <td> - <?= $user['is_ldap_user'] ? t('Remote') : t('Local') ?> + <?= $this->render('user/dropdown', array('user' => $user)) ?> </td> </tr> <?php endforeach ?> diff --git a/app/Template/user/show.php b/app/Template/user/show.php index 89c6b36b..9da56666 100644 --- a/app/Template/user/show.php +++ b/app/Template/user/show.php @@ -5,6 +5,7 @@ <li><?= t('Username:') ?> <strong><?= $this->e($user['username']) ?></strong></li> <li><?= t('Name:') ?> <strong><?= $this->e($user['name']) ?: t('None') ?></strong></li> <li><?= t('Email:') ?> <strong><?= $this->e($user['email']) ?: t('None') ?></strong></li> + <li><?= t('Status:') ?> <strong><?= $user['is_active'] ? t('Active') : t('Inactive') ?></strong></li> </ul> <div class="page-header"> diff --git a/app/Template/user/sidebar.php b/app/Template/user/sidebar.php index 765a1e6e..2faf8220 100644 --- a/app/Template/user/sidebar.php +++ b/app/Template/user/sidebar.php @@ -76,11 +76,5 @@ <?php endif ?> <?= $this->hook->render('template:user:sidebar:actions', array('user' => $user)) ?> - - <?php if ($this->user->hasAccess('user', 'remove') && ! $this->user->isCurrentUser($user['id'])): ?> - <li <?= $this->app->checkMenuSelection('user', 'remove') ?>> - <?= $this->url->link(t('Remove'), 'user', 'remove', array('user_id' => $user['id'])) ?> - </li> - <?php endif ?> </ul> </div>
\ No newline at end of file diff --git a/app/Template/user_status/disable.php b/app/Template/user_status/disable.php new file mode 100644 index 00000000..90d8c757 --- /dev/null +++ b/app/Template/user_status/disable.php @@ -0,0 +1,13 @@ +<div class="page-header"> + <h2><?= t('Disable user') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"><?= t('Do you really want to disable this user: "%s"?', $user['name'] ?: $user['username']) ?></p> + + <div class="form-actions"> + <?= $this->url->link(t('Yes'), 'UserStatus', 'disable', array('user_id' => $user['id']), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'user', 'index', array(), false, 'close-popover') ?> + </div> +</div> diff --git a/app/Template/user_status/enable.php b/app/Template/user_status/enable.php new file mode 100644 index 00000000..cd3d4947 --- /dev/null +++ b/app/Template/user_status/enable.php @@ -0,0 +1,13 @@ +<div class="page-header"> + <h2><?= t('Enable user') ?></h2> +</div> + +<div class="confirm"> + <p class="alert alert-info"><?= t('Do you really want to enable this user: "%s"?', $user['name'] ?: $user['username']) ?></p> + + <div class="form-actions"> + <?= $this->url->link(t('Yes'), 'UserStatus', 'enable', array('user_id' => $user['id']), true, 'btn btn-red') ?> + <?= t('or') ?> + <?= $this->url->link(t('cancel'), 'user', 'index', array(), false, 'close-popover') ?> + </div> +</div> diff --git a/app/Template/user/remove.php b/app/Template/user_status/remove.php index 810a3a3f..cd5c09a6 100644 --- a/app/Template/user/remove.php +++ b/app/Template/user_status/remove.php @@ -6,8 +6,8 @@ <p class="alert alert-info"><?= t('Do you really want to remove this user: "%s"?', $user['name'] ?: $user['username']) ?></p> <div class="form-actions"> - <?= $this->url->link(t('Yes'), 'user', 'remove', array('user_id' => $user['id'], 'confirmation' => 'yes'), true, 'btn btn-red') ?> + <?= $this->url->link(t('Yes'), 'UserStatus', 'remove', array('user_id' => $user['id']), true, 'btn btn-red') ?> <?= t('or') ?> - <?= $this->url->link(t('cancel'), 'user', 'show', array('user_id' => $user['id'])) ?> + <?= $this->url->link(t('cancel'), 'user', 'index', array(), false, 'close-popover') ?> </div> -</div>
\ No newline at end of file +</div> |