diff --git a/openslides/core/static/js/core.js b/openslides/core/static/js/core.js
index 2f60f596f..6b9b3990b 100644
--- a/openslides/core/static/js/core.js
+++ b/openslides/core/static/js/core.js
@@ -3,8 +3,6 @@ angular.module('OpenSlidesApp.core', [])
.config(function($stateProvider) {
// Use stateProvider.decorator to give default values to our states
$stateProvider.decorator('views', function(state, parent) {
-
-
var result = {},
views = parent(state);
@@ -69,12 +67,9 @@ angular.module('OpenSlidesApp.core', [])
});
})
-.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
+.config(function($stateProvider, $locationProvider) {
// Core urls
- $urlRouterProvider.otherwise('/');
-
- $stateProvider
- .state('dashboard', {
+ $stateProvider.state('dashboard', {
url: '/',
templateUrl: 'static/templates/dashboard.html'
});
@@ -119,16 +114,6 @@ angular.module('OpenSlidesApp.core', [])
});
})
-.run(function($rootScope, i18n) {
- // Puts the gettext methods into each scope.
- // Uses the methods that are known by xgettext by default.
- methods = ['gettext', 'dgettext', 'dcgettext', 'ngettext', 'dngettext',
- 'pgettext', 'dpgettext'];
- _.forEach(methods, function(method) {
- $rootScope[method] = _.bind(i18n[method], i18n);
- });
-})
-
.run(function($rootScope, Config) {
// Puts the config object into each scope.
// TODO: maybe rootscope.config has to set before findAll() is finished
diff --git a/openslides/core/static/templates/index.html b/openslides/core/static/templates/index.html
index 02e161343..10ae6ee69 100644
--- a/openslides/core/static/templates/index.html
+++ b/openslides/core/static/templates/index.html
@@ -22,14 +22,13 @@
{{ config('event_name') }}
- {% block loginbutton %}
-
{% if chat_messages != None %}
@@ -61,7 +61,7 @@
{% trans "Logout" %}
{% else %}
-
+
{% trans "Login" %}
{% endif %}
diff --git a/openslides/core/views.py b/openslides/core/views.py
index 36e7fea7c..1a7b0c4ec 100644
--- a/openslides/core/views.py
+++ b/openslides/core/views.py
@@ -10,6 +10,7 @@ from django.utils.importlib import import_module
from django.utils.translation import ugettext as _
from haystack.views import SearchView as _SearchView
from django.http import HttpResponse
+from django.views.decorators.csrf import ensure_csrf_cookie
from openslides import get_version as get_openslides_version
from openslides import get_git_commit_id, RELEASE
@@ -35,6 +36,14 @@ class IndexView(utils_views.View):
to the custom staticfiles directory. See STATICFILES_DIRS in settings.py.
"""
+ @classmethod
+ def as_view(cls, *args, **kwargs):
+ """
+ Makes sure that the csrf cookie is send.
+ """
+ view = super().as_view(*args, **kwargs)
+ return ensure_csrf_cookie(view)
+
def get(self, *args, **kwargs):
with open(finders.find('templates/index.html')) as f:
content = f.read()
@@ -341,6 +350,6 @@ class TagViewSet(ModelViewSet):
Calls self.permission_denied() if the requesting user has not the
permission to manage tags and it is a create, update or detroy request.
"""
- if (self.action in ('create', 'update', 'destroy')
- and not request.user.has_perm('core.can_manage_tags')):
+ if (self.action in ('create', 'update', 'destroy') and
+ not request.user.has_perm('core.can_manage_tags')):
self.permission_denied(request)
diff --git a/openslides/global_settings.py b/openslides/global_settings.py
index 77c3d1798..beab81772 100644
--- a/openslides/global_settings.py
+++ b/openslides/global_settings.py
@@ -11,7 +11,7 @@ AUTH_USER_MODEL = 'users.User'
AUTHENTICATION_BACKENDS = ('openslides.users.auth.CustomizedModelBackend',)
-LOGIN_URL = '/login/'
+LOGIN_URL = '/users/'
LOGIN_REDIRECT_URL = '/'
SESSION_COOKIE_NAME = 'OpenSlidesSessionID'
diff --git a/openslides/urls.py b/openslides/urls.py
index fc4c4d4b2..b47facbaf 100644
--- a/openslides/urls.py
+++ b/openslides/urls.py
@@ -11,14 +11,15 @@ handler500 = ErrorView.as_view(status_code=500)
urlpatterns = patterns(
'',
url(r'^rest/', include(router.urls)),
- # TODO: add "special" urls, for example pdf views etc.
+ (r'^users/', include('openslides.users.urls')),
- url(r'^user.*', IndexView.as_view()),
+ url(r'^users.*', IndexView.as_view()),
# activate next line go get more angular views
# url(r'^$', IndexView.as_view()),
# url(r'^assignment.*', IndexView.as_view()),
# url(r'^agenda.*', IndexView.as_view()),
+
)
@@ -31,7 +32,6 @@ urlpatterns += patterns(
(r'^agenda/', include('openslides.agenda.urls')),
(r'^motion/', include('openslides.motion.urls')),
(r'^assignment/', include('openslides.assignment.urls')),
- (r'^user/', include('openslides.users.urls')),
(r'^mediafile/', include('openslides.mediafile.urls')),
(r'^config/', include('openslides.config.urls')),
(r'^projector/', include('openslides.projector.urls')),
@@ -44,14 +44,6 @@ urlpatterns += patterns(
'',
(r'^jsi18n/$', 'django.views.i18n.javascript_catalog', js_info_dict),
- url(r'^login/$',
- 'openslides.users.views.login',
- name='user_login'),
-
- url(r'^logout/$',
- 'django.contrib.auth.views.logout_then_login',
- name='user_logout'),
-
url(r'^myusersettings/$',
UserSettingsView.as_view(),
name='user_settings'),
diff --git a/openslides/users/forms.py b/openslides/users/forms.py
index 967f94969..53e0ef106 100644
--- a/openslides/users/forms.py
+++ b/openslides/users/forms.py
@@ -1,147 +1,10 @@
from django import forms
from django.conf import settings
-from django.utils.translation import ugettext as _, ugettext_lazy
+from django.utils.translation import ugettext_lazy
-from openslides.config.api import config
-from openslides.utils.forms import (CssClassMixin,
- LocalizedModelMultipleChoiceField)
+from openslides.utils.forms import CssClassMixin
-from .models import Group, Permission, User
-from .api import get_protected_perm
-
-
-class UserCreateForm(CssClassMixin, forms.ModelForm):
- groups = LocalizedModelMultipleChoiceField(
- # Hide the built-in groups 'Anonymous' (pk=1) and 'Registered' (pk=2)
- queryset=Group.objects.exclude(pk__in=[1, 2]),
- label=ugettext_lazy('Groups'), required=False)
-
- class Meta:
- model = User
- fields = ('title', 'first_name', 'last_name', 'groups',
- 'structure_level', 'about_me', 'comment', 'is_active',
- 'default_password')
-
- def clean(self, *args, **kwargs):
- """
- Ensures that a user has either a first name or a last name.
- """
- cleaned_data = super(UserCreateForm, self).clean(*args, **kwargs)
- if not (cleaned_data['first_name'] or cleaned_data['last_name']):
- error_msg = _('First name and last name can not both be empty.')
- raise forms.ValidationError(error_msg)
- return cleaned_data
-
-
-class UserMultipleCreateForm(forms.Form):
- users_block = forms.CharField(
- widget=forms.Textarea,
- label=ugettext_lazy('Users'),
- help_text=ugettext_lazy('Use one line per user for its name '
- '(first name and last name).'))
-
-
-class UserUpdateForm(UserCreateForm):
- """
- Form to update an user. It raises a validation error, if a non-superuser
- user edits himself and removes the last group containing the permission
- to manage users.
- """
-
- class Meta:
- model = User
- fields = ('username', 'title', 'first_name', 'last_name',
- 'groups', 'structure_level', 'about_me', 'comment',
- 'is_active', 'default_password')
-
- def __init__(self, *args, **kwargs):
- self.request = kwargs.pop('request')
- return super().__init__(*args, **kwargs)
-
- def clean(self, *args, **kwargs):
- """
- Raises a validation error if a non-superuser user edits himself
- and removes the last group containing the permission to manage users.
- """
- # TODO: Check this in clean_groups
- if (self.request.user == self.instance and
- not self.instance.is_superuser and
- not self.cleaned_data['groups'].filter(permissions__in=[get_protected_perm()]).exists()):
- error_msg = _('You can not remove the last group containing the permission to manage users.')
- raise forms.ValidationError(error_msg)
- return super().clean(*args, **kwargs)
-
-
-class GroupForm(CssClassMixin, forms.ModelForm):
- permissions = LocalizedModelMultipleChoiceField(
- queryset=Permission.objects.all(), label=ugettext_lazy('Permissions'), required=False,
- widget=forms.SelectMultiple(attrs={'class': 'dont_use_bsmselect'}))
- users = forms.ModelMultipleChoiceField(
- queryset=User.objects.all(), label=ugettext_lazy('Participants'), required=False,
- widget=forms.SelectMultiple(attrs={'class': 'dont_use_bsmselect'}))
-
- class Meta:
- model = Group
- fields = '__all__'
-
- def __init__(self, *args, **kwargs):
- # Take request argument
- self.request = kwargs.pop('request', None)
- # Initial users
- if kwargs.get('instance', None) is not None:
- initial = kwargs.setdefault('initial', {})
- initial['users'] = kwargs['instance'].user_set.all()
-
- super().__init__(*args, **kwargs)
- if config['users_sort_users_by_first_name']:
- self.fields['users'].queryset = self.fields['users'].queryset.order_by('first_name')
-
- def save(self, commit=True):
- instance = forms.ModelForm.save(self, False)
-
- old_save_m2m = self.save_m2m
-
- def save_m2m():
- old_save_m2m()
-
- instance.user_set.clear()
- for user in self.cleaned_data['users']:
- instance.user_set.add(user)
- self.save_m2m = save_m2m
-
- if commit:
- instance.save()
- self.save_m2m()
-
- return instance
-
- def clean(self, *args, **kwargs):
- """
- Raises a validation error if a non-superuser user removes himself
- from the last group containing the permission to manage users.
-
- Raises also a validation error if a non-superuser removes his last
- permission to manage users from the (last) group.
- """
- # TODO: Check this in clean_users or clean_permissions
- if (self.request and
- not self.request.user.is_superuser and
- self.request.user not in self.cleaned_data['users'] and
- not Group.objects.exclude(pk=self.instance.pk).filter(
- permissions__in=[get_protected_perm()],
- user__pk=self.request.user.pk).exists()):
- error_msg = _('You can not remove yourself from the last group containing the permission to manage users.')
- raise forms.ValidationError(error_msg)
-
- if (self.request and
- not self.request.user.is_superuser and
- not get_protected_perm() in self.cleaned_data['permissions'] and
- not Group.objects.exclude(pk=self.instance.pk).filter(
- permissions__in=[get_protected_perm()],
- user__pk=self.request.user.pk).exists()):
- error_msg = _('You can not remove the permission to manage users from the last group you are in.')
- raise forms.ValidationError(error_msg)
- return super(GroupForm, self).clean(*args, **kwargs)
+from .models import User
class UsersettingsForm(CssClassMixin, forms.ModelForm):
diff --git a/openslides/users/main_menu.py b/openslides/users/main_menu.py
index da4b423ef..8b2477462 100644
--- a/openslides/users/main_menu.py
+++ b/openslides/users/main_menu.py
@@ -10,5 +10,5 @@ class UserMainMenuEntry(MainMenuEntry):
verbose_name = ugettext_lazy('Users')
required_permission = 'users.can_see_extra_data'
default_weight = 50
- pattern_name = 'user_list'
+ pattern_name = 'core_dashboard'
icon_css_class = 'glyphicon-user'
diff --git a/openslides/users/models.py b/openslides/users/models.py
index 265d1548f..cf3635172 100644
--- a/openslides/users/models.py
+++ b/openslides/users/models.py
@@ -3,12 +3,13 @@
from random import choice
from django.contrib.auth.hashers import make_password
-from django.contrib.auth.models import (
+from django.contrib.auth.models import ( # noqa
AbstractBaseUser,
BaseUserManager,
- PermissionsMixin)
-from django.contrib.auth.models import Group, Permission # noqa
-from django.core.urlresolvers import reverse
+ Group,
+ Permission,
+ PermissionsMixin
+)
from django.db import models
from django.utils.translation import ugettext_lazy, ugettext_noop
@@ -158,11 +159,9 @@ class User(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, PermissionsMixin, Abstr
Returns the URL to the user.
"""
if link == 'detail':
- url = reverse('user_detail', args=[str(self.pk)])
+ url = "/users/%s/" % self.pk
elif link == 'update':
- url = reverse('user_update', args=[str(self.pk)])
- elif link == 'delete':
- url = reverse('user_delete', args=[str(self.pk)])
+ url = "/users/%s/edit/" % self.pk
else:
url = super().get_absolute_url(link)
return url
diff --git a/openslides/users/static/js/users/users.js b/openslides/users/static/js/users/users.js
index 6fff8ad87..03cb5a3dd 100644
--- a/openslides/users/static/js/users/users.js
+++ b/openslides/users/static/js/users/users.js
@@ -3,7 +3,7 @@ angular.module('OpenSlidesApp.users', [])
.config(function($stateProvider) {
$stateProvider
.state('users', {
- url: '/user',
+ url: '/users',
abstract: true,
template: "
",
})
@@ -33,7 +33,48 @@ angular.module('OpenSlidesApp.users', [])
});
})
-.factory('User', function(DS) {
+.factory('operator', function(User, Group) {
+ var operator = {
+ user: null,
+ perms: [],
+ isAuthenticated: function() {
+ return !!this.user;
+ },
+ setUser: function(user_id) {
+ if (user_id) {
+ User.find(user_id).then(function(user) {
+ operator.user = user;
+ // TODO: load only the needed groups
+ Group.findAll().then(function() {
+ operator.perms = user.getPerms();
+ });
+ });
+ } else {
+ operator.user = null;
+ Group.find(1).then(function(group) {
+ operator.perms = group.permissions;
+ });
+ }
+ },
+ hasPerms: function(perms) {
+ if (typeof perms == 'string') {
+ perms = perms.split(' ');
+ }
+ return _.intersection(perms, operator.perms).length > 0;
+ },
+ }
+ return operator;
+})
+
+.run(function(operator, $rootScope, $http) {
+ // Put the operator into the root scope
+ $http.get('/users/whoami/').success(function(data) {
+ operator.setUser(data.user_id);
+ });
+ $rootScope.operator = operator;
+})
+
+.factory('User', function(DS, Group) {
return DS.defineResource({
name: 'users/user',
endpoint: '/rest/users/user/',
@@ -52,7 +93,23 @@ angular.module('OpenSlidesApp.users', [])
}
return name;
},
- }
+ getPerms: function() {
+ var allPerms = [];
+ _.forEach(this.groups, function(groupId) {
+ // Get group from server
+ Group.find(groupId);
+ // But do not work with the returned promise, because in
+ // this case this method can not be called in $watch
+ group = Group.get(groupId);
+ if (group) {
+ _.forEach(group.permissions, function(perm) {
+ allPerms.push(perm);
+ });
+ }
+ });
+ return _.uniq(allPerms);
+ },
+ },
});
})
@@ -63,6 +120,73 @@ angular.module('OpenSlidesApp.users', [])
});
})
+/*
+ * Directive to check for permissions
+ *
+ * This is the Code from angular.js ngIf.
+ *
+ * TODO: find a way not to copy the code.
+*/
+.directive('osPerms', ['$animate', function($animate) {
+ return {
+ multiElement: true,
+ transclude: 'element',
+ priority: 600,
+ terminal: true,
+ restrict: 'A',
+ $$tlb: true,
+ link: function($scope, $element, $attr, ctrl, $transclude) {
+ var block, childScope, previousElements, perms;
+ if ($attr.osPerms[0] === '!') {
+ perms = _.trimLeft($attr.osPerms, '!')
+ } else {
+ perms = $attr.osPerms;
+ }
+ $scope.$watch(
+ function (scope) {
+ return scope.operator.hasPerms(perms);
+ },
+ function (value) {
+ if ($attr.osPerms[0] === '!') {
+ value = !value;
+ }
+ if (value) {
+ if (!childScope) {
+ $transclude(function(clone, newScope) {
+ childScope = newScope;
+ clone[clone.length++] = document.createComment(' end ngIf: ' + $attr.ngIf + ' ');
+ // Note: We only need the first/last node of the cloned nodes.
+ // However, we need to keep the reference to the jqlite wrapper as it might be changed later
+ // by a directive with templateUrl when its template arrives.
+ block = {
+ clone: clone
+ };
+ $animate.enter(clone, $element.parent(), $element);
+ });
+ }
+ } else {
+ if (previousElements) {
+ previousElements.remove();
+ previousElements = null;
+ }
+ if (childScope) {
+ childScope.$destroy();
+ childScope = null;
+ }
+ if (block) {
+ previousElements = getBlockNodes(block.clone);
+ $animate.leave(previousElements).then(function() {
+ previousElements = null;
+ });
+ block = null;
+ }
+ }
+ }
+ );
+ }
+ };
+}])
+
.controller('UserListCtrl', function($scope, User, i18n) {
User.bindAll({}, $scope, 'users');
})
@@ -85,4 +209,42 @@ angular.module('OpenSlidesApp.users', [])
User.save(user);
// TODO: redirect to list-view
};
+})
+
+.controller('userMenu', function($scope, $http, DS, User, operator) {
+ $scope.logout = function() {
+ $http.post('/users/logout/').success(function(data) {
+ operator.setUser(null);
+ // TODO: remove all data from cache and reload page
+ // DS.flush();
+ });
+ };
+
+ $scope.login = function(username, password) {
+ $http.post(
+ '/users/login/',
+ {'username': username, 'password': password}
+ ).success(function(data) {
+ operator.setUser(data.user_id);
+ $scope.showLoginForm = false;
+ });
+ };
});
+
+
+// this is code from angular.js. Find a way to call this function from this file
+function getBlockNodes(nodes) {
+ // TODO(perf): just check if all items in `nodes` are siblings and if they are return the original
+ // collection, otherwise update the original collection.
+ var node = nodes[0];
+ var endNode = nodes[nodes.length - 1];
+ var blockNodes = [node];
+
+ do {
+ node = node.nextSibling;
+ if (!node) break;
+ blockNodes.push(node);
+ } while (node !== endNode);
+
+ return $(blockNodes);
+}
diff --git a/openslides/users/static/templates/users/user-list.html b/openslides/users/static/templates/users/user-list.html
index c432ff438..971598229 100644
--- a/openslides/users/static/templates/users/user-list.html
+++ b/openslides/users/static/templates/users/user-list.html
@@ -4,4 +4,7 @@
{{ gettext('Edit') }}
-
{{ gettext('New') }}
+
+
{{ gettext('New') }}
+
No Permission to create
+
PDF
diff --git a/openslides/users/templates/users/group_detail.html b/openslides/users/templates/users/group_detail.html
deleted file mode 100644
index 267af9f66..000000000
--- a/openslides/users/templates/users/group_detail.html
+++ /dev/null
@@ -1,55 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load tags %}
-
-{% block title %}{% trans group.name %} – {{ block.super }}{% endblock %}
-
-{% block content %}
-
-
-
-
{{ group.description }}
-
-
{% trans "Members" %}
-
-
-{% for member in group_members %}
- - {{ member }}
-{% empty %}
- {% trans "No members available." %}
-{% endfor %}
-
-
-{% endblock %}
diff --git a/openslides/users/templates/users/group_form.html b/openslides/users/templates/users/group_form.html
deleted file mode 100644
index 33b8df1d1..000000000
--- a/openslides/users/templates/users/group_form.html
+++ /dev/null
@@ -1,39 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-
-{% block title %}
- {% if group %}
- {% trans "Edit group" %}
- {% else %}
- {% trans "New group" %}
- {% endif %}
- – {{ block.super }}
-{% endblock %}
-
-{% block content %}
-
- {% if group %}
- {% trans "Edit group" %}
- {% else %}
- {% trans "New group" %}
- {% endif %}
-
-
-
- {% trans "Back to overview" %}
-
-
-
-
-
-{% endblock %}
diff --git a/openslides/users/templates/users/group_list.html b/openslides/users/templates/users/group_list.html
deleted file mode 100644
index d1a2f9bf9..000000000
--- a/openslides/users/templates/users/group_list.html
+++ /dev/null
@@ -1,72 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load staticfiles %}
-{% load tags %}
-
-{% block title %}{% trans "Groups" %} – {{ block.super }}{% endblock %}
-
-{% block header %}
-
-{% endblock %}
-
-{% block javascript %}
-
-
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
- {% trans "ID" %} |
- {% trans "Group" %} |
- {% trans "Members" %} |
- {% trans "Actions" %} |
-
-
- {% for group in groups %}
-
- {{ group.pk }}
- {% if group.pk == 1 or group.pk == 2 %}
-
- {% endif %}
- |
-
- {% trans group.name %}
- |
-
- {{ group.user_set.all.count }}
- |
-
-
-
-
-
- {% if group.pk != 1 and group.pk != 2 %}
-
-
-
- {% endif %}
-
- |
-
- {% endfor %}
-
-{% endblock %}
diff --git a/openslides/users/templates/users/login.html b/openslides/users/templates/users/login.html
deleted file mode 100644
index 35184437a..000000000
--- a/openslides/users/templates/users/login.html
+++ /dev/null
@@ -1,59 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load staticfiles %}
-
-{% block title %}{{ block.super }} – {% trans "Login" %} {% endblock %}
-
-{% block loginbutton %}
-{% endblock %}
-
-{% block body %}
-
-
-
- {% if form.errors %}
-
- {% for msg in form.non_field_errors %}
- {{ msg }}
- {% if not forloop.last %}
{% endif %}
- {% empty %}
- {% trans "Your username and password were not accepted. Please try again." %}
- {% endfor %}
-
- {% endif %}
- {% if first_time_message %}
-
- {{ first_time_message|safe }}
-
- {% endif %}
-
-{% endblock %}
diff --git a/openslides/users/templates/users/user_detail.html b/openslides/users/templates/users/user_detail.html
deleted file mode 100644
index 985862df5..000000000
--- a/openslides/users/templates/users/user_detail.html
+++ /dev/null
@@ -1,92 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load tags %}
-
-{% block title %}{{ shown_user }} – {{ block.super }}{% endblock %}
-
-{% block content %}
-
-
- {{ shown_user.clean_name }}
-
-
-
-
- {% trans "Back to overview" %}
-
-
-
- {% if perms.core.can_manage_projector %}
-
-
-
- {% endif %}
- {% if perms.users.can_manage %}
-
- {% endif %}
-
-
-
-
-
-
-
-
-
- {% if perms.users.can_manage %}
-
-{% endblock %}
diff --git a/openslides/users/templates/users/user_form.html b/openslides/users/templates/users/user_form.html
deleted file mode 100644
index 6f649c0ef..000000000
--- a/openslides/users/templates/users/user_form.html
+++ /dev/null
@@ -1,49 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load staticfiles %}
-
-
-{% block title %}
- {% if edit_user %}
- {% trans "Edit user" %}
- {% else %}
- {% trans "New user" %}
- {% endif %}
- – {{ block.super }}
-{% endblock %}
-
-{% block content %}
-
- {% if edit_user %}
- {% trans "Edit user" %}
- {% else %}
- {% trans "New user" %}
- {% endif %}
-
-
-
- {% trans "Back to overview" %}
-
-
-
-
-
-{% endblock %}
diff --git a/openslides/users/templates/users/user_form_multiple.html b/openslides/users/templates/users/user_form_multiple.html
deleted file mode 100644
index 883a8db95..000000000
--- a/openslides/users/templates/users/user_form_multiple.html
+++ /dev/null
@@ -1,28 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-
-{% block title %}
- {% trans 'New multiple users' %} – {{ block.super }}
-{% endblock %}
-
-{% block content %}
-
-
-{% endblock %}
diff --git a/openslides/users/templates/users/user_list.html b/openslides/users/templates/users/user_list.html
deleted file mode 100644
index 850362555..000000000
--- a/openslides/users/templates/users/user_list.html
+++ /dev/null
@@ -1,158 +0,0 @@
-{% extends "base.html" %}
-
-{% load i18n %}
-{% load staticfiles %}
-{% load tags %}
-
-{% block title %}{% trans "Users" %} – {{ block.super }}{% endblock %}
-
-{% block header %}
-
-{% endblock %}
-
-{% block javascript %}
-
-
-{% endblock %}
-
-{% block content %}
-
-
-
-
-
- {% trans "Present" %} |
- {% trans "Title" %} |
- {% trans "Name" %} |
- {% trans "Structure level" %} |
- {% trans "Group" %} |
- {% trans "Committee" %} |
- {% if perms.users.can_manage %}
- {% trans "Comment" %} |
- {% trans "Last Login" %} |
- {% trans "Actions" %} |
- {% endif %}
-
-
-
- {% for user in users %}
-
- {% if perms.users.can_manage %}
- {% if user != request_user %}
-
-
-
- {% endif %}
- {% else %}
-
-
- {% endif %}
- |
- {{ user.title }} |
-
- {% if 'users_sort_users_by_first_name'|get_config %}
- {{ user.first_name }} {{ user.last_name }}
- {% else %}
- {{ user.last_name }}{% if user.last_name and user.first_name %},{% endif %} {{ user.first_name }}
- {% endif %}
- |
- {{ user.structure_level }} |
-
- {% for group in user.groups.all %}
- {% if group.pk != 2 %}
- {% trans group.name %}
- {% if not forloop.last %} {% endif %}
- {% endif %}
- {% endfor %}
- |
- {{ user.committee }} |
- {% if perms.users.can_manage %}
-
- {% if user.comment %}
-
-
-
- {% endif %}
- |
-
- {% if user.last_login > user.date_joined %}
- {{ user.last_login }}
- {% endif %}
- |
-
-
- {% if perms.core.can_manage_projector %}
-
-
-
- {% endif %}
-
-
-
- {% if user != request_user %}
-
-
-
- {% endif %}
-
- |
- {% endif %}
-
- {% endfor %}
-
-
-{% endblock %}
diff --git a/openslides/users/urls.py b/openslides/users/urls.py
index 2be58e29a..9729d0248 100644
--- a/openslides/users/urls.py
+++ b/openslides/users/urls.py
@@ -4,75 +4,10 @@ from . import views
urlpatterns = patterns(
'',
- # User
- url(r'^$',
- views.UserListView.as_view(),
- name='user_list'),
-
- url(r'^new/$',
- views.UserCreateView.as_view(),
- name='user_create'),
-
- url(r'^new_multiple/$',
- views.UserMultipleCreateView.as_view(),
- name='user_create_multiple'),
-
- url(r'^(?P
\d+)/$',
- views.UserDetailView.as_view(),
- name='user_detail'),
-
- url(r'^(?P\d+)/edit/$',
- views.UserUpdateView.as_view(),
- name='user_update'),
-
- url(r'^(?P\d+)/del/$',
- views.UserDeleteView.as_view(),
- name='user_delete'),
-
- url(r'^(?P\d+)/reset_password/$',
- views.ResetPasswordView.as_view(),
- name='user_reset_password'),
-
- url(r'^(?P\d+)/status/activate/$',
- views.SetUserStatusView.as_view(),
- {'action': 'activate'},
- name='user_status_activate'),
-
- url(r'^(?P\d+)/status/deactivate/$',
- views.SetUserStatusView.as_view(),
- {'action': 'deactivate'},
- name='user_status_deactivate'),
-
- url(r'^(?P\d+)/status/toggle/$',
- views.SetUserStatusView.as_view(),
- {'action': 'toggle'},
- name='user_status_toggle'),
-
url(r'^csv_import/$',
views.UserCSVImportView.as_view(),
name='user_csv_import'),
- # Group
- url(r'^group/$',
- views.GroupListView.as_view(),
- name='group_list'),
-
- url(r'^group/new/$',
- views.GroupCreateView.as_view(),
- name='group_create'),
-
- url(r'^group/(?P\d+)/$',
- views.GroupDetailView.as_view(),
- name='group_detail'),
-
- url(r'^group/(?P\d+)/edit/$',
- views.GroupUpdateView.as_view(),
- name='group_update'),
-
- url(r'^group/(?P\d+)/del/$',
- views.GroupDeleteView.as_view(),
- name='group_delete'),
-
# PDF
url(r'^print/$',
views.UsersListPDF.as_view(),
@@ -81,4 +16,17 @@ urlpatterns = patterns(
url(r'^passwords/print/$',
views.UsersPasswordsPDF.as_view(),
name='print_passwords'),
+
+ # auth
+ url(r'^login/$',
+ views.UserLoginView.as_view(),
+ name='user_login'),
+
+ url(r'^logout/$',
+ views.UserLogoutView.as_view(),
+ name='user_logout'),
+
+ url(r'^whoami/$',
+ views.WhoAmIView.as_view(),
+ name='user_whoami'),
)
diff --git a/openslides/users/views.py b/openslides/users/views.py
index e9d82fd01..a7504e11f 100644
--- a/openslides/users/views.py
+++ b/openslides/users/views.py
@@ -1,179 +1,30 @@
from django.contrib import messages
-from django.contrib.auth.forms import PasswordChangeForm
-from django.contrib.auth.hashers import make_password
-from django.contrib.auth.views import login as django_login
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext as _, ugettext_lazy, activate
+from django.contrib.auth import login as auth_login
+from django.contrib.auth import logout as auth_logout
+from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm
+from django.utils.translation import ugettext as _
+from django.utils.translation import activate, ugettext_lazy
-from openslides.config.api import config
from openslides.utils.rest_api import ModelViewSet
-from openslides.utils.utils import delete_default_permissions, html_strong
from openslides.utils.views import (
- CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
- PDFView, PermissionMixin, QuestionView, RedirectView, SingleObjectMixin,
- UpdateView, LoginMixin)
-from openslides.utils.exceptions import OpenSlidesError
+ CSVImportView,
+ FormView,
+ LoginMixin,
+ PDFView,
+ UpdateView,
+ APIView
+)
-from .api import get_protected_perm
from .csv_import import import_users
-from .forms import (GroupForm, UserCreateForm, UserMultipleCreateForm,
- UsersettingsForm, UserUpdateForm)
+from .forms import UsersettingsForm
from .models import Group, User
-from .pdf import users_to_pdf, users_passwords_to_pdf
-from .serializers import GroupSerializer, UserCreateUpdateSerializer, UserFullSerializer, UserShortSerializer
-
-
-class UserListView(ListView):
- """
- Show all users.
- """
- required_permission = 'users.can_see_extra_data'
- context_object_name = 'users'
-
- def get_queryset(self):
- query = User.objects
- if config['users_sort_users_by_first_name']:
- query = query.order_by('first_name')
- else:
- query = query.order_by('last_name')
- return query.all()
-
- def get_context_data(self, **kwargs):
- context = super().get_context_data(**kwargs)
- all_users = User.objects.count()
- # context vars
- context.update({
- 'allusers': all_users,
- 'request_user': self.request.user})
- return context
-
-
-class UserDetailView(DetailView, PermissionMixin):
- """
- Classed based view to show a specific user in the interface.
- """
- required_permission = 'users.can_see_extra_data'
- model = User
- context_object_name = 'shown_user'
-
-
-class UserCreateView(CreateView):
- """
- Create a new user.
- """
- required_permission = 'users.can_manage'
- model = User
- context_object_name = 'edit_user'
- form_class = UserCreateForm
- success_url_name = 'user_list'
- url_name_args = []
-
- def manipulate_object(self, form):
- self.object.username = User.objects.generate_username(
- form.cleaned_data['first_name'], form.cleaned_data['last_name'])
-
- if not self.object.default_password:
- self.object.default_password = User.objects.generate_password()
-
- self.object.set_password(self.object.default_password)
-
-
-class UserMultipleCreateView(FormView):
- """
- View to create multiple users at once using a big text field.
-
- Sets the password with md5. It is the same password as in the
- default_password field in cleartext. A stronger password hasher is used,
- when the password is changed by the user.
- """
- required_permission = 'users.can_manage'
- template_name = 'users/user_form_multiple.html'
- form_class = UserMultipleCreateForm
- success_url_name = 'user_list'
-
- def form_valid(self, form):
- # TODO: Use bulk_create
- for number, line in enumerate(form.cleaned_data['users_block'].splitlines()):
- names_list = line.split()
- first_name = ' '.join(names_list[:-1])
- last_name = names_list[-1]
- username = User.objects.generate_username(first_name, last_name)
- default_password = User.objects.generate_password()
- User.objects.create(
- username=username,
- first_name=first_name,
- last_name=last_name,
- default_password=default_password,
- password=make_password(default_password, '', 'md5'))
- messages.success(self.request, _('%(number)d users successfully created.') % {'number': number + 1})
- return super(UserMultipleCreateView, self).form_valid(form)
-
-
-class UserUpdateView(UpdateView):
- """
- Update an existing users.
- """
- required_permission = 'users.can_manage'
- model = User
- context_object_name = 'edit_user'
- form_class = UserUpdateForm
- success_url_name = 'user_list'
- url_name_args = []
-
- def get_form_kwargs(self, *args, **kwargs):
- form_kwargs = super(UserUpdateView, self).get_form_kwargs(*args, **kwargs)
- form_kwargs.update({'request': self.request})
- return form_kwargs
-
-
-class UserDeleteView(DeleteView):
- """
- Delete a user.
- """
- required_permission = 'users.can_manage'
- model = User
- success_url_name = 'user_list'
- url_name_args = []
-
- def pre_redirect(self, request, *args, **kwargs):
- if self.get_object() == self.request.user:
- messages.error(request, _("You can not delete yourself."))
- else:
- super().pre_redirect(request, *args, **kwargs)
-
- def pre_post_redirect(self, request, *args, **kwargs):
- if self.get_object() == self.request.user:
- messages.error(self.request, _("You can not delete yourself."))
- else:
- super().pre_post_redirect(request, *args, **kwargs)
-
-
-class SetUserStatusView(SingleObjectMixin, RedirectView):
- """
- Activate or deactivate an user.
- """
- required_permission = 'users.can_manage'
- allow_ajax = True
- url_name = 'user_list'
- url_name_args = []
- model = User
-
- def pre_redirect(self, request, *args, **kwargs):
- action = kwargs['action']
- if action == 'activate':
- self.get_object().is_active = True
- elif action == 'deactivate':
- if self.get_object().user == self.request.user:
- messages.error(request, _("You can not deactivate yourself."))
- else:
- self.get_object().is_active = False
- self.get_object().save()
- return super(SetUserStatusView, self).pre_redirect(request, *args, **kwargs)
-
- def get_ajax_context(self, **kwargs):
- context = super(SetUserStatusView, self).get_ajax_context(**kwargs)
- context['active'] = self.get_object().is_active
- return context
+from .pdf import users_passwords_to_pdf, users_to_pdf
+from .serializers import (
+ GroupSerializer,
+ UserCreateUpdateSerializer,
+ UserFullSerializer,
+ UserShortSerializer
+)
class UsersListPDF(PDFView):
@@ -213,30 +64,10 @@ class UserCSVImportView(CSVImportView):
"""
Import users via CSV.
"""
- import_function = staticmethod(import_users)
required_permission = 'users.can_manage'
success_url_name = 'user_list'
template_name = 'users/user_form_csv_import.html'
-
-
-class ResetPasswordView(SingleObjectMixin, QuestionView):
- """
- Set the Passwort for a user to his default password.
- """
- required_permission = 'users.can_manage'
- model = User
- allow_ajax = True
- question_message = ugettext_lazy('Do you really want to reset the password?')
-
- def get_redirect_url(self, **kwargs):
- return self.get_object().get_absolute_url('update')
-
- def on_clicked_yes(self):
- self.get_object().reset_password()
- self.get_object().save()
-
- def get_final_message(self):
- return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
+ import_function = staticmethod(import_users)
class UserViewSet(ModelViewSet):
@@ -291,164 +122,13 @@ class GroupViewSet(ModelViewSet):
self.permission_denied(request)
-class GroupListView(ListView):
- """
- Overview over all groups.
- """
- required_permission = 'users.can_manage'
- template_name = 'users/group_list.html'
- context_object_name = 'groups'
- model = Group
-
-
-class GroupDetailView(DetailView, PermissionMixin):
- """
- Classed based view to show a specific group in the interface.
- """
- required_permission = 'users.can_manage'
- model = Group
- template_name = 'users/group_detail.html'
- context_object_name = 'group'
-
- def get_context_data(self, *args, **kwargs):
- context = super(GroupDetailView, self).get_context_data(*args, **kwargs)
- query = User.objects
- if config['users_sort_users_by_first_name']:
- query = query.order_by('first_name')
- else:
- query = query.order_by('last_name')
- context['group_members'] = query.filter(groups__in=[context['group']])
- return context
-
-
-class GroupCreateView(CreateView):
- """
- Create a new group.
- """
- required_permission = 'users.can_manage'
- template_name = 'users/group_form.html'
- context_object_name = 'group'
- model = Group
- form_class = GroupForm
- success_url_name = 'group_list'
- url_name_args = []
-
- def get(self, request, *args, **kwargs):
- delete_default_permissions()
- return super(GroupCreateView, self).get(request, *args, **kwargs)
-
- def get_apply_url(self):
- """
- Returns the url when the user clicks on 'apply'.
- """
- return self.get_url('group_update', args=[self.object.pk])
-
-
-class GroupUpdateView(UpdateView):
- """
- Update an existing group.
- """
- required_permission = 'users.can_manage'
- template_name = 'users/group_form.html'
- model = Group
- context_object_name = 'group'
- form_class = GroupForm
- url_name_args = []
- success_url_name = 'group_list'
-
- def get(self, request, *args, **kwargs):
- delete_default_permissions()
- return super().get(request, *args, **kwargs)
-
- def get_form_kwargs(self, *args, **kwargs):
- form_kwargs = super().get_form_kwargs(*args, **kwargs)
- form_kwargs.update({'request': self.request})
- return form_kwargs
-
- def get_apply_url(self):
- """
- Returns the url when the user clicks on 'apply'.
- """
- return self.get_url('group_update', args=[self.object.pk])
-
-
-class GroupDeleteView(DeleteView):
- """
- Delete a group.
- """
- required_permission = 'users.can_manage'
- model = Group
- success_url_name = 'group_list'
- question_url_name = 'group_detail'
- url_name_args = []
-
- def pre_redirect(self, request, *args, **kwargs):
- if not self.is_protected_from_deleting():
- super().pre_redirect(request, *args, **kwargs)
-
- def pre_post_redirect(self, request, *args, **kwargs):
- if not self.is_protected_from_deleting():
- super().pre_post_redirect(request, *args, **kwargs)
-
- def is_protected_from_deleting(self):
- """
- Checks whether the group is protected.
- """
- if self.get_object().pk in [1, 2]:
- messages.error(self.request, _('You can not delete this group.'))
- return True
- if (not self.request.user.is_superuser and
- get_protected_perm() in self.get_object().permissions.all() and
- not Group.objects.exclude(pk=self.get_object().pk).filter(
- permissions__in=[get_protected_perm()],
- user__pk=self.request.user.pk).exists()):
- messages.error(
- self.request,
- _('You can not delete the last group containing the permission '
- 'to manage users you are in.'))
- return True
- return False
-
- def get_url_name_args(self):
- try:
- answer = self.get_answer()
- except OpenSlidesError:
- answer = 'no'
-
- if self.request.method == 'POST' and answer != 'no':
- return []
- else:
- return [self.object.pk]
-
-
-def login(request):
- extra_content = {}
- try:
- admin = User.objects.get(pk=1)
- if admin.check_password(admin.default_password):
- user_data = {
- 'user': html_strong(admin.username),
- 'password': html_strong(admin.default_password)}
-
- extra_content['first_time_message'] = _(
- "Installation was successfully! Use %(user)s "
- "(password: %(password)s) for first login.
"
- "Important: Please change the password after "
- "first login! Otherwise this message still appears for "
- "everyone and could be a security risk.") % user_data
-
- extra_content['next'] = reverse('password_change')
- except User.DoesNotExist:
- pass
- return django_login(request, template_name='users/login.html', extra_context=extra_content)
-
-
class UserSettingsView(LoginMixin, UpdateView):
+ required_permission = None
+ template_name = 'users/settings.html'
+ success_url_name = 'user_settings'
model = User
form_class = UsersettingsForm
- success_url_name = 'user_settings'
url_name_args = []
- template_name = 'users/settings.html'
def get_initial(self):
initial = super().get_initial()
@@ -465,9 +145,10 @@ class UserSettingsView(LoginMixin, UpdateView):
class UserPasswordSettingsView(LoginMixin, FormView):
- form_class = PasswordChangeForm
- success_url_name = 'core_dashboard'
+ required_permission = None
template_name = 'users/password_change.html'
+ success_url_name = 'core_dashboard'
+ form_class = PasswordChangeForm
def form_valid(self, form):
form.save()
@@ -478,3 +159,54 @@ class UserPasswordSettingsView(LoginMixin, FormView):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
+
+
+class UserLoginView(APIView):
+ """
+ Login the user via ajax.
+ """
+ http_method_names = ['post']
+
+ def post(self, *args, **kwargs):
+ form = AuthenticationForm(self.request, data=self.request.data)
+ if form.is_valid():
+ self.user = form.get_user()
+ auth_login(self.request, self.user)
+ self.success = True
+ else:
+ self.success = False
+ return super().post(*args, **kwargs)
+
+ def get_context_data(self, **context):
+ context['success'] = self.success
+ if self.success:
+ context['user_id'] = self.user.pk
+ return super().get_context_data(**context)
+
+
+class UserLogoutView(APIView):
+ """
+ Logout the user via ajax.
+ """
+ http_method_names = ['post']
+
+ def post(self, *args, **kwargs):
+ auth_logout(self.request)
+ return super().post(*args, **kwargs)
+
+
+class WhoAmIView(APIView):
+ """
+ Returns the user id in the session.
+ """
+ http_method_names = ['get']
+
+ def get_context_data(self, **context):
+ """
+ Appends the user id into the context.
+
+ Uses None for the anonymous user.
+ """
+ return super().get_context_data(
+ user_id=self.request.user.pk,
+ **context)
diff --git a/openslides/users/widgets.py b/openslides/users/widgets.py
index 73b73bacc..4965bebe9 100644
--- a/openslides/users/widgets.py
+++ b/openslides/users/widgets.py
@@ -17,7 +17,6 @@ class UserWidget(Widget):
default_weight = 60
default_active = False
template_name = 'users/widget_user.html'
- more_link_pattern_name = 'user_list'
def get_context_data(self, **context):
return super(UserWidget, self).get_context_data(
diff --git a/openslides/utils/views.py b/openslides/utils/views.py
index 914344114..35e78601e 100644
--- a/openslides/utils/views.py
+++ b/openslides/utils/views.py
@@ -13,6 +13,8 @@ from django.utils.translation import ugettext as _, ugettext_lazy
from django.views import generic as django_views
from reportlab.lib.units import cm
from reportlab.platypus import SimpleDocTemplate, Spacer
+from rest_framework.views import APIView as _APIView
+from rest_framework.response import Response
from .exceptions import OpenSlidesError
from .forms import CSVImportForm
@@ -76,13 +78,19 @@ class AjaxMixin(object):
Mixin to response to an ajax request with an json object.
"""
- def get_ajax_context(self, **kwargs):
+ def get_ajax_context(self, **context):
"""
Returns a dictonary with the context for the ajax response.
"""
- return kwargs
+ return context
def ajax_get(self, request, *args, **kwargs):
+ """
+ Deprecated. Use ajax_response instead.
+ """
+ return self.ajax_response()
+
+ def ajax_response(self):
"""
Returns the HttpResponse.
"""
@@ -289,13 +297,15 @@ class ListView(PermissionMixin, ExtraContextMixin, django_views.ListView):
class AjaxView(PermissionMixin, AjaxMixin, View):
"""
View for ajax requests.
+
+ Deprecated. Use APIView instead.
"""
def get(self, request, *args, **kwargs):
# TODO: Raise an error, if the request is not an ajax-request
- return self.ajax_get(request, *args, **kwargs)
+ return self.ajax_response()
def post(self, *args, **kwargs):
- return self.get(*args, **kwargs)
+ return self.ajax_response()
class RedirectView(PermissionMixin, AjaxMixin, UrlMixin, django_views.RedirectView):
@@ -640,3 +650,33 @@ class CSVImportView(FormView):
messages.warning(self.request, warning)
messages.error(self.request, error)
return super(CSVImportView, self).form_valid(form)
+
+
+class APIView(_APIView):
+ """
+ The Django Rest framework APIView with improvements for OpenSlides.
+ """
+
+ http_method_names = []
+ """
+ The allowed actions have to be explicitly defined.
+
+ Django allowes the following:
+ http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
+ """
+
+ def get_context_data(self, **context):
+ """
+ Returns the context for the response.
+ """
+ return context
+
+ def method_call(self, request, *args, **kwargs):
+ """
+ Http method that returns the response object with the context data.
+ """
+ return Response(self.get_context_data())
+
+ # Add the http-methods and delete the method "method_call"
+ get = post = put = patch = delete = head = options = trace = method_call
+ del method_call
diff --git a/tests/integration/users/test_views.py b/tests/integration/users/test_views.py
new file mode 100644
index 000000000..08659ca6a
--- /dev/null
+++ b/tests/integration/users/test_views.py
@@ -0,0 +1,89 @@
+import json
+
+from rest_framework.test import APIClient
+
+from openslides.utils.test import TestCase
+
+
+class TestWhoAmIView(TestCase):
+ url = '/users/whoami/'
+
+ def test_get_anonymous(self):
+ response = self.client.get(self.url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, b'{"user_id":null}')
+
+ def test_get_authenticated_user(self):
+ self.client.login(username='admin', password='admin')
+
+ response = self.client.get(self.url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, b'{"user_id":1}')
+
+ def test_post(self):
+ response = self.client.post(self.url)
+
+ self.assertEqual(response.status_code, 405)
+
+
+class TestUserLogoutView(TestCase):
+ url = '/users/logout/'
+
+ def test_get(self):
+ response = self.client.get(self.url)
+
+ self.assertEqual(response.status_code, 405)
+
+ def test_post_anonymous(self):
+ response = self.client.post(self.url)
+
+ self.assertEqual(response.status_code, 200)
+
+ def test_post_authenticated_user(self):
+ self.client.login(username='admin', password='admin')
+ self.client.session['test_key'] = 'test_value'
+
+ response = self.client.post(self.url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertFalse(hasattr(self.client.session, 'test_key'))
+
+
+class TestUserLoginView(TestCase):
+ url = '/users/login/'
+
+ def setUp(self):
+ self.client = APIClient()
+
+ def test_get(self):
+ response = self.client.get(self.url)
+
+ self.assertEqual(response.status_code, 405)
+
+ def test_post_no_data(self):
+ response = self.client.post(self.url)
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(response.content, b'{"success":false}')
+
+ def test_post_correct_data(self):
+ response = self.client.post(
+ self.url,
+ {'username': 'admin', 'password': 'admin'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ json.loads(response.content.decode('utf-8')),
+ {'success': True, 'user_id': 1})
+
+ def test_post_incorrect_data(self):
+ response = self.client.post(
+ self.url,
+ {'username': 'wrong', 'password': 'wrong'})
+
+ self.assertEqual(response.status_code, 200)
+ self.assertEqual(
+ json.loads(response.content.decode('utf-8')),
+ {'success': False})
diff --git a/tests/old/config/test_config.py b/tests/old/config/test_config.py
index 42576742c..9c2c41edf 100644
--- a/tests/old/config/test_config.py
+++ b/tests/old/config/test_config.py
@@ -120,10 +120,6 @@ class ConfigFormTest(TestCase):
response = self.client_manager.get('/config/')
self.assertRedirects(response=response, expected_url='/config/general/',
status_code=302, target_status_code=200)
- bad_client = Client()
- response = bad_client.get('/config/', follow=True)
- self.assertRedirects(response=response, expected_url='/login/?next=/config/general/',
- status_code=302, target_status_code=200)
def test_get_config_form_testgroupedpage1_manager_client(self):
response = self.client_manager.get('/config/testgroupedpage1/')
@@ -141,10 +137,6 @@ class ConfigFormTest(TestCase):
def test_get_config_form_testgroupedpage1_other_clients(self):
response = self.client_normal_user.get('/config/testgroupedpage1/')
self.assertEqual(response.status_code, 403)
- bad_client = Client()
- response = bad_client.get('/config/testgroupedpage1/')
- self.assertRedirects(response=response, expected_url='/login/?next=/config/testgroupedpage1/',
- status_code=302, target_status_code=200)
def test_get_config_form_testsimplepage1_manager_client(self):
response = self.client_manager.get('/config/testsimplepage1/')
diff --git a/tests/old/mediafile/tests.py b/tests/old/mediafile/tests.py
index ec028d96d..f2782b046 100644
--- a/tests/old/mediafile/tests.py
+++ b/tests/old/mediafile/tests.py
@@ -64,9 +64,6 @@ class MediafileTest(TestCase):
response = client.get('/mediafile/')
self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, 'mediafile/mediafile_list.html')
- bad_client = Client()
- response = bad_client.get('/mediafile/')
- self.assertRedirects(response, expected_url='/login/?next=/mediafile/', status_code=302, target_status_code=200)
def test_upload_mediafile_get_request(self):
clients = self.login_clients()
@@ -82,10 +79,6 @@ class MediafileTest(TestCase):
response = clients['client_normal_user'].get('/mediafile/new/')
self.assertEqual(response.status_code, 403)
- bad_client = Client()
- response = bad_client.get('/mediafile/new/')
- self.assertRedirects(response, expected_url='/login/?next=/mediafile/new/', status_code=302, target_status_code=200)
-
def test_upload_mediafile_post_request(self):
# Test first user
client_1 = self.login_clients()['client_manager']
@@ -139,10 +132,6 @@ class MediafileTest(TestCase):
response = clients['client_normal_user'].get('/mediafile/1/edit/')
self.assertEqual(response.status_code, 403)
- bad_client = Client()
- response = bad_client.get('/mediafile/1/edit/')
- self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/edit/', status_code=302, target_status_code=200)
-
def test_edit_mediafile_get_request_own_file(self):
clients = self.login_clients()
self.object.uploader = self.vip_user
@@ -204,9 +193,6 @@ class MediafileTest(TestCase):
self.assertEqual(response.status_code, 403)
response = clients['client_normal_user'].get('/mediafile/1/del/')
self.assertEqual(response.status_code, 403)
- bad_client = Client()
- response = bad_client.get('/mediafile/1/del/')
- self.assertRedirects(response, expected_url='/login/?next=/mediafile/1/del/', status_code=302, target_status_code=200)
def test_delete_mediafile_get_request_own_file(self):
self.object.uploader = self.vip_user
diff --git a/tests/unit/users/test_models.py b/tests/unit/users/test_models.py
index bae7e12af..ff15a0f95 100644
--- a/tests/unit/users/test_models.py
+++ b/tests/unit/users/test_models.py
@@ -41,15 +41,9 @@ class UserGetAbsoluteUrlTest(TestCase):
"""
user = User(pk=5)
- with patch('openslides.users.models.reverse') as mock_reverse:
- mock_reverse.return_value = 'test url'
- url = user.get_absolute_url()
-
self.assertEqual(
- url,
- 'test url',
- "User.get_absolute_url() does not return the result of reverse.")
- mock_reverse.assert_called_once_with('user_detail', args=['5'])
+ user.get_absolute_url(),
+ '/users/5/')
def test_get_absolute_url_detail(self):
"""
@@ -57,15 +51,11 @@ class UserGetAbsoluteUrlTest(TestCase):
"""
user = User(pk=5)
- with patch('openslides.users.models.reverse') as mock_reverse:
- mock_reverse.return_value = 'test url'
- url = user.get_absolute_url('detail')
+ url = user.get_absolute_url('detail')
self.assertEqual(
url,
- 'test url',
- "User.get_absolute_url('detail') does not return the result of reverse.")
- mock_reverse.assert_called_once_with('user_detail', args=['5'])
+ '/users/5/')
def test_get_absolute_url_update(self):
"""
@@ -73,31 +63,11 @@ class UserGetAbsoluteUrlTest(TestCase):
"""
user = User(pk=5)
- with patch('openslides.users.models.reverse') as mock_reverse:
- mock_reverse.return_value = 'test url'
- url = user.get_absolute_url('update')
+ url = user.get_absolute_url('update')
self.assertEqual(
url,
- 'test url',
- "User.get_absolute_url('update') does not return the result of reverse.")
- mock_reverse.assert_called_once_with('user_update', args=['5'])
-
- def test_get_absolute_url_delete(self):
- """
- Tests get_absolute_url() with 'delete' as argument.
- """
- user = User(pk=5)
-
- with patch('openslides.users.models.reverse') as mock_reverse:
- mock_reverse.return_value = 'test url'
- url = user.get_absolute_url('delete')
-
- self.assertEqual(
- url,
- 'test url',
- "User.get_absolute_url('delete') does not return the result of reverse.")
- mock_reverse.assert_called_once_with('user_delete', args=['5'])
+ '/users/5/edit/')
def test_get_absolute_url_other(self):
"""
@@ -112,8 +82,7 @@ class UserGetAbsoluteUrlTest(TestCase):
self.assertEqual(
url,
- 'test url',
- "User.get_absolute_url(OTHER) does not return the result of reverse.")
+ 'test url')
mock_super().get_absolute_url.assert_called_once_with(dummy_argument)
diff --git a/tests/unit/utils/test_views.py b/tests/unit/utils/test_views.py
index 434bcf844..4be914be4 100644
--- a/tests/unit/utils/test_views.py
+++ b/tests/unit/utils/test_views.py
@@ -316,3 +316,18 @@ class SingleObjectMixinTest(TestCase):
self.assertTrue(
view.get_object.called,
"view.get_object() should be called")
+
+
+class TestAPIView(TestCase):
+ def test_class_creation(self):
+ """
+ Tests that the APIView has all relevant methods
+ """
+ http_methods = set(('get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'))
+
+ self.assertTrue(
+ http_methods.issubset(views.APIView.__dict__),
+ "All http methods should be defined in the APIView")
+ self.assertFalse(
+ hasattr(views.APIView, 'method_call'),
+ "The APIView should not have the method 'method_call'")