summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/Api/Me.php4
-rw-r--r--app/Api/User.php15
-rw-r--r--app/Auth/DatabaseAuth.php3
-rw-r--r--app/Controller/Config.php7
-rw-r--r--app/Controller/File.php2
-rw-r--r--app/Controller/Projectuser.php6
-rw-r--r--app/Controller/User.php29
-rw-r--r--app/Controller/UserStatus.php111
-rw-r--r--app/Core/User/UserProfile.php2
-rw-r--r--app/Helper/App.php5
-rw-r--r--app/Helper/Text.php23
-rw-r--r--app/Locale/bs_BA/translations.php3
-rw-r--r--app/Locale/cs_CZ/translations.php3
-rw-r--r--app/Locale/da_DK/translations.php3
-rw-r--r--app/Locale/de_DE/translations.php3
-rw-r--r--app/Locale/el_GR/translations.php3
-rw-r--r--app/Locale/es_ES/translations.php3
-rw-r--r--app/Locale/fi_FI/translations.php3
-rw-r--r--app/Locale/fr_FR/translations.php3
-rw-r--r--app/Locale/hu_HU/translations.php3
-rw-r--r--app/Locale/id_ID/translations.php3
-rw-r--r--app/Locale/it_IT/translations.php3
-rw-r--r--app/Locale/ja_JP/translations.php3
-rw-r--r--app/Locale/my_MY/translations.php3
-rw-r--r--app/Locale/nb_NO/translations.php3
-rw-r--r--app/Locale/nl_NL/translations.php3
-rw-r--r--app/Locale/pl_PL/translations.php3
-rw-r--r--app/Locale/pt_BR/translations.php3
-rw-r--r--app/Locale/pt_PT/translations.php3
-rw-r--r--app/Locale/ru_RU/translations.php3
-rw-r--r--app/Locale/sr_Latn_RS/translations.php3
-rw-r--r--app/Locale/sv_SE/translations.php3
-rw-r--r--app/Locale/th_TH/translations.php3
-rw-r--r--app/Locale/tr_TR/translations.php3
-rw-r--r--app/Model/ProjectGroupRole.php1
-rw-r--r--app/Model/ProjectPermission.php3
-rw-r--r--app/Model/ProjectUserRole.php5
-rw-r--r--app/Model/User.php40
-rw-r--r--app/Schema/Mysql.php7
-rw-r--r--app/Schema/Postgres.php7
-rw-r--r--app/Schema/Sqlite.php7
-rw-r--r--app/ServiceProvider/AuthenticationProvider.php3
-rw-r--r--app/ServiceProvider/RouteProvider.php1
-rw-r--r--app/Template/app/layout.php2
-rw-r--r--app/Template/config/project.php1
-rw-r--r--app/Template/file/new.php37
-rw-r--r--app/Template/header.php2
-rw-r--r--app/Template/user/dropdown.php27
-rw-r--r--app/Template/user/index.php33
-rw-r--r--app/Template/user/show.php1
-rw-r--r--app/Template/user/sidebar.php6
-rw-r--r--app/Template/user_status/disable.php13
-rw-r--r--app/Template/user_status/enable.php13
-rw-r--r--app/Template/user_status/remove.php (renamed from app/Template/user/remove.php)6
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'] ?>&nbsp;
<?= $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>