Merge pull request #2260 from FinnStutzenstein/Feature435
New matrix interface for group editing (closes #435)
This commit is contained in:
commit
73095948ca
@ -27,6 +27,7 @@ Motions:
|
|||||||
Users:
|
Users:
|
||||||
- Added field is_committee and new default group Committees.
|
- Added field is_committee and new default group Committees.
|
||||||
- Added field number.
|
- Added field number.
|
||||||
|
- Added new matrix-interface for managing groups and their permissions.
|
||||||
|
|
||||||
Other:
|
Other:
|
||||||
- Removed config cache to support multiple threads or processes.
|
- Removed config cache to support multiple threads or processes.
|
||||||
|
@ -28,11 +28,13 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users'])
|
|||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
'Projector',
|
'Projector',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
function($http, DS, Speaker, jsDataModel, Projector, gettextCatalog) {
|
'gettext',
|
||||||
|
function($http, DS, Speaker, jsDataModel, Projector, gettextCatalog, gettext) {
|
||||||
var name = 'agenda/item';
|
var name = 'agenda/item';
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
|
verboseName: gettext('Agenda'),
|
||||||
methods: {
|
methods: {
|
||||||
getResourceName: function () {
|
getResourceName: function () {
|
||||||
return name;
|
return name;
|
||||||
|
@ -167,6 +167,7 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
verboseName: gettext('Election'),
|
verboseName: gettext('Election'),
|
||||||
|
verboseNamePlural: gettext('Elections'),
|
||||||
phases: phases,
|
phases: phases,
|
||||||
getPhases: function () {
|
getPhases: function () {
|
||||||
if (!this.phases) {
|
if (!this.phases) {
|
||||||
|
@ -798,6 +798,42 @@ img {
|
|||||||
content: ":";
|
content: ":";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* group list */
|
||||||
|
#groups-table {
|
||||||
|
table-layout: fixed;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table > thead > tr > th {
|
||||||
|
vertical-align: top;
|
||||||
|
text-align: center;
|
||||||
|
min-width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table .perm-head {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table > thead > tr > th > div {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table > tbody > tr:hover {
|
||||||
|
background-color: #f5f5f5 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table > tbody > tr:first-child {
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table > tbody > tr > td:first-child {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#groups-table .optional-show { /* hide optional-show column */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
/* search results */
|
/* search results */
|
||||||
.searchresults li {
|
.searchresults li {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
@ -976,7 +1012,6 @@ tr.selected td {
|
|||||||
color: #9a9898;
|
color: #9a9898;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*////////////////////////////////////////
|
/*////////////////////////////////////////
|
||||||
=MEDIA QUERIES (RESPONSIVE DESIGN)
|
=MEDIA QUERIES (RESPONSIVE DESIGN)
|
||||||
////////////////////////////////////////*/
|
////////////////////////////////////////*/
|
||||||
@ -999,6 +1034,16 @@ tr.selected td {
|
|||||||
@media only screen and (max-width: 900px) {
|
@media only screen and (max-width: 900px) {
|
||||||
|
|
||||||
#nav .navbar li a { padding: 24px 5px; }
|
#nav .navbar li a { padding: 24px 5px; }
|
||||||
|
|
||||||
|
#groups-table .perm-head {
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hide text in groups-table earlier */
|
||||||
|
#groups-table .optional { display: none; }
|
||||||
|
|
||||||
|
/* show replacement elements, if any */
|
||||||
|
#groups-table .optional-show { display: block !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1021,6 +1066,9 @@ tr.selected td {
|
|||||||
|
|
||||||
/* hide marked element / column */
|
/* hide marked element / column */
|
||||||
.optional, .hide-sm { display: none; }
|
.optional, .hide-sm { display: none; }
|
||||||
|
|
||||||
|
/* show replacement elements, if any */
|
||||||
|
.optional-show { display: block !important; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/* display for resolutions smaller that 560px */
|
/* display for resolutions smaller that 560px */
|
||||||
@ -1035,4 +1083,7 @@ tr.selected td {
|
|||||||
.col2 .projector_full {
|
.col2 .projector_full {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
}
|
}
|
||||||
|
#groups-table .perm-head {
|
||||||
|
width: 150px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,11 +7,14 @@ angular.module('OpenSlidesApp.mediafiles', [])
|
|||||||
.factory('Mediafile', [
|
.factory('Mediafile', [
|
||||||
'DS',
|
'DS',
|
||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
function(DS, jsDataModel) {
|
'gettext',
|
||||||
|
function(DS, jsDataModel, gettext) {
|
||||||
var name = 'mediafiles/mediafile';
|
var name = 'mediafiles/mediafile';
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
|
verboseName: gettext('Files'),
|
||||||
|
verboseNamePlural: gettext('Files'),
|
||||||
getAllImages: function () {
|
getAllImages: function () {
|
||||||
var images = [];
|
var images = [];
|
||||||
angular.forEach(this.getAll(), function(file) {
|
angular.forEach(this.getAll(), function(file) {
|
||||||
|
@ -123,6 +123,7 @@ angular.module('OpenSlidesApp.motions', [
|
|||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
verboseName: gettext('Motion'),
|
verboseName: gettext('Motion'),
|
||||||
|
verboseNamePlural: gettext('Motions'),
|
||||||
methods: {
|
methods: {
|
||||||
getResourceName: function () {
|
getResourceName: function () {
|
||||||
return name;
|
return name;
|
||||||
|
@ -3,7 +3,6 @@ from django.contrib.auth import get_user_model
|
|||||||
from django.contrib.auth.backends import ModelBackend
|
from django.contrib.auth.backends import ModelBackend
|
||||||
from django.contrib.auth.models import AnonymousUser as DjangoAnonymousUser
|
from django.contrib.auth.models import AnonymousUser as DjangoAnonymousUser
|
||||||
from django.contrib.auth.models import Permission
|
from django.contrib.auth.models import Permission
|
||||||
from django.db.models import Q
|
|
||||||
from django.utils.functional import SimpleLazyObject
|
from django.utils.functional import SimpleLazyObject
|
||||||
from rest_framework.authentication import BaseAuthentication
|
from rest_framework.authentication import BaseAuthentication
|
||||||
|
|
||||||
@ -14,9 +13,9 @@ from ..core.config import config
|
|||||||
|
|
||||||
class CustomizedModelBackend(ModelBackend):
|
class CustomizedModelBackend(ModelBackend):
|
||||||
"""
|
"""
|
||||||
Customized backend for authentication. Ensures that registered users
|
Customized backend for authentication. Ensures that all users
|
||||||
have all permissions of the group 'Registered' (pk=2). See
|
without a group have the permissions of the group 'Default' (pk=1).
|
||||||
AUTHENTICATION_BACKENDS settings.
|
See AUTHENTICATION_BACKENDS settings.
|
||||||
"""
|
"""
|
||||||
def get_group_permissions(self, user_obj, obj=None):
|
def get_group_permissions(self, user_obj, obj=None):
|
||||||
"""
|
"""
|
||||||
@ -31,11 +30,12 @@ class CustomizedModelBackend(ModelBackend):
|
|||||||
if user_obj.is_superuser:
|
if user_obj.is_superuser:
|
||||||
perms = Permission.objects.all()
|
perms = Permission.objects.all()
|
||||||
else:
|
else:
|
||||||
user_groups_field = get_user_model()._meta.get_field('groups')
|
if user_obj.groups.all().count() == 0: # user is in no group
|
||||||
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
|
perms = Permission.objects.filter(group__pk=1) # group 'default' (pk=1)
|
||||||
# The next two lines are the customization.
|
else:
|
||||||
query = Q(**{user_groups_query: user_obj}) | Q(group__pk=2)
|
user_groups_field = get_user_model()._meta.get_field('groups')
|
||||||
perms = Permission.objects.filter(query)
|
user_groups_query = 'group__%s' % user_groups_field.related_query_name()
|
||||||
|
perms = Permission.objects.filter(**{user_groups_query: user_obj})
|
||||||
perms = perms.values_list('content_type__app_label', 'codename').order_by()
|
perms = perms.values_list('content_type__app_label', 'codename').order_by()
|
||||||
user_obj._group_perm_cache = set("%s.%s" % (ct, name) for ct, name in perms)
|
user_obj._group_perm_cache = set("%s.%s" % (ct, name) for ct, name in perms)
|
||||||
return user_obj._group_perm_cache
|
return user_obj._group_perm_cache
|
||||||
|
@ -35,13 +35,13 @@ class UserManager(BaseUserManager):
|
|||||||
"""
|
"""
|
||||||
Creates an user with the username 'admin'. If such a user already
|
Creates an user with the username 'admin'. If such a user already
|
||||||
exists, resets it. The password is (re)set to 'admin'. The user
|
exists, resets it. The password is (re)set to 'admin'. The user
|
||||||
becomes member of the group 'Staff' (pk=4).
|
becomes member of the group 'Staff' (pk=3).
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
staff = Group.objects.get(pk=4)
|
staff = Group.objects.get(pk=3)
|
||||||
except Group.DoesNotExist:
|
except Group.DoesNotExist:
|
||||||
raise UsersError("Admin user can not be created or reset because "
|
raise UsersError("Admin user can not be created or reset because "
|
||||||
"the group 'Staff' (pk=4) is not available.")
|
"the group 'Staff' (pk=3) is not available.")
|
||||||
admin, created = self.get_or_create(
|
admin, created = self.get_or_create(
|
||||||
username='admin',
|
username='admin',
|
||||||
defaults={'last_name': 'Administrator'})
|
defaults={'last_name': 'Administrator'})
|
||||||
|
@ -44,7 +44,7 @@ class UserFullSerializer(ModelSerializer):
|
|||||||
"""
|
"""
|
||||||
groups = IdPrimaryKeyRelatedField(
|
groups = IdPrimaryKeyRelatedField(
|
||||||
many=True,
|
many=True,
|
||||||
queryset=Group.objects.exclude(pk__in=(1, 2)),
|
queryset=Group.objects.exclude(pk=1),
|
||||||
help_text=ugettext_lazy('The groups this user belongs to. A user will '
|
help_text=ugettext_lazy('The groups this user belongs to. A user will '
|
||||||
'get all permissions granted to each of '
|
'get all permissions granted to each of '
|
||||||
'his/her groups.'))
|
'his/her groups.'))
|
||||||
|
@ -6,13 +6,12 @@ from .models import Group, User
|
|||||||
|
|
||||||
def create_builtin_groups_and_admin(**kwargs):
|
def create_builtin_groups_and_admin(**kwargs):
|
||||||
"""
|
"""
|
||||||
Creates the builtin groups: Anonymous, Registered, Delegates, Staff and
|
Creates the builtin groups: Default, Delegates, Staff and Committees.
|
||||||
Committees.
|
|
||||||
|
|
||||||
Creates the builtin user: admin.
|
Creates the builtin user: admin.
|
||||||
"""
|
"""
|
||||||
# Check whether the group pk's 1 to 5 are free.
|
# Check whether the group pk's 1 to 4 are free.
|
||||||
if Group.objects.filter(pk__in=range(1, 6)).exists():
|
if Group.objects.filter(pk__in=range(1, 5)).exists():
|
||||||
# Do completely nothing if there are already some of our groups in the database.
|
# Do completely nothing if there are already some of our groups in the database.
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -53,7 +52,7 @@ def create_builtin_groups_and_admin(**kwargs):
|
|||||||
permission_string = '.'.join((permission.content_type.app_label, permission.codename))
|
permission_string = '.'.join((permission.content_type.app_label, permission.codename))
|
||||||
permission_dict[permission_string] = permission
|
permission_dict[permission_string] = permission
|
||||||
|
|
||||||
# Anonymous (pk 1) and Registered (pk 2)
|
# Default (pk 1)
|
||||||
base_permissions = (
|
base_permissions = (
|
||||||
permission_dict['agenda.can_see'],
|
permission_dict['agenda.can_see'],
|
||||||
permission_dict['agenda.can_see_hidden_items'],
|
permission_dict['agenda.can_see_hidden_items'],
|
||||||
@ -63,40 +62,54 @@ def create_builtin_groups_and_admin(**kwargs):
|
|||||||
permission_dict['mediafiles.can_see'],
|
permission_dict['mediafiles.can_see'],
|
||||||
permission_dict['motions.can_see'],
|
permission_dict['motions.can_see'],
|
||||||
permission_dict['users.can_see_name'], )
|
permission_dict['users.can_see_name'], )
|
||||||
group_anonymous = Group.objects.create(name='Guests', pk=1)
|
group_default = Group.objects.create(name='Default', pk=1)
|
||||||
group_anonymous.permissions.add(*base_permissions)
|
group_default.permissions.add(*base_permissions)
|
||||||
group_registered = Group.objects.create(name='Registered users', pk=2)
|
|
||||||
group_registered.permissions.add(
|
|
||||||
permission_dict['agenda.can_be_speaker'],
|
|
||||||
*base_permissions)
|
|
||||||
|
|
||||||
# Delegates (pk 3)
|
# Delegates (pk 2)
|
||||||
delegates_permissions = (
|
delegates_permissions = (
|
||||||
|
permission_dict['agenda.can_see'],
|
||||||
|
permission_dict['agenda.can_see_hidden_items'],
|
||||||
|
permission_dict['agenda.can_be_speaker'],
|
||||||
|
permission_dict['assignments.can_see'],
|
||||||
permission_dict['assignments.can_nominate_other'],
|
permission_dict['assignments.can_nominate_other'],
|
||||||
permission_dict['assignments.can_nominate_self'],
|
permission_dict['assignments.can_nominate_self'],
|
||||||
|
permission_dict['core.can_see_frontpage'],
|
||||||
|
permission_dict['core.can_see_projector'],
|
||||||
|
permission_dict['mediafiles.can_see'],
|
||||||
permission_dict['mediafiles.can_upload'],
|
permission_dict['mediafiles.can_upload'],
|
||||||
|
permission_dict['motions.can_see'],
|
||||||
permission_dict['motions.can_create'],
|
permission_dict['motions.can_create'],
|
||||||
permission_dict['motions.can_support'], )
|
permission_dict['motions.can_support'],
|
||||||
group_delegates = Group.objects.create(name='Delegates', pk=3)
|
permission_dict['users.can_see_name'], )
|
||||||
|
group_delegates = Group.objects.create(name='Delegates', pk=2)
|
||||||
group_delegates.permissions.add(*delegates_permissions)
|
group_delegates.permissions.add(*delegates_permissions)
|
||||||
|
|
||||||
# Staff (pk 4)
|
# Staff (pk 3)
|
||||||
staff_permissions = (
|
staff_permissions = (
|
||||||
|
permission_dict['agenda.can_see'],
|
||||||
|
permission_dict['agenda.can_see_hidden_items'],
|
||||||
|
permission_dict['agenda.can_be_speaker'],
|
||||||
permission_dict['agenda.can_manage'],
|
permission_dict['agenda.can_manage'],
|
||||||
|
permission_dict['assignments.can_see'],
|
||||||
permission_dict['assignments.can_manage'],
|
permission_dict['assignments.can_manage'],
|
||||||
permission_dict['assignments.can_nominate_other'],
|
permission_dict['assignments.can_nominate_other'],
|
||||||
permission_dict['assignments.can_nominate_self'],
|
permission_dict['assignments.can_nominate_self'],
|
||||||
|
permission_dict['core.can_see_frontpage'],
|
||||||
|
permission_dict['core.can_see_projector'],
|
||||||
permission_dict['core.can_manage_config'],
|
permission_dict['core.can_manage_config'],
|
||||||
permission_dict['core.can_manage_projector'],
|
permission_dict['core.can_manage_projector'],
|
||||||
permission_dict['core.can_manage_tags'],
|
permission_dict['core.can_manage_tags'],
|
||||||
permission_dict['core.can_use_chat'],
|
permission_dict['core.can_use_chat'],
|
||||||
|
permission_dict['mediafiles.can_see'],
|
||||||
permission_dict['mediafiles.can_manage'],
|
permission_dict['mediafiles.can_manage'],
|
||||||
permission_dict['mediafiles.can_upload'],
|
permission_dict['mediafiles.can_upload'],
|
||||||
|
permission_dict['motions.can_see'],
|
||||||
permission_dict['motions.can_create'],
|
permission_dict['motions.can_create'],
|
||||||
permission_dict['motions.can_manage'],
|
permission_dict['motions.can_manage'],
|
||||||
|
permission_dict['users.can_see_name'],
|
||||||
permission_dict['users.can_manage'],
|
permission_dict['users.can_manage'],
|
||||||
permission_dict['users.can_see_extra_data'],)
|
permission_dict['users.can_see_extra_data'],)
|
||||||
group_staff = Group.objects.create(name='Staff', pk=4)
|
group_staff = Group.objects.create(name='Staff', pk=3)
|
||||||
group_staff.permissions.add(*staff_permissions)
|
group_staff.permissions.add(*staff_permissions)
|
||||||
|
|
||||||
# Add users.can_see_name permission to staff
|
# Add users.can_see_name permission to staff
|
||||||
@ -105,12 +118,21 @@ def create_builtin_groups_and_admin(**kwargs):
|
|||||||
group_staff.permissions.add(
|
group_staff.permissions.add(
|
||||||
permission_dict['users.can_see_name'])
|
permission_dict['users.can_see_name'])
|
||||||
|
|
||||||
# Committees (pk 5)
|
# Committees (pk 4)
|
||||||
committees_permissions = (
|
committees_permissions = (
|
||||||
|
permission_dict['agenda.can_see'],
|
||||||
|
permission_dict['agenda.can_see_hidden_items'],
|
||||||
|
permission_dict['agenda.can_be_speaker'],
|
||||||
|
permission_dict['assignments.can_see'],
|
||||||
|
permission_dict['core.can_see_frontpage'],
|
||||||
|
permission_dict['core.can_see_projector'],
|
||||||
|
permission_dict['mediafiles.can_see'],
|
||||||
permission_dict['mediafiles.can_upload'],
|
permission_dict['mediafiles.can_upload'],
|
||||||
|
permission_dict['motions.can_see'],
|
||||||
permission_dict['motions.can_create'],
|
permission_dict['motions.can_create'],
|
||||||
permission_dict['motions.can_support'], )
|
permission_dict['motions.can_support'],
|
||||||
group_committee = Group.objects.create(name='Committees', pk=5)
|
permission_dict['users.can_see_name'], )
|
||||||
|
group_committee = Group.objects.create(name='Committees', pk=4)
|
||||||
group_committee.permissions.add(*committees_permissions)
|
group_committee.permissions.add(*committees_permissions)
|
||||||
|
|
||||||
# Create or reset admin user
|
# Create or reset admin user
|
||||||
|
@ -55,6 +55,10 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
}
|
}
|
||||||
return _.intersection(perms, operator.perms).length > 0;
|
return _.intersection(perms, operator.perms).length > 0;
|
||||||
},
|
},
|
||||||
|
// Returns true if the operator is a member of group.
|
||||||
|
isInGroup: function(group) {
|
||||||
|
return _.indexOf(operator.user.groups_id, group.id) > -1;
|
||||||
|
},
|
||||||
};
|
};
|
||||||
return operator;
|
return operator;
|
||||||
}
|
}
|
||||||
@ -64,11 +68,14 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
'DS',
|
'DS',
|
||||||
'Group',
|
'Group',
|
||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
function(DS, Group, jsDataModel) {
|
'gettext',
|
||||||
|
function(DS, Group, jsDataModel, gettext) {
|
||||||
var name = 'users/user';
|
var name = 'users/user';
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
useClass: jsDataModel,
|
useClass: jsDataModel,
|
||||||
|
verboseName: gettext('Participants'),
|
||||||
|
verboseNamePlural: gettext('Participants'),
|
||||||
computed: {
|
computed: {
|
||||||
full_name: function () {
|
full_name: function () {
|
||||||
return this.get_full_name();
|
return this.get_full_name();
|
||||||
@ -125,8 +132,9 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
if (this.groups_id) {
|
if (this.groups_id) {
|
||||||
allGroups = this.groups_id.slice(0);
|
allGroups = this.groups_id.slice(0);
|
||||||
}
|
}
|
||||||
// Add registered group
|
if (allGroups.length === 0) {
|
||||||
allGroups.push(2);
|
allGroups.push(1); // add default group
|
||||||
|
}
|
||||||
_.forEach(allGroups, function(groupId) {
|
_.forEach(allGroups, function(groupId) {
|
||||||
var group = Group.get(groupId);
|
var group = Group.get(groupId);
|
||||||
if (group) {
|
if (group) {
|
||||||
@ -191,10 +199,10 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
'gettext',
|
'gettext',
|
||||||
function (gettext) {
|
function (gettext) {
|
||||||
// default group names (from users/signals.py)
|
// default group names (from users/signals.py)
|
||||||
gettext('Guests');
|
gettext('Default');
|
||||||
gettext('Registered users');
|
|
||||||
gettext('Delegates');
|
gettext('Delegates');
|
||||||
gettext('Staff');
|
gettext('Staff');
|
||||||
|
gettext('Committees');
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -111,36 +111,12 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
|||||||
resolve: {
|
resolve: {
|
||||||
groups: function(Group) {
|
groups: function(Group) {
|
||||||
return Group.findAll();
|
return Group.findAll();
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('users.group.create', {
|
|
||||||
resolve: {
|
|
||||||
permissions: function(Group) {
|
|
||||||
return Group.getPermissions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('users.group.detail', {
|
|
||||||
resolve: {
|
|
||||||
group: function(Group, $stateParams) {
|
|
||||||
return Group.find($stateParams.id);
|
|
||||||
},
|
},
|
||||||
permissions: function(Group) {
|
permissions: function(Group) {
|
||||||
return Group.getPermissions();
|
return Group.getPermissions();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('users.group.detail.update', {
|
|
||||||
views: {
|
|
||||||
'@users.group': {}
|
|
||||||
},
|
|
||||||
resolve: {
|
|
||||||
permissions: function(Group) {
|
|
||||||
return Group.getPermissions();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('login', {
|
.state('login', {
|
||||||
template: null,
|
template: null,
|
||||||
url: '/login',
|
url: '/login',
|
||||||
@ -331,7 +307,7 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
|||||||
label: gettextCatalog.getString('Groups'),
|
label: gettextCatalog.getString('Groups'),
|
||||||
options: Group.getAll(),
|
options: Group.getAll(),
|
||||||
ngOptions: 'option.id as option.name | translate for option in to.options | ' +
|
ngOptions: 'option.id as option.name | translate for option in to.options | ' +
|
||||||
'filter: {id: "!1"} | filter: {id: "!2"}',
|
'filter: {id: "!1"}',
|
||||||
placeholder: gettextCatalog.getString('Select or search a group ...')
|
placeholder: gettextCatalog.getString('Select or search a group ...')
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -850,79 +826,233 @@ angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
|||||||
|
|
||||||
.controller('GroupListCtrl', [
|
.controller('GroupListCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
'$http',
|
||||||
|
'operator',
|
||||||
'Group',
|
'Group',
|
||||||
function($scope, Group) {
|
'permissions',
|
||||||
Group.bindAll({}, $scope, 'groups');
|
'gettext',
|
||||||
|
'Agenda',
|
||||||
|
'Assignment',
|
||||||
|
'Mediafile',
|
||||||
|
'Motion',
|
||||||
|
'User',
|
||||||
|
'ngDialog',
|
||||||
|
function($scope, $http, operator, Group, permissions, gettext, Agenda, Assignment, Mediafile, Motion, User, ngDialog) {
|
||||||
|
//Group.bindAll({}, $scope, 'groups');
|
||||||
|
$scope.permissions = permissions;
|
||||||
|
|
||||||
|
$scope.$watch(function() {
|
||||||
|
return Group.lastModified();
|
||||||
|
}, function() {
|
||||||
|
$scope.groups = Group.getAll();
|
||||||
|
|
||||||
|
// find all groups with the 2 dangerous permissions
|
||||||
|
var groups_danger = [];
|
||||||
|
$scope.groups.forEach(function (group) {
|
||||||
|
if ((_.indexOf(group.permissions, 'users.can_see_name') > -1) &&
|
||||||
|
(_.indexOf(group.permissions, 'users.can_manage') > -1)){
|
||||||
|
if (operator.isInGroup(group)){
|
||||||
|
groups_danger.push(group);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// if there is only one dangerous group, block it.
|
||||||
|
$scope.group_danger = groups_danger.length == 1 ? groups_danger[0] : null;
|
||||||
|
});
|
||||||
|
|
||||||
|
$scope.apps = [];
|
||||||
|
// Create the main clustering with appname->permissions
|
||||||
|
angular.forEach(permissions, function(perm) {
|
||||||
|
var permissionApp = perm.value.split('.')[0]; // get appname
|
||||||
|
|
||||||
|
// To insert perm in the right spot in $scope.apps
|
||||||
|
var insert = function (id, perm, verboseName) {
|
||||||
|
if (!$scope.apps[id]) {
|
||||||
|
$scope.apps[id] = {
|
||||||
|
app_name: verboseName,
|
||||||
|
app_visible: true,
|
||||||
|
permissions: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
$scope.apps[id].permissions.push(perm);
|
||||||
|
};
|
||||||
|
|
||||||
|
switch(permissionApp) {
|
||||||
|
case 'core': // id 0 (projector) and id 6 (general)
|
||||||
|
if (perm.value.indexOf('projector') > -1) {
|
||||||
|
insert(0, perm, gettext('Projector'));
|
||||||
|
} else {
|
||||||
|
insert(6, perm, gettext('General'));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'agenda': // id 1
|
||||||
|
insert(1, perm, Agenda.verboseName);
|
||||||
|
break;
|
||||||
|
case 'motions': // id 2
|
||||||
|
insert(2, perm, Motion.verboseNamePlural);
|
||||||
|
break;
|
||||||
|
case 'assignments': // id 3
|
||||||
|
insert(3, perm, Assignment.verboseNamePlural);
|
||||||
|
break;
|
||||||
|
case 'mediafiles': // id 4
|
||||||
|
insert(4, perm, Mediafile.verboseNamePlural);
|
||||||
|
break;
|
||||||
|
case 'users': // id 5
|
||||||
|
insert(5, perm, User.verboseNamePlural);
|
||||||
|
break;
|
||||||
|
default: // plugins: id>5
|
||||||
|
var display_name = permissionApp.charAt(0).toUpperCase() + permissionApp.slice(1);
|
||||||
|
// does the app exists?
|
||||||
|
var result = -1;
|
||||||
|
angular.forEach($scope.apps, function (app, index) {
|
||||||
|
if (app.app_name === display_name)
|
||||||
|
result = index;
|
||||||
|
});
|
||||||
|
var id = result == -1 ? $scope.apps.length : result;
|
||||||
|
insert(id, perm, display_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort each app: first all permission with 'see', then 'manage', then the rest
|
||||||
|
// save the permissions in different lists an concat them in the right order together
|
||||||
|
// Special Users: the two "see"-permissions are normally swapped. To create the right
|
||||||
|
// order, we could simply reverse the whole permissions.
|
||||||
|
angular.forEach($scope.apps, function (app, index) {
|
||||||
|
if(index == 5) { // users
|
||||||
|
app.permissions.reverse();
|
||||||
|
} else { // rest
|
||||||
|
var see = [];
|
||||||
|
var manage = [];
|
||||||
|
var others = [];
|
||||||
|
angular.forEach(app.permissions, function (perm) {
|
||||||
|
if (perm.value.indexOf('see') > -1) {
|
||||||
|
see.push(perm);
|
||||||
|
} else if (perm.value.indexOf('manage') > -1) {
|
||||||
|
manage.push(perm);
|
||||||
|
} else {
|
||||||
|
others.push(perm);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.permissions = see.concat(manage.concat(others));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// check if the given group has the given permission
|
||||||
|
$scope.hasPerm = function (group, permission) {
|
||||||
|
return _.indexOf(group.permissions, permission.value) > -1;
|
||||||
|
};
|
||||||
|
|
||||||
|
// The current user is not allowed to lock himself out of the configuration:
|
||||||
|
// - if the permission is 'users.can_manage' or 'users.can_see'
|
||||||
|
// - if the user is in only one group with these permissions (group_danger is set)
|
||||||
|
$scope.danger = function (group, permission){
|
||||||
|
if ($scope.group_danger){
|
||||||
|
if (permission.value == 'users.can_see_name' ||
|
||||||
|
permission.value == 'users.can_manage'){
|
||||||
|
return $scope.group_danger == group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
// delete selected group
|
// delete selected group
|
||||||
$scope.delete = function (group) {
|
$scope.delete = function (group) {
|
||||||
Group.destroy(group.id);
|
Group.destroy(group.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// save changed permission
|
||||||
|
$scope.changePermission = function (group, perm) {
|
||||||
|
if (!$scope.danger(group, perm)) {
|
||||||
|
if (!$scope.hasPerm(group, perm)) { // activate perm
|
||||||
|
group.permissions.push(perm.value);
|
||||||
|
} else {
|
||||||
|
// delete perm in group.permissions
|
||||||
|
group.permissions = _.filter(group.permissions, function(value) {
|
||||||
|
return value != perm.value; // remove perm
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Group.save(group);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.openDialog = function (group) {
|
||||||
|
var resolve;
|
||||||
|
if (group) {
|
||||||
|
resolve = {
|
||||||
|
group: function() {return Group.find(group.id);}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ngDialog.open({
|
||||||
|
template: 'static/templates/users/group-edit.html',
|
||||||
|
controller: group ? 'GroupRenameCtrl' : 'GroupCreateCtrl',
|
||||||
|
className: 'ngdialog-theme-default wide-form',
|
||||||
|
closeByEscape: false,
|
||||||
|
closeByDocument: false,
|
||||||
|
resolve: (resolve) ? resolve : null
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
.controller('GroupRenameCtrl', [
|
||||||
|
'$scope',
|
||||||
|
'Group',
|
||||||
|
'group',
|
||||||
|
function($scope, Group, group) {
|
||||||
|
$scope.group = group;
|
||||||
|
$scope.new_name = group.name;
|
||||||
|
|
||||||
|
$scope.alert = {};
|
||||||
|
$scope.save = function() {
|
||||||
|
var old_name = $scope.group.name;
|
||||||
|
$scope.group.name = $scope.new_name;
|
||||||
|
Group.save($scope.group).then(
|
||||||
|
function (success) {
|
||||||
|
$scope.closeThisDialog();
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
var message = '';
|
||||||
|
for (var e in error.data) {
|
||||||
|
message += e + ': ' + error.data[e] + ' ';
|
||||||
|
}
|
||||||
|
$scope.alert = { msg: message, show: true };
|
||||||
|
$scope.group.name = old_name;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
.controller('GroupCreateCtrl', [
|
.controller('GroupCreateCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
'$state',
|
|
||||||
'Group',
|
'Group',
|
||||||
'permissions',
|
function($scope, Group) {
|
||||||
function($scope, $state, Group, permissions) {
|
$scope.new_name = '';
|
||||||
// get all permissions
|
$scope.alert = {};
|
||||||
$scope.permissions = permissions;
|
|
||||||
$scope.group = {};
|
$scope.save = function() {
|
||||||
$scope.save = function (group) {
|
var group = {
|
||||||
if (!group.permissions) {
|
name: $scope.new_name,
|
||||||
group.permissions = [];
|
permissions: []
|
||||||
}
|
};
|
||||||
|
|
||||||
Group.create(group).then(
|
Group.create(group).then(
|
||||||
function(success) {
|
function (success) {
|
||||||
$state.go('users.group.list');
|
$scope.closeThisDialog();
|
||||||
|
},
|
||||||
|
function (error) {
|
||||||
|
var message = '';
|
||||||
|
for (var e in error.data) {
|
||||||
|
message += e + ': ' + error.data[e] + ' ';
|
||||||
|
}
|
||||||
|
$scope.alert = { msg: message, show: true };
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
.controller('GroupUpdateCtrl', [
|
|
||||||
'$scope',
|
|
||||||
'$state',
|
|
||||||
'Group',
|
|
||||||
'permissions',
|
|
||||||
'group',
|
|
||||||
function($scope, $state, Group, permissions, group) {
|
|
||||||
// get all permissions
|
|
||||||
$scope.permissions = permissions;
|
|
||||||
$scope.group = group; // autoupdate is not activated
|
|
||||||
$scope.save = function (group) {
|
|
||||||
Group.save(group).then(
|
|
||||||
function(success) {
|
|
||||||
$state.go('users.group.list');
|
|
||||||
}
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.controller('GroupDetailCtrl', [
|
|
||||||
'$scope',
|
|
||||||
'Group',
|
|
||||||
'group',
|
|
||||||
'permissions',
|
|
||||||
function($scope, Group, group, permissions) {
|
|
||||||
Group.bindOne(group.id, $scope, 'group');
|
|
||||||
$scope.groupPermissionNames = [];
|
|
||||||
// get display names of group permissions
|
|
||||||
// from an object array with all available permissions [{display_name, value}]
|
|
||||||
angular.forEach(group.permissions, function(permValue) {
|
|
||||||
angular.forEach(permissions, function(p) {
|
|
||||||
if (p.value == permValue) {
|
|
||||||
$scope.groupPermissionNames.push(p.display_name);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.controller('userMenu', [
|
.controller('userMenu', [
|
||||||
'$scope',
|
'$scope',
|
||||||
'$http',
|
'$http',
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
<div class="header">
|
|
||||||
<div class="title">
|
|
||||||
<div class="submenu">
|
|
||||||
<a ui-sref="users.group.list" class="btn btn-sm btn-default">
|
|
||||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
|
||||||
<translate>Back to overview</translate>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h1>{{ group.name | translate }}</h1>
|
|
||||||
<h2 translate>Group</h2>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="details">
|
|
||||||
<p class="lead" translate>Permissions:</p>
|
|
||||||
<ul ng-repeat="permission in groupPermissionNames">
|
|
||||||
<li>{{ permission | translate }}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
27
openslides/users/static/templates/users/group-edit.html
Normal file
27
openslides/users/static/templates/users/group-edit.html
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<h1 ng-if="group" translate>Edit name</h1>
|
||||||
|
<h1 ng-if="!group" translate>Create new group</h1>
|
||||||
|
<uib-alert ng-show="alert.show" type="danger" ng-click="alert={}" close="alert={}">
|
||||||
|
{{ alert.msg }}
|
||||||
|
</uib-alert>
|
||||||
|
<label for="name_1">
|
||||||
|
<span ng-if="group">
|
||||||
|
<translate>Please enter a new name for</translate>
|
||||||
|
{{ group.name }}
|
||||||
|
</span>
|
||||||
|
<span ng-if="!group" translate>
|
||||||
|
Please enter a name for the new group
|
||||||
|
</span>
|
||||||
|
</label>
|
||||||
|
<div>
|
||||||
|
<div>
|
||||||
|
<input class="form-control" id="name_1" type="text" ng-model="new_name">
|
||||||
|
</div>
|
||||||
|
<div class="spacer-top-lg">
|
||||||
|
<button ng-click="save()" ng-disabled="new_name == ''" class="btn btn-primary" translate>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
<button ng-click="closeThisDialog()" class="btn btn-default" translate>
|
||||||
|
Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
@ -1,34 +0,0 @@
|
|||||||
<div class="header">
|
|
||||||
<div class="title">
|
|
||||||
<div class="submenu">
|
|
||||||
<a ui-sref="users.group.list" class="btn btn-sm btn-default">
|
|
||||||
<i class="fa fa-angle-double-left fa-lg"></i>
|
|
||||||
<translate>Back to overview</translate>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<h1 ng-if="group.id" translate>Edit group</h1>
|
|
||||||
<h1 ng-if="!group.id" translate>New group</h1>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="details">
|
|
||||||
<form name="groupForm">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="inputName" translate>Name</label>
|
|
||||||
<input type="text" ng-model="group.name" class="form-control" name="inputName" required>
|
|
||||||
</div>
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="selectPermissions" translate>Permissions</label>
|
|
||||||
<select multiple size="15" ng-model="group.permissions" class="form-control" id="selectPermissions">
|
|
||||||
<option value="{{ permission.value }}" ng-repeat="permission in permissions">{{ permission.display_name | translate }}</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="submit" ng-click="save(group)" class="btn btn-primary" translate>
|
|
||||||
Save
|
|
||||||
</button>
|
|
||||||
<button ui-sref="users.group.list" class="btn btn-default" translate>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
@ -1,7 +1,8 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
<div class="submenu">
|
<div class="submenu">
|
||||||
<a ui-sref="users.group.create" os-perms="users.can_manage" class="btn btn-primary btn-sm">
|
<a os-perms="users.can_manage" class="btn btn-primary btn-sm"
|
||||||
|
ng-click="openDialog()" title="add new group">
|
||||||
<i class="fa fa-plus fa-lg"></i>
|
<i class="fa fa-plus fa-lg"></i>
|
||||||
<translate>New</translate>
|
<translate>New</translate>
|
||||||
</a>
|
</a>
|
||||||
@ -15,48 +16,58 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="details">
|
<div class="details">
|
||||||
<div class="row form-group">
|
<p translate>
|
||||||
<div class="col-sm-4 pull-right">
|
All your changes are saved immediately. Changes you make are only effective once you (or the users concerned) reload the page.
|
||||||
<div class="form-group">
|
</p>
|
||||||
<div class="input-group">
|
<table id="groups-table" class="table table-bordered">
|
||||||
<div class="input-group-addon"><i class="fa fa-search"></i></div>
|
|
||||||
<input type="text" ng-model="filter.search" class="form-control"
|
|
||||||
placeholder="{{ 'Search' | translate}}">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<table class="table table-striped table-bordered table-hover">
|
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th ng-click="sortby='id';reverse=!reverse">
|
<th class="perm-head">
|
||||||
<translate>ID</translate>
|
<h4 translate>Permissions</h4>
|
||||||
<i class="fa" ng-show="sortby == 'id'"
|
<th ng-repeat="group in groups" ng-mouseover="group.hover=true" ng-mouseleave="group.hover=false">
|
||||||
ng-class="reverse ? 'fa-caret-down' : 'fa-caret-up'"></i>
|
<span class="optional">
|
||||||
<th ng-click="sortby='name';reverse=!reverse">
|
{{ group.name | translate }}
|
||||||
<translate>Name</translate>
|
</span>
|
||||||
<i class="fa" ng-show="sortby == 'name'"
|
<span class="optional-show" uib-tooltip="{{ group.name }}">
|
||||||
ng-class="reverse ? 'fa-caret-down' : 'fa-caret-up'"></i>
|
{{ group.name | translate | limitTo: 1 }}...
|
||||||
<th os-perms="users.can_manage core.can_manage_projector" class="mini_width" translate>Actions</th>
|
</span>
|
||||||
<tbody>
|
<i class="fa fa-info-circle" ng-if="group.id == 1"
|
||||||
<tr ng-repeat="group in groups | filter: filter.search | orderBy:sortby:reverse">
|
uib-tooltip="{{ 'Users without any assigned group gain the permissions from this group.' | translate }}"></i>
|
||||||
<td>{{ group.id }}
|
<div os-perms="users.can_manage" ng-hide="group.id == 1" class="hoverActions"
|
||||||
<td><a ui-sref="users.group.detail({id: group.id})">{{ group.name | translate }}</a>
|
ng-class="{'hiddenDiv': !group.hover}">
|
||||||
<td os-perms="users.can_manage" class="nobr">
|
<!--edit name-->
|
||||||
<!-- edit -->
|
<a href="" ng-click="openDialog(group)">
|
||||||
<a ui-sref="users.group.detail.update({id: group.id})" os-perms="users.can_manage"
|
<i class="fa fa-pencil fa-lg"></i></a>
|
||||||
class="btn btn-default btn-sm"
|
|
||||||
title="{{ 'Edit' | translate }}">
|
<!--delete-->
|
||||||
<i class="fa fa-pencil"></i>
|
<a href="" class="text-danger"
|
||||||
</a>
|
|
||||||
<!-- delete -->
|
|
||||||
<a os-perms="users.can_manage" class="btn btn-danger btn-sm"
|
|
||||||
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
|
||||||
<b>{{ group.name }}</b>"
|
<b>{{ group.name | translate }}</b>"
|
||||||
ng-bootbox-confirm-action="delete(group)"
|
ng-bootbox-confirm-action="delete(group)" translate>
|
||||||
title="{{ 'Delete' | translate }}">
|
<i class="fa fa-trash fa-lg"></i>
|
||||||
<i class="fa fa-trash-o"></i>
|
</a>
|
||||||
</a>
|
</div>
|
||||||
|
<tbody ng-repeat="app in apps" os-perms="users.can_manage">
|
||||||
|
<tr class="pointer" ng-click="app.app_visible=!app.app_visible">
|
||||||
|
<td>
|
||||||
|
<b>{{ app.app_name | translate}}</b>
|
||||||
|
<i class="fa" ng-class="app.app_visible ? 'fa-minus-square' : 'fa-plus-square'">
|
||||||
|
<td ng-repeat="group in groups">
|
||||||
|
<!-- empty, just filling the table -->
|
||||||
|
<tr ng-repeat="permission in app.permissions" ng-class="{'collapse': !app.app_visible}">
|
||||||
|
<td>
|
||||||
|
{{ permission.display_name | translate }}
|
||||||
|
<td class="pointer" ng-repeat="group in groups"
|
||||||
|
ng-click="changePermission(group, permission)">
|
||||||
|
<!-- Simulating a checkbox with FontAwesome icons. -->
|
||||||
|
<i class="fa"
|
||||||
|
ng-if="!danger(group, permission)"
|
||||||
|
ng-class="hasPerm(group, permission) ? 'fa-check-square-o' : 'fa-square-o'"></i>
|
||||||
|
<span class="fa fa-stack"
|
||||||
|
ng-if="danger(group, permission)"
|
||||||
|
uib-tooltip="{{ 'You are not allowed to lock yourself out of the configuration!' | translate }}">
|
||||||
|
<i class="fa fa-check-square-o fa-stack-1x"></i>
|
||||||
|
<i class="fa fa-ban fa-stack-2x text-danger"></i>
|
||||||
|
</span>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
@ -70,9 +70,9 @@
|
|||||||
<li><translate>Required comma or semicolon separated values with these column header names in the first row</translate>:<br>
|
<li><translate>Required comma or semicolon separated values with these column header names in the first row</translate>:<br>
|
||||||
<code>title, first_name, last_name, structure_level, number, groups, comment, is_active, is_committee</code>
|
<code>title, first_name, last_name, structure_level, number, groups, comment, is_active, is_committee</code>
|
||||||
<li><translate>Default groups</translate>:
|
<li><translate>Default groups</translate>:
|
||||||
<translate>Delegates</translate> <code>3</code>,
|
<translate>Delegates</translate> <code>2</code>,
|
||||||
<translate>Staff</translate> <code>4</code>
|
<translate>Staff</translate> <code>3</code>
|
||||||
<translate>Committees</translate> <code>5</code>
|
<translate>Committees</translate> <code>4</code>
|
||||||
<li translate>At least first name or last name have to be filled in. All
|
<li translate>At least first name or last name have to be filled in. All
|
||||||
other fields are optional and may be empty.
|
other fields are optional and may be empty.
|
||||||
<li translate>Only double quotes are accepted as text delimiter (no single quotes).
|
<li translate>Only double quotes are accepted as text delimiter (no single quotes).
|
||||||
|
@ -150,7 +150,7 @@ class RetrieveMotion(TestCase):
|
|||||||
self.motion.create_poll()
|
self.motion.create_poll()
|
||||||
|
|
||||||
def test_number_of_queries(self):
|
def test_number_of_queries(self):
|
||||||
with self.assertNumQueries(16):
|
with self.assertNumQueries(17):
|
||||||
self.client.get(reverse('motion-detail', args=[self.motion.pk]))
|
self.client.get(reverse('motion-detail', args=[self.motion.pk]))
|
||||||
|
|
||||||
|
|
||||||
@ -312,7 +312,7 @@ class SupportMotion(TestCase):
|
|||||||
"""
|
"""
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.admin = get_user_model().objects.get(username='admin')
|
self.admin = get_user_model().objects.get(username='admin')
|
||||||
self.admin.groups.add(3)
|
self.admin.groups.add(2)
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
self.motion = Motion(
|
self.motion = Motion(
|
||||||
title='test_title_chee7ahCha6bingaew4e',
|
title='test_title_chee7ahCha6bingaew4e',
|
||||||
|
@ -44,7 +44,7 @@ class UserCreate(TestCase):
|
|||||||
def test_creation_with_group(self):
|
def test_creation_with_group(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
# These are the builtin groups 'Delegates' and 'Staff'. The pks are valid.
|
# These are the builtin groups 'Delegates' and 'Staff'. The pks are valid.
|
||||||
group_pks = (3, 4,)
|
group_pks = (2, 3,)
|
||||||
|
|
||||||
self.client.post(
|
self.client.post(
|
||||||
reverse('user-list'),
|
reverse('user-list'),
|
||||||
@ -55,19 +55,19 @@ class UserCreate(TestCase):
|
|||||||
self.assertTrue(user.groups.filter(pk=group_pks[0]).exists())
|
self.assertTrue(user.groups.filter(pk=group_pks[0]).exists())
|
||||||
self.assertTrue(user.groups.filter(pk=group_pks[1]).exists())
|
self.assertTrue(user.groups.filter(pk=group_pks[1]).exists())
|
||||||
|
|
||||||
def test_creation_with_anonymous_or_registered_group(self):
|
def test_creation_with_default_group(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
# These are the builtin groups 'Anonymous' and 'Registered'.
|
# This is the builtin groups 'default'.
|
||||||
# The pks are valid. But these groups can not be added to users.
|
# The pk is valid. But this group can not be added to users.
|
||||||
group_pks = (1, 2,)
|
group_pk = (1,)
|
||||||
|
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
reverse('user-list'),
|
reverse('user-list'),
|
||||||
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
{'last_name': 'Test name aedah1iequoof0Ashed4',
|
||||||
'groups_id': group_pks})
|
'groups_id': group_pk})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
self.assertEqual(response.data, {'groups_id': ["Invalid pk \"%d\" - object does not exist." % group_pks[0]]})
|
self.assertEqual(response.data, {'groups_id': ["Invalid pk \"%d\" - object does not exist." % group_pk]})
|
||||||
|
|
||||||
|
|
||||||
class UserUpdate(TestCase):
|
class UserUpdate(TestCase):
|
||||||
@ -211,8 +211,8 @@ class GroupReceive(TestCase):
|
|||||||
user = User(username='test')
|
user = User(username='test')
|
||||||
user.set_password('test')
|
user.set_password('test')
|
||||||
user.save()
|
user.save()
|
||||||
registered_group = Group.objects.get(pk=2)
|
default_group = Group.objects.get(pk=1)
|
||||||
registered_group.permissions.all().delete()
|
default_group.permissions.all().delete()
|
||||||
self.client.login(username='test', password='test')
|
self.client.login(username='test', password='test')
|
||||||
|
|
||||||
response = self.client.get('/rest/users/group/')
|
response = self.client.get('/rest/users/group/')
|
||||||
@ -277,7 +277,7 @@ class GroupUpdate(TestCase):
|
|||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
# This is the builtin group 'Delegates'. The pk is valid.
|
# This is the builtin group 'Delegates'. The pk is valid.
|
||||||
group_pk = 3
|
group_pk = 2
|
||||||
# This contains one valid permission of the users app.
|
# This contains one valid permission of the users app.
|
||||||
permissions = ('users.can_see_name',)
|
permissions = ('users.can_see_name',)
|
||||||
|
|
||||||
@ -295,7 +295,7 @@ class GroupUpdate(TestCase):
|
|||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
# This is the builtin group 'Delegates'. The pk is valid.
|
# This is the builtin group 'Delegates'. The pk is valid.
|
||||||
group_pk = 3
|
group_pk = 2
|
||||||
# This contains one valid permission of the users app.
|
# This contains one valid permission of the users app.
|
||||||
permissions = ('users.can_see_name',)
|
permissions = ('users.can_see_name',)
|
||||||
|
|
||||||
@ -324,9 +324,8 @@ class GroupDelete(TestCase):
|
|||||||
def test_delete_builtin_groups(self):
|
def test_delete_builtin_groups(self):
|
||||||
admin_client = APIClient()
|
admin_client = APIClient()
|
||||||
admin_client.login(username='admin', password='admin')
|
admin_client.login(username='admin', password='admin')
|
||||||
# The pks of builtin groups 'Anonymous' and 'Registered'
|
# The pk of builtin group 'Default'
|
||||||
group_pks = (1, 2,)
|
group_pk = 1
|
||||||
|
|
||||||
for group_pk in group_pks:
|
response = admin_client.delete(reverse('group-detail', args=[group_pk]))
|
||||||
response = admin_client.delete(reverse('group-detail', args=[group_pk]))
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
|
||||||
|
@ -290,12 +290,10 @@ class UserManagerGeneratePassword(TestCase):
|
|||||||
class UserManagerCreateOrResetAdminUser(TestCase):
|
class UserManagerCreateOrResetAdminUser(TestCase):
|
||||||
def test_get_admin_group(self, mock_group):
|
def test_get_admin_group(self, mock_group):
|
||||||
"""
|
"""
|
||||||
Tests that the Group with pk=4 is added to the admin.
|
Tests that the Group with pk=3 is added to the admin.
|
||||||
"""
|
"""
|
||||||
def mock_side_effect(pk):
|
def mock_side_effect(pk):
|
||||||
if pk == 2:
|
if pk == 3:
|
||||||
result = 'mock_registered'
|
|
||||||
elif pk == 4:
|
|
||||||
result = 'mock_staff'
|
result = 'mock_staff'
|
||||||
else:
|
else:
|
||||||
result = ''
|
result = ''
|
||||||
@ -308,7 +306,7 @@ class UserManagerCreateOrResetAdminUser(TestCase):
|
|||||||
|
|
||||||
manager.create_or_reset_admin_user()
|
manager.create_or_reset_admin_user()
|
||||||
|
|
||||||
mock_group.objects.get.assert_called_once_with(pk=4)
|
mock_group.objects.get.assert_called_once_with(pk=3)
|
||||||
admin_user.groups.add.assert_called_once_with('mock_staff')
|
admin_user.groups.add.assert_called_once_with('mock_staff')
|
||||||
|
|
||||||
def test_password_set_to_admin(self, mock_group):
|
def test_password_set_to_admin(self, mock_group):
|
||||||
|
Loading…
Reference in New Issue
Block a user