From 80ca3f24843b4fe32363e582a30a263d216b5279 Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Sat, 14 Feb 2015 10:10:08 +0100 Subject: [PATCH] Use ui-router to handle django urls See: #1453 --- openslides/core/static/js/core.js | 33 +++++- openslides/core/urls.py | 5 + openslides/core/views.py | 31 ++++- openslides/users/static/js/users/users.js | 138 +++++++++++----------- openslides/users/views.py | 102 ++++++++-------- tests/unit/core/__init__.py | 0 tests/unit/core/test_views.py | 21 ++++ 7 files changed, 206 insertions(+), 124 deletions(-) create mode 100644 tests/unit/core/__init__.py create mode 100644 tests/unit/core/test_views.py diff --git a/openslides/core/static/js/core.js b/openslides/core/static/js/core.js index 58d5d49db..6d132be6e 100644 --- a/openslides/core/static/js/core.js +++ b/openslides/core/static/js/core.js @@ -9,11 +9,12 @@ angular.module('OpenSlidesApp.core', []) var result = {}, views = parent(state); - if (state.abstract) { + if (state.abstract || state.data && state.data.extern) { return views; } angular.forEach(views, function(config, name) { + // Sets default values for templateUrl var patterns = state.name.split('.'), templateUrl, @@ -75,9 +76,9 @@ angular.module('OpenSlidesApp.core', []) .config(function($stateProvider, $locationProvider) { // Core urls $stateProvider.state('dashboard', { - url: '/', - templateUrl: 'static/templates/dashboard.html' - }); + url: '/', + templateUrl: 'static/templates/dashboard.html' + }); $locationProvider.html5Mode(true); }) @@ -98,6 +99,30 @@ angular.module('OpenSlidesApp.core', []) }; }) +.provider('runtimeStates', function($stateProvider) { + this.$get = function($q, $timeout, $state) { + return { + addState: function(name, state) { + $stateProvider.state(name, state); + } + } + } +}) + +.run(function(runtimeStates, $http) { + $http.get('/core/url_patterns/').then(function(data) { + for (var pattern in data.data) { + runtimeStates.addState(pattern, { + 'url': data.data[pattern], + data: {extern: true}, + onEnter: function($window) { + $window.location.href = this.url; + } + }); + } + }); +}) + .run(function(DS, autoupdate) { autoupdate.on_message(function(data) { // TODO: when MODEL.find() is called after this diff --git a/openslides/core/urls.py b/openslides/core/urls.py index acddec2d7..9936f08d7 100644 --- a/openslides/core/urls.py +++ b/openslides/core/urls.py @@ -43,4 +43,9 @@ urlpatterns = patterns( url(r'tags/$', views.TagListView.as_view(), name='core_tag_list'), + + # Ajax Urls + url(r'^core/url_patterns/$', + views.UrlPatternsView.as_view(), + name='core_url_patterns'), ) diff --git a/openslides/core/views.py b/openslides/core/views.py index 53e1b589e..e9175476a 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -1,27 +1,33 @@ +import re + from django.conf import settings from django.contrib import messages from django.contrib.staticfiles import finders from django.core.exceptions import PermissionDenied -from django.core.urlresolvers import reverse +from django.core.urlresolvers import get_resolver, reverse from django.db import IntegrityError +from django.http import HttpResponse from django.shortcuts import redirect, render_to_response from django.template import RequestContext 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 openslides import __version__ as openslides_version from openslides.config.api import config from openslides.utils import views as utils_views -from openslides.utils.plugins import get_plugin_description, get_plugin_verbose_name, get_plugin_version +from openslides.utils.plugins import ( + get_plugin_description, + get_plugin_verbose_name, + get_plugin_version, +) from openslides.utils.rest_api import ModelViewSet from openslides.utils.signals import template_manipulation from openslides.utils.widgets import Widget +from .exceptions import TagException from .forms import SelectWidgetsForm from .models import CustomSlide, Tag -from .exceptions import TagException from .serializers import CustomSlideSerializer, TagSerializer @@ -338,3 +344,20 @@ class TagViewSet(ModelViewSet): if (self.action in ('create', 'update', 'destroy') and not request.user.has_perm('core.can_manage_tags')): self.permission_denied(request) + + +class UrlPatternsView(utils_views.APIView): + """ + Returns a dictonary with all url patterns as json. + """ + URL_KWARGS_REGEX = re.compile(r'%\((\w*)\)s') + http_method_names = ['get'] + + def get_context_data(self, **context): + result = {} + url_dict = get_resolver(None).reverse_dict + for pattern_name in filter(lambda key: isinstance(key, str), url_dict.keys()): + url = url_dict[pattern_name][0][0][0] + result[pattern_name] = self.URL_KWARGS_REGEX.sub(r':\1', url) + + return result diff --git a/openslides/users/static/js/users/users.js b/openslides/users/static/js/users/users.js index 37d0bd77d..bd3e3e2d6 100644 --- a/openslides/users/static/js/users/users.js +++ b/openslides/users/static/js/users/users.js @@ -2,82 +2,82 @@ angular.module('OpenSlidesApp.users', []) .config(function($stateProvider) { $stateProvider - .state('users', { - url: '/users', - abstract: true, - template: "", - }) - .state('users.user', { - abstract: true, - template: "", - }) - .state('users.user.list', { - resolve: { - users: function(User) { - return User.findAll(); - } + .state('users', { + url: '/users', + abstract: true, + template: "", + }) + .state('users.user', { + abstract: true, + template: "", + }) + .state('users.user.list', { + resolve: { + users: function(User) { + return User.findAll(); } - }) - .state('users.user.create', { - resolve: { - groups: function(Group) { - return Group.findAll(); - } + } + }) + .state('users.user.create', { + resolve: { + groups: function(Group) { + return Group.findAll(); } - }) - .state('users.user.detail', { - resolve: { - user: function(User, $stateParams) { - return User.find($stateParams.id); - } + } + }) + .state('users.user.detail', { + resolve: { + user: function(User, $stateParams) { + return User.find($stateParams.id); } - }) - .state('users.user.detail.update', { - views: { - '@users.user': {} - }, - resolve: { - groups: function(Group) { - return Group.findAll(); - } + } + }) + .state('users.user.detail.update', { + views: { + '@users.user': {} + }, + resolve: { + groups: function(Group) { + return Group.findAll(); } - }) - .state('users.user.csv-import', { - url: '/csv-import', - controller: 'UserCSVImportCtrl', - }) - // groups - .state('users.group', { - url: '/groups', - abstract: true, - template: "", - }) - .state('users.group.list', { - resolve: { - groups: function(Group) { - return Group.findAll(); - } + } + }) + .state('users.user.csv-import', { + url: '/csv-import', + controller: 'UserCSVImportCtrl', + }) + // groups + .state('users.group', { + url: '/groups', + abstract: true, + template: "", + }) + .state('users.group.list', { + resolve: { + groups: function(Group) { + return Group.findAll(); } - }) - .state('users.group.create', { - resolve: { - groups: function(Group) { - return Group.findAll(); - } + } + }) + .state('users.group.create', { + resolve: { + groups: function(Group) { + return Group.findAll(); } - }) - .state('users.group.detail', { - resolve: { - group: function(Group, $stateParams) { - return Group.find($stateParams.id); - } + } + }) + .state('users.group.detail', { + resolve: { + group: function(Group, $stateParams) { + return Group.find($stateParams.id); } - }) - .state('users.group.detail.update', { - views: { - '@users.group': {} - } - }); + } + }) + .state('users.group.detail.update', { + views: { + '@users.group': {} + } + }); }) .factory('operator', function(User, Group) { diff --git a/openslides/users/views.py b/openslides/users/views.py index d64f60ffd..687666ea3 100644 --- a/openslides/users/views.py +++ b/openslides/users/views.py @@ -28,6 +28,8 @@ from .serializers import ( ) +# Views to generate PDFs + class UsersListPDF(PDFView): """ Generate the userliste as PDF. @@ -61,15 +63,7 @@ class UsersPasswordsPDF(PDFView): users_passwords_to_pdf(pdf) -class UserCSVImportView(CSVImportView): - """ - Import users via CSV. - """ - required_permission = 'users.can_manage' - success_url_name = 'user_list' - template_name = 'users/user_form_csv_import.html' - import_function = staticmethod(import_users) - +# Viewsets for the rest api class UserViewSet(ModelViewSet): """ @@ -136,44 +130,7 @@ class GroupViewSet(ModelViewSet): return response -class UserSettingsView(LoginMixin, UpdateView): - required_permission = None - template_name = 'users/settings.html' - success_url_name = 'user_settings' - model = User - form_class = UsersettingsForm - url_name_args = [] - - def get_initial(self): - initial = super().get_initial() - initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE) - return initial - - def form_valid(self, form): - self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language'] - activate(self.request.LANGUAGE_CODE) - return super().form_valid(form) - - def get_object(self): - return self.request.user - - -class UserPasswordSettingsView(LoginMixin, FormView): - required_permission = None - template_name = 'users/password_change.html' - success_url_name = 'core_dashboard' - form_class = PasswordChangeForm - - def form_valid(self, form): - form.save() - messages.success(self.request, _('Password successfully changed.')) - return super().form_valid(form) - - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs['user'] = self.request.user - return kwargs - +# API Views class UserLoginView(APIView): """ @@ -224,3 +181,54 @@ class WhoAmIView(APIView): return super().get_context_data( user_id=self.request.user.pk, **context) + + +# Deprecated views. Will be removed after the implementation in angularjs + +class UserCSVImportView(CSVImportView): + """ + Import users via CSV. + """ + required_permission = 'users.can_manage' + success_url_name = 'user_list' + template_name = 'users/user_form_csv_import.html' + import_function = staticmethod(import_users) + + +class UserSettingsView(LoginMixin, UpdateView): + required_permission = None + template_name = 'users/settings.html' + success_url_name = 'user_settings' + model = User + form_class = UsersettingsForm + url_name_args = [] + + def get_initial(self): + initial = super().get_initial() + initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE) + return initial + + def form_valid(self, form): + self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language'] + activate(self.request.LANGUAGE_CODE) + return super().form_valid(form) + + def get_object(self): + return self.request.user + + +class UserPasswordSettingsView(LoginMixin, FormView): + required_permission = None + template_name = 'users/password_change.html' + success_url_name = 'core_dashboard' + form_class = PasswordChangeForm + + def form_valid(self, form): + form.save() + messages.success(self.request, _('Password successfully changed.')) + return super().form_valid(form) + + def get_form_kwargs(self): + kwargs = super().get_form_kwargs() + kwargs['user'] = self.request.user + return kwargs diff --git a/tests/unit/core/__init__.py b/tests/unit/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/unit/core/test_views.py b/tests/unit/core/test_views.py new file mode 100644 index 000000000..36f1df6b3 --- /dev/null +++ b/tests/unit/core/test_views.py @@ -0,0 +1,21 @@ +from unittest import TestCase +from unittest.mock import patch + +from openslides.core import views + + +class TestUrlPatternsView(TestCase): + @patch('openslides.core.views.get_resolver') + def test_get_context_data(self, mock_resolver): + mock_resolver().reverse_dict = { + 'url_pattern1': [[['my_url1']]], + 'url_pattern2': [[['my_url2/%(kwarg)s/']]], + ('not_a_str', ): [[['not_a_str']]]} + view = views.UrlPatternsView() + + context = view.get_context_data() + + self.assertEqual( + context, + {'url_pattern1': 'my_url1', + 'url_pattern2': 'my_url2/:kwarg/'})