From 671d138d03180c4c298a335f9f39a74246a0228f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Thu, 29 Nov 2018 00:37:49 +0100 Subject: [PATCH] some more stuff for the editor API --- src/c3nav/api/api.py | 11 +++----- src/c3nav/api/middleware.py | 21 --------------- src/c3nav/api/utils.py | 16 +++++++++++ src/c3nav/editor/api.py | 53 ++++++++++++++++++++++++++++++++----- src/c3nav/settings.py | 1 - 5 files changed, 66 insertions(+), 36 deletions(-) delete mode 100644 src/c3nav/api/middleware.py create mode 100644 src/c3nav/api/utils.py diff --git a/src/c3nav/api/api.py b/src/c3nav/api/api.py index a9e54dd2..8a0a4fb4 100644 --- a/src/c3nav/api/api.py +++ b/src/c3nav/api/api.py @@ -9,6 +9,7 @@ from rest_framework.response import Response from rest_framework.viewsets import ViewSet from c3nav.api.models import Token +from c3nav.api.utils import get_api_post_data class SessionViewSet(ViewSet): @@ -34,10 +35,7 @@ class SessionViewSet(ViewSet): if request.user.is_authenticated: raise ParseError(_('Log out first.')) - try: - data = request.json_body - except AttributeError: - data = request.POST + data = get_api_post_data(request) if 'token' in data: try: @@ -65,10 +63,7 @@ class SessionViewSet(ViewSet): # django-rest-framework doesn't do this for logged out requests SessionAuthentication().enforce_csrf(request) - try: - data = request.json_body - except AttributeError: - data = request.POST + data = get_api_post_data(request) form = AuthenticationForm(request, data=data) if not form.is_valid(): diff --git a/src/c3nav/api/middleware.py b/src/c3nav/api/middleware.py deleted file mode 100644 index 1198829e..00000000 --- a/src/c3nav/api/middleware.py +++ /dev/null @@ -1,21 +0,0 @@ -import json - -from django.http import HttpResponseBadRequest - - -class JsonRequestBodyMiddleware: - """ - Enables posting JSON requests. - """ - def __init__(self, get_response): - self.get_response = get_response - - def __call__(self, request): - is_json = request.META.get('CONTENT_TYPE').lower() == 'application/json' - if is_json: - try: - data = json.loads(request.body) - except json.JSONDecodeError: - raise HttpResponseBadRequest - request.json_body = data - return self.get_response(request) diff --git a/src/c3nav/api/utils.py b/src/c3nav/api/utils.py new file mode 100644 index 00000000..2757b3e5 --- /dev/null +++ b/src/c3nav/api/utils.py @@ -0,0 +1,16 @@ +import json + +from rest_framework.exceptions import ParseError + + +def get_api_post_data(request): + is_json = request.META.get('CONTENT_TYPE').lower() == 'application/json' + if is_json: + try: + data = json.loads(request.body) + except json.JSONDecodeError: + raise ParseError('Invalid JSON.') + else: + request.json_body = data + return data + return request.POST diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index 8e14199b..f8b1c0ee 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -12,6 +12,7 @@ from rest_framework.response import Response from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet from shapely.ops import cascaded_union +from c3nav.api.utils import get_api_post_data from c3nav.editor.models import ChangeSet from c3nav.editor.views.base import etag_func from c3nav.mapdata.api import api_etag @@ -262,7 +263,7 @@ class EditorViewSet(ViewSet): @api_etag(etag_func=etag_func, cache_parameters={}) def bounds(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied return Response({ 'bounds': Source.max_bounds(), @@ -310,7 +311,7 @@ class EditorViewSet(ViewSet): def retrieve(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied resolved = self.resolved if not resolved: @@ -319,6 +320,8 @@ class EditorViewSet(ViewSet): if not getattr(resolved.func, 'api_hybrid', False): raise NotFound(_('Matching editor view point does not provide an API.')) + get_api_post_data(request) + response = resolved.func(request, api=True, *resolved.args, **resolved.kwargs) return response @@ -327,6 +330,9 @@ class ChangeSetViewSet(ReadOnlyModelViewSet): """ List change sets /current/ returns the current changeset. + /direct_editing/ POST to activate direct editing (if available). + /deactive/ POST to deactivate current changeset or deactivate direct editing + /{id}/changes/ returns the changes of a given changeset. """ queryset = ChangeSet.objects.all() @@ -335,28 +341,63 @@ class ChangeSetViewSet(ReadOnlyModelViewSet): def list(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied return Response([obj.serialize() for obj in self.get_queryset().order_by('id')]) def retrieve(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied return Response(self.get_object().serialize()) @action(detail=False, methods=['get']) def current(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied + changeset = ChangeSet.get_for_request(request) return Response({ 'direct_editing': changeset.direct_editing, 'changeset': changeset.serialize() if changeset.pk else None, }) + @action(detail=False, methods=['post']) + def direct_editing(self, request, *args, **kwargs): + if not can_access_editor(request): + raise PermissionDenied + # django-rest-framework doesn't automatically do this for logged out requests + SessionAuthentication().enforce_csrf(request) + + if not ChangeSet.can_direct_edit(request): + raise PermissionDenied(_('You don\'t have the permission to activate direct editing.')) + + changeset = ChangeSet.get_for_request(request) + if changeset.pk is not None: + raise PermissionDenied(_('You cannot activate direct editing if you have an active changeset.')) + + request.session['direct_editing'] = True + + return Response({ + 'success': True, + }) + + @action(detail=False, methods=['post']) + def deactivate(self, request, *args, **kwargs): + if not can_access_editor(request): + raise PermissionDenied + # django-rest-framework doesn't automatically do this for logged out requests + SessionAuthentication().enforce_csrf(request) + + request.session.pop('changeset', None) + request.session['direct_editing'] = False + + return Response({ + 'success': True, + }) + @action(detail=True, methods=['get']) def changes(self, request, *args, **kwargs): if not can_access_editor(request): - return PermissionDenied + raise PermissionDenied changeset = self.get_object() changeset.fill_changes_cache() return Response([obj.serialize() for obj in changeset.iter_changed_objects()]) diff --git a/src/c3nav/settings.py b/src/c3nav/settings.py index b6b2b52b..d150f96e 100644 --- a/src/c3nav/settings.py +++ b/src/c3nav/settings.py @@ -227,7 +227,6 @@ MIDDLEWARE = [ 'c3nav.mapdata.middleware.UserDataMiddleware', 'c3nav.site.middleware.MobileclientMiddleware', 'c3nav.control.middleware.UserPermissionsMiddleware', - 'c3nav.api.middleware.JsonRequestBodyMiddleware', ] with suppress(ImportError):