diff --git a/src/c3nav/editor/api.py b/src/c3nav/editor/api.py index 2ad9abfd..d53d3c1f 100644 --- a/src/c3nav/editor/api.py +++ b/src/c3nav/editor/api.py @@ -1,8 +1,10 @@ from itertools import chain from django.db.models import Prefetch, Q +from django.urls import Resolver404, resolve +from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import detail_route, list_route -from rest_framework.exceptions import PermissionDenied, ValidationError +from rest_framework.exceptions import NotFound, PermissionDenied, ValidationError from rest_framework.generics import get_object_or_404 from rest_framework.response import Response from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet @@ -23,6 +25,9 @@ class EditorViewSet(ViewSet): /geometrystyles/ returns styling information for all geometry types /bounds/ returns the maximum bounds of the map """ + lookup_field = 'path' + lookup_value_regex = r'.+' + @staticmethod def _get_level_geometries(level): buildings = level.buildings.all() @@ -261,6 +266,33 @@ class EditorViewSet(ViewSet): 'bounds': Source.max_bounds(), }) + def list(self, request, *args, **kwargs): + return self.retrieve(request, *args, **kwargs) + + def retrieve(self, request, *args, **kwargs): + if not can_access_editor(request): + return PermissionDenied + + resolved = None + path = self.kwargs.get('path', '') + if path: + try: + resolved = resolve('/editor/'+path+'/') + except Resolver404: + pass + + if not resolved: + try: + resolved = resolve('/editor/'+path) + except Resolver404: + raise NotFound(_('No matching editor view endpoint found.')) + + if not getattr(resolved.func, 'api_hybrid', False): + raise NotFound(_('Matching editor view point does not provide an API.')) + + response = resolved.func(request, *resolved.args, **resolved.kwargs) + return Response(str(response)) + class ChangeSetViewSet(ReadOnlyModelViewSet): """ diff --git a/src/c3nav/editor/views/base.py b/src/c3nav/editor/views/base.py index ccb93535..7bbeb270 100644 --- a/src/c3nav/editor/views/base.py +++ b/src/c3nav/editor/views/base.py @@ -11,25 +11,34 @@ from c3nav.mapdata.models.access import AccessPermission from c3nav.mapdata.utils.user import can_access_editor -def sidebar_view(func=None, select_related=None): +def sidebar_view(func=None, select_related=None, api_hybrid=False): if func is None: def wrapped(inner_func): - return sidebar_view(inner_func, select_related) + return sidebar_view(inner_func, select_related=select_related, api_hybrid=api_hybrid) return wrapped @wraps(func) - def with_ajax_check(request, *args, **kwargs): + def wrapped(request, *args, api=False, **kwargs): + if api and api_hybrid: + raise Exception('API call on a view without api_hybrid!') + if not can_access_editor(request): raise PermissionDenied request.changeset = ChangeSet.get_for_request(request, select_related) - ajax = request.is_ajax() or 'ajax' in request.GET + if api: + return call_api_hybrid_view_for_api(func, request, *args, **kwargs) + ajax = request.is_ajax() or 'ajax' in request.GET if not ajax: request.META.pop('HTTP_IF_NONE_MATCH', None) - response = func(request, *args, **kwargs) + if api_hybrid: + response = call_api_hybrid_view_for_html(func, request, *args, **kwargs) + else: + response = func(request, *args, **kwargs) + if ajax: if isinstance(response, HttpResponseRedirect): return render(request, 'editor/redirect.html', {'target': response['location']}) @@ -44,8 +53,19 @@ def sidebar_view(func=None, select_related=None): response['Cache-Control'] = 'no-cache' patch_vary_headers(response, ('X-Requested-With', )) return response + wrapped.api_hybrid = api_hybrid - return with_ajax_check + return wrapped + + +def call_api_hybrid_view_for_api(func, request, *args, **kwargs): + response = func(request, *args, **kwargs) + return response + + +def call_api_hybrid_view_for_html(func, request, *args, **kwargs): + response = func(request, *args, **kwargs) + return response def etag_func(request, *args, **kwargs):