Merge pull request #9919 from nextcloud/immutable-groups

Don't pretend we can add/remove users to/from groups when we can't
This commit is contained in:
John Molakvoæ 2018-06-19 23:52:04 +02:00 committed by GitHub
commit 7fdba6f607
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 113 additions and 21 deletions

View File

@ -115,7 +115,9 @@ class GroupsController extends AUserData {
'id' => $group->getGID(), 'id' => $group->getGID(),
'displayname' => $group->getDisplayName(), 'displayname' => $group->getDisplayName(),
'usercount' => $group->count(), 'usercount' => $group->count(),
'disabled' => $group->countDisabled() 'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
]; ];
}, $groups); }, $groups);

View File

@ -107,6 +107,12 @@ class GroupsControllerTest extends \Test\TestCase {
$group $group
->method('countDisabled') ->method('countDisabled')
->willReturn(11); ->willReturn(11);
$group
->method('canAddUser')
->willReturn(true);
$group
->method('canRemoveUser')
->willReturn(true);
return $group; return $group;
} }
@ -215,13 +221,18 @@ class GroupsControllerTest extends \Test\TestCase {
'id' => 'group1', 'id' => 'group1',
'displayname' => 'group1-name', 'displayname' => 'group1-name',
'usercount' => 123, 'usercount' => 123,
'disabled' => 11 'disabled' => 11,
'canAdd' => true,
'canRemove' => true
), ),
Array( Array(
'id' => 'group2', 'id' => 'group2',
'displayname' => 'group2-name', 'displayname' => 'group2-name',
'usercount' => 123, 'usercount' => 123,
'disabled' => 11 'disabled' => 11,
'canAdd' => true,
'canRemove' => true
) )
]], $result->getData()); ]], $result->getData());

View File

@ -782,7 +782,7 @@ input {
color: nc-lighten($color-main-text, 33%); color: nc-lighten($color-main-text, 33%);
width: 100%; width: 100%;
/* selected checkmark icon */ /* selected checkmark icon */
&:not(.multiselect__option--disabled)::before { &::before {
content: ' '; content: ' ';
background-image: url('../img/actions/checkmark.svg?v=1'); background-image: url('../img/actions/checkmark.svg?v=1');
background-repeat: no-repeat; background-repeat: no-repeat;
@ -790,12 +790,13 @@ input {
min-width: 16px; min-width: 16px;
min-height: 16px; min-height: 16px;
display: block; display: block;
opacity: 0.5; opacity: .5;
margin-right: 5px; margin-right: 5px;
visibility: hidden; visibility: hidden;
} }
&.multiselect__option--disabled { &.multiselect__option--disabled {
background-color: nc-darken($color-main-background, 8%); background-color: nc-darken($color-main-background, 8%);
opacity: .5;
} }
/* add the prop tag-placeholder="create" to add the + /* add the prop tag-placeholder="create" to add the +
* icon on top of an unknown-and-ready-to-be-created entry * icon on top of an unknown-and-ready-to-be-created entry
@ -809,11 +810,15 @@ input {
&.multiselect__option--highlight { &.multiselect__option--highlight {
color: $color-main-text; color: $color-main-text;
} }
&.multiselect__option--selected { &:not(.multiselect__option--disabled):hover::before {
&::before { opacity: .3;
visibility: visible; }
} &.multiselect__option--selected,
} &:not(.multiselect__option--disabled):hover {
&::before {
visibility: visible;
}
}
} }
} }
} }

View File

@ -30,6 +30,7 @@
namespace OC\Group; namespace OC\Group;
use OCP\GroupInterface;
use OCP\IGroup; use OCP\IGroup;
use OCP\IUser; use OCP\IUser;
use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\ICountDisabledInGroup;
@ -323,4 +324,30 @@ class Group implements IGroup {
} }
return $users; return $users;
} }
/**
* @return bool
* @since 14.0.0
*/
public function canRemoveUser() {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::REMOVE_FROM_GOUP)) {
return true;
}
}
return false;
}
/**
* @return bool
* @since 14.0.0
*/
public function canAddUser() {
foreach ($this->backends as $backend) {
if ($backend->implementsActions(GroupInterface::ADD_TO_GROUP)) {
return true;
}
}
return false;
}
} }

View File

@ -165,7 +165,9 @@ class MetaData {
'id' => $group->getGID(), 'id' => $group->getGID(),
'name' => $group->getDisplayName(), 'name' => $group->getDisplayName(),
'usercount' => $this->sorting === self::SORT_USERCOUNT ? $group->count($userSearch) : 0, 'usercount' => $this->sorting === self::SORT_USERCOUNT ? $group->count($userSearch) : 0,
'disabled' => $group->countDisabled() 'disabled' => $group->countDisabled(),
'canAdd' => $group->canAddUser(),
'canRemove' => $group->canRemoveUser(),
); );
} }

View File

