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. 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() queryset = AssignmentPoll.objects.all()
serializer_class = AssignmentAllPollSerializer serializer_class = AssignmentAllPollSerializer

View File

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

View File

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

View File

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

View File

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