Add group limit for creating boards

Signed-off-by: Julius Härtl <jus@bitgrid.net>
This commit is contained in:
Julius Härtl
2018-12-04 09:11:50 +01:00
parent bff3cafb37
commit 7c95783ab5
11 changed files with 257 additions and 18 deletions

View File

@@ -26,6 +26,9 @@ return [
'routes' => [ 'routes' => [
['name' => 'page#index', 'url' => '/', 'verb' => 'GET'], ['name' => 'page#index', 'url' => '/', 'verb' => 'GET'],
['name' => 'Config#get', 'url' => '/config', 'verb' => 'GET'],
['name' => 'Config#setValue', 'url' => '/config/{key}', 'verb' => 'POST'],
// boards // boards
['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'], ['name' => 'board#index', 'url' => '/boards', 'verb' => 'GET'],
['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'], ['name' => 'board#create', 'url' => '/boards', 'verb' => 'POST'],

View File

@@ -148,6 +148,22 @@ input.input-inline {
} }
#app-settings-content {
overflow: initial;
.ui-select-match-item {
border: 1px solid var(--color-background-darker) !important;
.select-label {
color: var(--color-main-text);
}
}
p.hint {
margin-top: 10px;
color: var(--color-text-light);
}
}
/** /**
* Board view * Board view
*/ */
@@ -1246,6 +1262,7 @@ input.input-inline {
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;
vertical-align: middle; vertical-align: middle;
flex-grow: 1;
} }
.icon-delete { .icon-delete {
@@ -1404,6 +1421,10 @@ input.input-inline {
} }
.select2-search-field { .select2-search-field {
margin-right: -10px; margin-right: -10px;
flex-grow: 1;
input {
width: 100% !important;
}
} }
} }
@@ -1537,7 +1558,7 @@ input.input-inline {
table { table {
margin-bottom: 10px; margin-bottom: 10px;
border-collapse: collapse; border-collapse: collapse;
thead { thead {
background-color: var(--color-background-dark, $color-lightgrey); background-color: var(--color-background-dark, $color-lightgrey);
} }

View File

@@ -4,25 +4,25 @@
* @author Julius Härtl <jus@bitgrid.net> * @author Julius Härtl <jus@bitgrid.net>
* *
* @license GNU AGPL version 3 or any later version * @license GNU AGPL version 3 or any later version
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as * it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the * published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version. * License, or (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details. * GNU Affero General Public License for more details.
* *
* You should have received a copy of the GNU Affero General Public License * You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
* *
*/ */
/* global app angular */ /* global app angular oc_isadmin */
var ListController = function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state, StatusService) { var ListController = function ($scope, $location, $filter, BoardService, $element, $timeout, $stateParams, $state, StatusService, $http, $q, $rootScope) {
function calculateNewColor() { function calculateNewColor() {
var boards = BoardService.getAll(); var boards = BoardService.getAll();
@@ -55,6 +55,56 @@ var ListController = function ($scope, $location, $filter, BoardService, $elemen
$scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD']; $scope.colors = ['0082c9', '00c9c6','00c906', 'c92b00', 'F1DB50', '7C31CC', '3A3B3D', 'CACBCD'];
$scope.boardservice = BoardService; $scope.boardservice = BoardService;
$scope.updatingBoard = null; $scope.updatingBoard = null;
$scope.isAdmin = oc_isadmin;
$scope.canCreate = $rootScope.config.canCreate;
if ($scope.isAdmin) {
OC.Apps.enableDynamicSlideToggle();
$scope.groups = [];
$scope.groupLimit = [];
$scope.groupLimitDisabled = true;
let fetchGroups = function () {
var deferred = $q.defer();
$http.get(OC.linkToOCS('cloud', 2) + 'groups/details').then(function (response) {
$scope.groups = response.data.ocs.data.groups;
deferred.resolve(response.data.ocs.data.groups);
}, function (error) {
deferred.reject('Error while loading groups');
});
$http.get(OC.generateUrl('apps/deck/config')).then(function (response) {
$scope.groupLimit = response.data.groupLimit;
$scope.groupLimitDisabled = false;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while loading groupLimit');
});
return deferred.promise;
};
let updateConfig = function() {
$scope.groupLimitDisabled = true;
var deferred = $q.defer();
$http.post(OC.generateUrl('apps/deck/config/groupLimit'), {value: $scope.groupLimit}).then(function (response) {
$scope.groupLimitDisabled = false;
deferred.resolve(response.data);
}, function (error) {
deferred.reject('Error while saving groupLimit');
});
return deferred.promise;
};
$scope.groupLimitAdd = function (element, model) {
$scope.groupLimit.push(element);
updateConfig();
};
$scope.groupLimitRemove = function (element, model) {
$scope.groupLimit = $scope.groupLimit.filter((el) => {
return el.id !== element.id;
});
updateConfig();
};
fetchGroups();
}
var filterData = function () { var filterData = function () {
if($element.attr('id') === 'app-navigation') { if($element.attr('id') === 'app-navigation') {

View File

@@ -0,0 +1,118 @@
<?php
/**
* @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net>
*
* @author Julius Härtl <jus@bitgrid.net>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OCA\Deck\Controller;
use OCA\Deck\Service\DefaultBoardService;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\IConfig;
use OCP\IGroup;
use OCP\IGroupManager;
use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Controller;
use OCP\IL10N;
class ConfigController extends Controller {
private $config;
private $userId;
private $groupManager;
public function __construct(
$AppName,
IRequest $request,
IConfig $config,
IGroupManager $groupManager,
$userId
) {
parent::__construct($AppName, $request);
$this->userId = $userId;
$this->groupManager = $groupManager;
$this->config = $config;
}
/**
* @NoCSRFRequired
*/
public function get() {
$data = [
'groupLimit' => $this->getGroupLimit(),
];
return new DataResponse($data);
}
/**
* @NoCSRFRequired
*/
public function setValue($key, $value) {
switch ($key) {
case 'groupLimit':
$result = $this->setGroupLimit($value);
break;
}
if ($result === null) {
return new NotFoundResponse();
}
return new DataResponse($result);
}
private function setGroupLimit($value) {
$groups = [];
foreach ($value as $group) {
$groups[] = $group['id'];
}
$data = implode(',', $groups);
$this->config->setAppValue($this->appName, 'groupLimit', $data);
return $groups;
}
private function getGroupLimitList() {
$value = $this->config->getAppValue($this->appName, 'groupLimit', '');
$groups = explode(',', $value);
if ($value === '') {
return [];
}
return $groups;
}
private function getGroupLimit() {
$groups = $this->getGroupLimitList();
$groups = array_map(function($groupId) {
/** @var IGroup $groups */
$group = $this->groupManager->get($groupId);
return [
'id' => $group->getGID(),
'displayname' => $group->getDisplayName(),
'usercount' => $group->count(),
'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
];
}, $groups);
return $groups;
}
}

View File

@@ -24,6 +24,7 @@
namespace OCA\Deck\Controller; namespace OCA\Deck\Controller;
use OCA\Deck\Service\DefaultBoardService; use OCA\Deck\Service\DefaultBoardService;
use OCA\Deck\Service\PermissionService;
use OCP\IRequest; use OCP\IRequest;
use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Controller; use OCP\AppFramework\Controller;
@@ -32,13 +33,15 @@ use OCP\IL10N;
class PageController extends Controller { class PageController extends Controller {
private $defaultBoardService; private $defaultBoardService;
private $permissionService;
private $userId; private $userId;
private $l10n; private $l10n;
public function __construct( public function __construct(
$AppName, $AppName,
IRequest $request, IRequest $request,
DefaultBoardService $defaultBoardService, DefaultBoardService $defaultBoardService,
PermissionService $permissionService,
IL10N $l10n, IL10N $l10n,
$userId $userId
) { ) {
@@ -46,6 +49,7 @@ class PageController extends Controller {
$this->userId = $userId; $this->userId = $userId;
$this->defaultBoardService = $defaultBoardService; $this->defaultBoardService = $defaultBoardService;
$this->permissionService = $permissionService;
$this->l10n = $l10n; $this->l10n = $l10n;
} }
@@ -60,8 +64,9 @@ class PageController extends Controller {
$params = [ $params = [
'user' => $this->userId, 'user' => $this->userId,
'maxUploadSize' => (int)\OCP\Util::uploadLimit(), 'maxUploadSize' => (int)\OCP\Util::uploadLimit(),
'canCreate' => $this->permissionService->canCreate()
]; ];
if ($this->defaultBoardService->checkFirstRun($this->userId, $this->appName)) { if ($this->defaultBoardService->checkFirstRun($this->userId, $this->appName)) {
$this->defaultBoardService->createDefaultBoard($this->l10n->t('Personal'), $this->userId, '000000'); $this->defaultBoardService->createDefaultBoard($this->l10n->t('Personal'), $this->userId, '000000');
} }

View File

@@ -31,6 +31,7 @@ use OCA\Deck\Db\AssignedUsersMapper;
use OCA\Deck\Db\ChangeHelper; use OCA\Deck\Db\ChangeHelper;
use OCA\Deck\Db\IPermissionMapper; use OCA\Deck\Db\IPermissionMapper;
use OCA\Deck\Db\Label; use OCA\Deck\Db\Label;
use OCA\Deck\NoPermissionException;
use OCA\Deck\Notification\NotificationHelper; use OCA\Deck\Notification\NotificationHelper;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\IGroupManager; use OCP\IGroupManager;
@@ -250,6 +251,10 @@ class BoardService {
throw new BadRequestException('color must be provided'); throw new BadRequestException('color must be provided');
} }
if (!$this->permissionService->canCreate()) {
throw new NoPermissionException('Creating boards has been disabled for your account.');
}
$board = new Board(); $board = new Board();
$board->setTitle($title); $board->setTitle($title);
$board->setOwner($userId); $board->setOwner($userId);

View File

@@ -33,6 +33,7 @@ use OCA\Deck\NoPermissionException;
use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Db\DoesNotExistException;
use OCP\AppFramework\Db\Entity; use OCP\AppFramework\Db\Entity;
use OCP\AppFramework\Db\MultipleObjectsReturnedException; use OCP\AppFramework\Db\MultipleObjectsReturnedException;
use OCP\IConfig;
use OCP\IGroupManager; use OCP\IGroupManager;
use OCP\ILogger; use OCP\ILogger;
use OCP\IUserManager; use OCP\IUserManager;
@@ -50,6 +51,8 @@ class PermissionService {
private $userManager; private $userManager;
/** @var IGroupManager */ /** @var IGroupManager */
private $groupManager; private $groupManager;
/** @var IConfig */
private $config;
/** @var string */ /** @var string */
private $userId; private $userId;
/** @var array */ /** @var array */
@@ -61,6 +64,7 @@ class PermissionService {
BoardMapper $boardMapper, BoardMapper $boardMapper,
IUserManager $userManager, IUserManager $userManager,
IGroupManager $groupManager, IGroupManager $groupManager,
IConfig $config,
$userId $userId
) { ) {
$this->aclMapper = $aclMapper; $this->aclMapper = $aclMapper;
@@ -68,6 +72,7 @@ class PermissionService {
$this->logger = $logger; $this->logger = $logger;
$this->userManager = $userManager; $this->userManager = $userManager;
$this->groupManager = $groupManager; $this->groupManager = $groupManager;
$this->config = $config;
$this->userId = $userId; $this->userId = $userId;
} }
@@ -235,4 +240,23 @@ class PermissionService {
$this->users[(string) $boardId] = $users; $this->users[(string) $boardId] = $users;
return $this->users[(string) $boardId]; return $this->users[(string) $boardId];
} }
}
public function canCreate() {
$groups = $this->getGroupLimitList();
foreach ($groups as $group) {
if ($this->groupManager->isInGroup($this->userId, $group)) {
return false;
}
}
return true;
}
private function getGroupLimitList() {
$value = $this->config->getAppValue('deck', 'groupLimit', '');
$groups = explode(',', $value);
if ($value === '') {
return [];
}
return $groups;
}
}

View File

@@ -53,7 +53,7 @@ if (\OC_Util::getVersion()[0] < 14) {
<div id="app-navigation" data-ng-controller="ListController" ng-init="initSidebar()"> <div id="app-navigation" data-ng-controller="ListController" ng-init="initSidebar()">
<?php print_unescaped($this->inc('part.navigation')); ?> <?php print_unescaped($this->inc('part.navigation')); ?>
<?php /* print_unescaped($this->inc('part.settings')); */ ?> <?php print_unescaped($this->inc('part.settings')); ?>
</div> </div>
<div id="app-content" ng-class="{ 'details-visible': sidebar.show }"><div id="app-navigation-toggle-custom" class="icon-menu" ng-click="toggleSidebar()"></div><div ui-view></div></div> <div id="app-content" ng-class="{ 'details-visible': sidebar.show }"><div id="app-navigation-toggle-custom" class="icon-menu" ng-click="toggleSidebar()"></div><div ui-view></div></div>
<div id="app-sidebar" ng-class="{ 'details-visible': sidebar.show }" ng-if="sidebar.show" class="details-view scroll-container" ui-view="sidebarView"></div> <div id="app-sidebar" ng-class="{ 'details-visible': sidebar.show }" ng-if="sidebar.show" class="details-view scroll-container" ui-view="sidebarView"></div>

View File

@@ -95,7 +95,7 @@
</div> </div>
</td> </td>
</tr> </tr>
<tr ng-if="status.filter === '' && !status.addBoard" ng-click="status.addBoard=!status.addBoard" class="board-create"> <tr ng-if="canCreate && status.filter === '' && !status.addBoard" ng-click="status.addBoard=!status.addBoard" class="board-create">
<td><span class="icon icon-add"></span></td> <td><span class="icon icon-add"></span></td>
<td colspan="3"> <td colspan="3">
<a ng-click="status.addBoard=!status.addBoard" <a ng-click="status.addBoard=!status.addBoard"

View File

@@ -52,7 +52,7 @@
</div> </div>
</li> </li>
<li ng-class="{editing: status.addBoard}"> <li ng-class="{editing: status.addBoard}" ng-if="canCreate">
<a ng-click="status.addBoard=!status.addBoard" class="icon-add app-navigation-noclose"> <a ng-click="status.addBoard=!status.addBoard" class="icon-add app-navigation-noclose">
<?php p($l->t('Create a new board')); ?> <?php p($l->t('Create a new board')); ?>
</a> </a>

View File

@@ -1,8 +1,21 @@
<div id="app-settings"> <div id="app-settings" ng-if="isAdmin">
<div id="app-settings-header"> <div id="app-settings-header">
<button class="settings-button" data-apps-slide-toggle="#app-settings-content"></button> <button class="settings-button" data-apps-slide-toggle="#app-settings-content"><?php p($l->t('Settings')); ?></button>
</div> </div>
<div id="app-settings-content"> <div id="app-settings-content" class="hidden">
<ui-select multiple tagging="" ng-model="groupLimit" theme="select2"
title="<?php p($l->t('Limit deck to groups')); ?>"
placeholder="<?php p($l->t('Limit deck to groups')); ?>"
on-select="groupLimitAdd($item, $model)"
on-remove="groupLimitRemove($item, $model)" ng-disabled="groupLimitDisabled">
<ui-select-match placeholder="<?php p($l->t('Limit deck to groups')); ?>">
<span class="select-label">{{$item.displayname}}&nbsp;</span>
</ui-select-match>
<ui-select-choices
repeat="group in groups | filter: $select.search | limitTo: 3 track by group.id" position="down">
<span class="choose-label">{{group.displayname}}</span>
</ui-select-choices>
</ui-select>
<p class="hint"><?php p($l->t('Limiting Deck will block users not part of those groups from creating their own boards. Users will still be able to work on boards that have been shared with them.')); ?></p>
</div> </div>
</div> </div>