Merge pull request #3017 from normanjaeckel/FixViewSets

Fixed use of PATCH and PUT. Fixed #1871.
This commit is contained in:
Emanuel Schütze 2017-03-03 13:32:11 +01:00 committed by GitHub
commit 283e037cdb
5 changed files with 38 additions and 27 deletions

View File

@ -235,7 +235,7 @@ class AssignmentPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet)
"""
API endpoint for assignment polls.
There are the following views: update and destroy.
There are the following views: update, partial_update and destroy.
"""
queryset = AssignmentPoll.objects.all()
serializer_class = AssignmentAllPollSerializer

View File

@ -199,9 +199,7 @@ class ProjectorViewSet(ModelViewSet):
"""
API endpoint for the projector slide info.
There are the following views: metadata, list, retrieve,
activate_elements, prune_elements, update_elements,
deactivate_elements, clear_elements and control_view.
There are the following views: See strings in check_view_permissions().
"""
access_permissions = ProjectorAccessPermissions()
queryset = Projector.objects.all()
@ -575,7 +573,7 @@ class TagViewSet(ModelViewSet):
# Every authenticated user can see the metadata.
# Anonymous users can do so if they are enabled.
result = self.request.user.is_authenticated() or anonymous_is_enabled()
elif self.action in ('create', 'update', 'destroy'):
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
result = has_perm(self.request.user, 'core.can_manage_tags')
else:
result = False
@ -616,7 +614,8 @@ class ConfigViewSet(ViewSet):
"""
API endpoint for the config.
There are the following views: metadata, list, retrieve and update.
There are the following views: metadata, list, retrieve, update and
partial_update.
"""
access_permissions = ConfigAccessPermissions()
metadata_class = ConfigMetadata
@ -632,7 +631,7 @@ class ConfigViewSet(ViewSet):
# retrieve the config. Anonymous users can do so if they are
# enabled.
result = self.request.user.is_authenticated() or anonymous_is_enabled()
elif self.action == 'update':
elif self.action in ('partial_update', 'update'):
result = has_perm(self.request.user, 'core.can_manage_config')
else:
result = False
@ -742,7 +741,8 @@ class ProjectorMessageViewSet(ModelViewSet):
"""
API endpoint for messages.
There are the following views: list, retrieve, create, update and destroy.
There are the following views: list, retrieve, create, update,
partial_update and destroy.
"""
access_permissions = ProjectorMessageAccessPermissions()
queryset = ProjectorMessage.objects.all()
@ -753,7 +753,7 @@ class ProjectorMessageViewSet(ModelViewSet):
"""
if self.action in ('list', 'retrieve'):
result = self.get_access_permissions().check_permissions(self.request.user)
elif self.action in ('create', 'update', 'destroy'):
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
result = has_perm(self.request.user, 'core.can_manage_projector')
else:
result = False
@ -764,7 +764,8 @@ class CountdownViewSet(ModelViewSet):
"""
API endpoint for Countdown.
There are the following views: list, retrieve, create, update and destroy.
There are the following views: list, retrieve, create, update,
partial_update and destroy.
"""
access_permissions = CountdownAccessPermissions()
queryset = Countdown.objects.all()
@ -775,7 +776,7 @@ class CountdownViewSet(ModelViewSet):
"""
if self.action in ('list', 'retrieve'):
result = self.get_access_permissions().check_permissions(self.request.user)
elif self.action in ('create', 'update', 'destroy'):
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
result = has_perm(self.request.user, 'core.can_manage_projector')
else:
result = False

View File

