diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index c8e5536d..66dcafe2 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -15,10 +15,11 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import action from rest_framework.exceptions import NotFound, ValidationError from rest_framework.generics import get_object_or_404 -from rest_framework.mixins import RetrieveModelMixin +from rest_framework.mixins import RetrieveModelMixin, UpdateModelMixin from rest_framework.response import Response from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet, ViewSet +from c3nav.mapdata.forms import PositionAPIUpdateForm from c3nav.mapdata.models import AccessRestriction, Building, Door, Hole, LocationGroup, MapUpdate, Source, Space from c3nav.mapdata.models.access import AccessPermission, AccessRestrictionGroup from c3nav.mapdata.models.geometry.base import GeometryMixin @@ -464,7 +465,7 @@ class LocationBySlugViewSet(LocationViewSetBase): return get_location_by_slug_for_request(self.kwargs['slug'], self.request) -class DynamicLocationPositionViewSet(RetrieveModelMixin, GenericViewSet): +class DynamicLocationPositionViewSet(UpdateModelMixin, RetrieveModelMixin, GenericViewSet): queryset = LocationSlug.objects.all() lookup_field = 'slug' lookup_value_regex = r'[^/]+' @@ -482,6 +483,20 @@ class DynamicLocationPositionViewSet(RetrieveModelMixin, GenericViewSet): obj = self.get_object() return Response(obj.serialize_position()) + def update(self, request, *args, **kwargs): + instance = self.get_object() + params = request.data + form = PositionAPIUpdateForm(instance=instance, data=params, request=request) + + if not form.is_valid(): + return Response({ + 'errors': form.errors, + }, status=400) + + form.save() + + return Response(form.instance.serialize_position()) + class SourceViewSet(MapdataViewSet): queryset = Source.objects.all() diff --git a/src/c3nav/mapdata/forms.py b/src/c3nav/mapdata/forms.py index 67713873..c7c1c8b7 100644 --- a/src/c3nav/mapdata/forms.py +++ b/src/c3nav/mapdata/forms.py @@ -8,6 +8,8 @@ from django.utils.translation import get_language_info from django.utils.translation import ugettext_lazy as _ from c3nav.mapdata.fields import I18nField +from c3nav.mapdata.models.locations import Position +from c3nav.mapdata.utils.locations import get_location_by_id_for_request class I18nModelFormMixin(ModelForm): @@ -62,3 +64,36 @@ class I18nModelFormMixin(ModelForm): super().full_clean() for field, values in self.i18n_fields: setattr(self.instance, field.attname, {lang: value for lang, value in values.items() if value}) + + +class PositionAPIUpdateForm(ModelForm): + secret = CharField() + + def __init__(self, *args, request=None, **kwargs): + self.request = request + super().__init__(*args, **kwargs) + + class Meta: + model = Position + fields = ['coordinates_id', 'timeout'] + + def clean_secret(self): + # not called api_secret so we don't overwrite it + api_secret = self.cleaned_data['secret'] + if api_secret != self.instance.api_secret: + raise ValidationError(_('Wrong API secret.')) + return api_secret + + def clean_coordinates_id(self): + coordinates_id = self.cleaned_data['coordinates_id'] + if coordinates_id is None: + return coordinates_id + + if not coordinates_id.startswith('c:'): + raise ValidationError(_('Invalid coordinates.')) + + coordinates = get_location_by_id_for_request(self.cleaned_data['coordinates_id'], self.request) + if coordinates is None: + raise ValidationError(_('Invalid coordinates.')) + + return coordinates_id diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index f1d318e3..44269034 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -566,7 +566,8 @@ class Position(CustomLocationProxyMixin, models.Model): name = models.CharField(_('name'), max_length=32) secret = models.CharField(_('secret'), unique=True, max_length=32, default=get_position_secret) last_coordinates_update = models.DateTimeField(_('last coordinates update'), null=True) - timeout = models.PositiveSmallIntegerField(_('timeout (in seconds)'), default=0, help_text=_('0 for no timeout')) + timeout = models.PositiveSmallIntegerField(_('timeout (in seconds)'), default=0, blank=True, + help_text=_('0 for no timeout')) coordinates_id = models.CharField(_('coordinates'), null=True, max_length=48) api_secret = models.CharField(_('api secret'), max_length=64, default=get_position_api_secret)