@ -126,4 +126,16 @@ interface IGroup {
* @since 8.0.0 * @since 8.0.0
*/ */
public function delete(); public function delete();
/**
* @return bool
* @since 14.0.0
*/
public function canRemoveUser();
/**
* @return bool
* @since 14.0.0
*/
public function canAddUser();
} }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -75,7 +75,7 @@
<!-- hidden input trick for vanilla html5 form validation --> <!-- hidden input trick for vanilla html5 form validation -->
<input type="text" :value="newUser.groups" v-if="!settings.isAdmin" <input type="text" :value="newUser.groups" v-if="!settings.isAdmin"
tabindex="-1" id="newgroups" :required="!settings.isAdmin" /> tabindex="-1" id="newgroups" :required="!settings.isAdmin" />
<multiselect :options="groups" v-model="newUser.groups" <multiselect :options="canAddGroups" v-model="newUser.groups"
:placeholder="t('settings', 'Add user in group')" :placeholder="t('settings', 'Add user in group')"
label="name" track-by="id" class="multiselect-vue" label="name" track-by="id" class="multiselect-vue"
:multiple="true" :close-on-select="false" :multiple="true" :close-on-select="false"
@ -202,7 +202,7 @@ export default {
return disabledUsers; return disabledUsers;
} }
if (!this.settings.isAdmin) { if (!this.settings.isAdmin) {
// We don't want subadmins to edit themselves // we don't want subadmins to edit themselves
return this.users.filter(user => user.enabled !== false && user.id !== oc_current_user); return this.users.filter(user => user.enabled !== false && user.id !== oc_current_user);
} }
return this.users.filter(user => user.enabled !== false); return this.users.filter(user => user.enabled !== false);
@ -213,6 +213,16 @@ export default {
.filter(group => group.id !== 'disabled') .filter(group => group.id !== 'disabled')
.sort((a, b) => a.name.localeCompare(b.name)); .sort((a, b) => a.name.localeCompare(b.name));
}, },
canAddGroups() {
// disabled if no permission to add new users to group
return this.groups.map((group) => {
// clone object because we don't want
// to edit the original groups
group = Object.assign({}, group);
group.$isDisabled = group.canAdd !== true;
return group;
});
},
subAdminsGroups() { subAdminsGroups() {
// data provided php side // data provided php side
return this.$store.getters.getSubadminGroups; return this.$store.getters.getSubadminGroups;

View File

@ -64,7 +64,7 @@
<input type="submit" class="icon-confirm" value="" /> <input type="submit" class="icon-confirm" value="" />
</form> </form>
<div class="groups" :class="{'icon-loading-small': loading.groups}"> <div class="groups" :class="{'icon-loading-small': loading.groups}">
<multiselect :value="userGroups" :options="groups" :disabled="loading.groups||loading.all" <multiselect :value="userGroups" :options="availableGroups" :disabled="loading.groups||loading.all"
tag-placeholder="create" :placeholder="t('settings', 'Add user in group')" tag-placeholder="create" :placeholder="t('settings', 'Add user in group')"
label="name" track-by="id" class="multiselect-vue" :limit="2" label="name" track-by="id" class="multiselect-vue" :limit="2"
:multiple="true" :taggable="settings.isAdmin" :closeOnSelect="false" :multiple="true" :taggable="settings.isAdmin" :closeOnSelect="false"
@ -182,6 +182,23 @@ export default {
let userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id)); let userSubAdminsGroups = this.subAdminsGroups.filter(group => this.user.subadmin.includes(group.id));
return userSubAdminsGroups; return userSubAdminsGroups;
}, },
availableGroups() {
return this.groups.map((group) => {
// clone object because we don't want
// to edit the original groups
let groupClone = Object.assign({}, group);
// two settings here:
// 1. user NOT in group but no permission to add
// 2. user is in group but no permission to remove
groupClone.$isDisabled =
(group.canAdd !== true &&
!this.user.groups.includes(group.id)) ||
(group.canRemove !== true &&
this.user.groups.includes(group.id));
return groupClone;
});
},
/* QUOTA MANAGEMENT */ /* QUOTA MANAGEMENT */
usedQuota() { usedQuota() {
@ -374,6 +391,9 @@ export default {
* @returns {Promise} * @returns {Promise}
*/ */
addUserGroup(group) { addUserGroup(group) {
if (!group.canAdd) {
return false;
}
this.loading.groups = true; this.loading.groups = true;
let userid = this.user.id; let userid = this.user.id;
let gid = group.id; let gid = group.id;
@ -388,6 +408,9 @@ export default {
* @returns {Promise} * @returns {Promise}
*/ */
removeUserGroup(group) { removeUserGroup(group) {
if (!group.canRemove) {
return false;
}
this.loading.groups = true; this.loading.groups = true;
let userid = this.user.id; let userid = this.user.id;
let gid = group.id; let gid = group.id;