@ -1324,7 +1324,7 @@ angular.module('OpenSlidesApp.motions.site', [
// inject the changed change recommendation (copy) object back into DS store
MotionChangeRecommendation.inject(change);
// save changed change recommendation object on server
MotionChangeRecommendation.save(change, { method: 'PATCH' }).then(
MotionChangeRecommendation.save(change).then(
function(success) {
$scope.closeThisDialog();
},
@ -1531,7 +1531,7 @@ angular.module('OpenSlidesApp.motions.site', [
// inject the changed motion (copy) object back into DS store
Motion.inject(motion);
// save change motion object on server
Motion.save(motion, { method: 'PATCH' }).then(
Motion.save(motion).then(
function(success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.

View File

@ -12,6 +12,7 @@ from rest_framework import status
from ..core.config import config
from ..utils.auth import has_perm
from ..utils.autoupdate import inform_changed_data
from ..utils.collection import CollectionElement
from ..utils.rest_api import (
DestroyModelMixin,
GenericViewSet,
@ -85,13 +86,16 @@ class MotionViewSet(ModelViewSet):
"""
Customized view endpoint to create a new motion.
"""
# Check if parent motion exists
parent_motion = None
if 'parent_id' in request.data:
# Check if parent motion exists.
if request.data.get('parent_id') is not None:
try:
parent_motion = Motion.objects.get(pk=request.data['parent_id'])
parent_motion = CollectionElement.from_values(
Motion.get_collection_string(),
request.data['parent_id'])
except Motion.DoesNotExist:
raise ValidationError({'detail': _('The parent motion does not exist.')})
else:
parent_motion = None
# Check permission to send some data.
if not has_perm(request.user, 'motions.can_manage'):
@ -101,16 +105,15 @@ class MotionViewSet(ModelViewSet):
'reason',
'comments', # This is checked later.
]
if parent_motion: # For creating amendments.
if parent_motion is not None:
# For creating amendments.
whitelist.extend([
'parent_id',
'category_id', # This will be set to the matching
'motion_block_id', # values from parent_motion.
])
request.data['category_id'] = (
parent_motion.category.id if parent_motion.category else None)
request.data['motion_block_id'] = (
parent_motion.motion_block.id if parent_motion.motion_block else None)
request.data['category_id'] = parent_motion.get_full_data().get('category_id')
request.data['motion_block_id'] = parent_motion.get_full_data().get('motion_block_id')
for key in request.data.keys():
if key not in whitelist:
# Non-staff users are allowed to send only some data.
@ -155,14 +158,17 @@ class MotionViewSet(ModelViewSet):
# Check permission to send only some data.
if not has_perm(request.user, 'motions.can_manage'):
# Remove fields that the user is not allowed to change.
# The list() is required because we want to use del inside the loop.
keys = list(request.data.keys())
whitelist = (
'title',
'text',
'reason',)
keys = list(request.data.keys())
'reason',
'comments', # This is checked later.
)
for key in keys:
if key not in whitelist:
# Non-staff users are allowed to send only some data. Ignore other data.
del request.data[key]
if not has_perm(request.user, 'motions.can_see_and_manage_comments'):
try:
@ -364,7 +370,7 @@ class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
"""
API endpoint for motion polls.
There are the following views: update and destroy.
There are the following views: update, partial_update and destroy.
"""
queryset = MotionPoll.objects.all()
serializer_class = MotionPollSerializer
@ -414,7 +420,8 @@ class MotionChangeRecommendationViewSet(ModelViewSet):
elif self.action == 'metadata':
result = has_perm(self.request.user, 'motions.can_see')
elif self.action in ('create', 'destroy', 'partial_update', 'update'):
result = has_perm(self.request.user, 'motions.can_manage')
result = (has_perm(self.request.user, 'motions.can_see') and
has_perm(self.request.user, 'motions.can_manage'))
else:
result = False
return result
@ -615,6 +622,8 @@ class WorkflowViewSet(ModelViewSet):
return result
# Special API views
class MotionDocxTemplateView(APIView):
"""
Returns the template for motions docx export

View File

@ -10,6 +10,7 @@ class MotionViewSetCreate(TestCase):
"""
def setUp(self):
self.request = MagicMock()
self.request.data.get.return_value = None
self.view_instance = MotionViewSet()
self.view_instance.request = self.request
self.view_instance.format_kwarg = MagicMock()