2016-09-13 11:54:30 +02:00
|
|
|
import base64
|
|
|
|
|
|
|
|
from django.contrib.staticfiles import finders
|
2016-07-29 23:33:47 +02:00
|
|
|
from django.db import IntegrityError, transaction
|
2015-04-30 19:13:28 +02:00
|
|
|
from django.http import Http404
|
2013-06-14 20:24:48 +02:00
|
|
|
from django.utils.text import slugify
|
2013-09-25 10:01:01 +02:00
|
|
|
from django.utils.translation import ugettext as _
|
2015-04-30 19:13:28 +02:00
|
|
|
from django.utils.translation import ugettext_noop
|
2015-03-09 15:40:54 +01:00
|
|
|
from reportlab.platypus import SimpleDocTemplate
|
2015-04-30 19:13:28 +02:00
|
|
|
from rest_framework import status
|
2013-04-24 15:07:39 +02:00
|
|
|
|
2015-06-29 12:08:15 +02:00
|
|
|
from openslides.core.config import config
|
2015-06-16 10:37:23 +02:00
|
|
|
from openslides.utils.rest_api import (
|
2015-07-22 15:23:57 +02:00
|
|
|
DestroyModelMixin,
|
|
|
|
GenericViewSet,
|
2015-06-16 10:37:23 +02:00
|
|
|
ModelViewSet,
|
|
|
|
Response,
|
2015-07-22 15:23:57 +02:00
|
|
|
UpdateModelMixin,
|
2015-06-16 10:37:23 +02:00
|
|
|
ValidationError,
|
|
|
|
detail_route,
|
|
|
|
)
|
2016-09-13 11:54:30 +02:00
|
|
|
from openslides.utils.views import APIView, PDFView, SingleObjectMixin
|
2015-06-16 10:37:23 +02:00
|
|
|
|
2016-02-11 22:58:32 +01:00
|
|
|
from .access_permissions import (
|
|
|
|
CategoryAccessPermissions,
|
|
|
|
MotionAccessPermissions,
|
2016-10-01 20:42:44 +02:00
|
|
|
MotionBlockAccessPermissions,
|
2016-09-10 18:49:38 +02:00
|
|
|
MotionChangeRecommendationAccessPermissions,
|
2016-02-11 22:58:32 +01:00
|
|
|
WorkflowAccessPermissions,
|
|
|
|
)
|
2015-07-22 15:23:57 +02:00
|
|
|
from .exceptions import WorkflowError
|
2016-09-03 21:43:11 +02:00
|
|
|
from .models import (
|
|
|
|
Category,
|
|
|
|
Motion,
|
2016-10-01 20:42:44 +02:00
|
|
|
MotionBlock,
|
2016-09-10 18:49:38 +02:00
|
|
|
MotionChangeRecommendation,
|
2016-09-03 21:43:11 +02:00
|
|
|
MotionPoll,
|
|
|
|
MotionVersion,
|
|
|
|
State,
|
|
|
|
Workflow,
|
|
|
|
)
|
2013-09-25 10:01:01 +02:00
|
|
|
from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf
|
2016-02-11 22:58:32 +01:00
|
|
|
from .serializers import MotionPollSerializer
|
2011-07-31 10:46:29 +02:00
|
|
|
|
2013-02-02 00:37:43 +01:00
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
# Viewsets for the REST API
|
|
|
|
|
2015-02-12 18:48:14 +01:00
|
|
|
class MotionViewSet(ModelViewSet):
|
2015-01-24 16:35:50 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
API endpoint for motions.
|
|
|
|
|
2015-08-31 14:07:24 +02:00
|
|
|
There are the following views: metadata, list, retrieve, create,
|
2015-07-22 15:23:57 +02:00
|
|
|
partial_update, update, destroy, manage_version, support, set_state and
|
|
|
|
create_poll.
|
2015-01-24 16:35:50 +01:00
|
|
|
"""
|
2016-02-11 22:58:32 +01:00
|
|
|
access_permissions = MotionAccessPermissions()
|
2015-01-24 16:35:50 +01:00
|
|
|
queryset = Motion.objects.all()
|
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
def check_view_permissions(self):
|
2015-01-24 16:35:50 +01:00
|
|
|
"""
|
2015-07-01 23:18:48 +02:00
|
|
|
Returns True if the user has required permissions.
|
2015-01-24 16:35:50 +01:00
|
|
|
"""
|
2016-09-17 22:26:23 +02:00
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action in ('metadata', 'partial_update', 'update'):
|
2015-07-01 23:18:48 +02:00
|
|
|
result = self.request.user.has_perm('motions.can_see')
|
|
|
|
# For partial_update and update requests the rest of the check is
|
|
|
|
# done in the update method. See below.
|
|
|
|
elif self.action == 'create':
|
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_create') and
|
|
|
|
(not config['motions_stop_submitting'] or
|
|
|
|
self.request.user.has_perm('motions.can_manage')))
|
2016-09-03 21:43:11 +02:00
|
|
|
elif self.action in ('destroy', 'manage_version', 'set_state', 'set_recommendation', 'create_poll'):
|
2015-07-01 23:18:48 +02:00
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_manage'))
|
|
|
|
elif self.action == 'support':
|
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_support'))
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
2015-01-24 16:35:50 +01:00
|
|
|
|
2015-04-30 19:13:28 +02:00
|
|
|
def create(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Customized view endpoint to create a new motion.
|
|
|
|
"""
|
|
|
|
# Check permission to send submitter and supporter data.
|
|
|
|
if (not request.user.has_perm('motions.can_manage') and
|
2015-11-18 01:20:49 +01:00
|
|
|
(request.data.get('submitters_id') or request.data.get('supporters_id'))):
|
2015-04-30 19:13:28 +02:00
|
|
|
# Non-staff users are not allowed to send submitter or supporter data.
|
|
|
|
self.permission_denied(request)
|
|
|
|
|
2016-10-01 20:42:44 +02:00
|
|
|
# TODO: Should non staff users be allowed to set motions to blocks or send categories, ...? #2506
|
|
|
|
|
2016-07-29 23:33:47 +02:00
|
|
|
# Check permission to send comment data.
|
2016-09-16 23:35:37 +02:00
|
|
|
if not request.user.has_perm('motions.can_see_and_manage_comments'):
|
|
|
|
try:
|
|
|
|
# Ignore comments data if user is not allowed to send comments.
|
|
|
|
del request.data['comments']
|
|
|
|
except KeyError:
|
|
|
|
# No comments here. Just do nothing.
|
|
|
|
pass
|
2016-07-29 23:33:47 +02:00
|
|
|
|
2015-04-30 19:13:28 +02:00
|
|
|
# Validate data and create motion.
|
|
|
|
serializer = self.get_serializer(data=request.data)
|
|
|
|
serializer.is_valid(raise_exception=True)
|
|
|
|
motion = serializer.save(request_user=request.user)
|
|
|
|
|
|
|
|
# Write the log message and initiate response.
|
|
|
|
motion.write_log([ugettext_noop('Motion created')], request.user)
|
|
|
|
headers = self.get_success_headers(serializer.data)
|
|
|
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
|
|
|
|
|
|
|
def update(self, request, *args, **kwargs):
|
|
|
|
"""
|
|
|
|
Customized view endpoint to update a motion.
|
|
|
|
|
|
|
|
Checks also whether the requesting user can update the motion. He
|
|
|
|
needs at least the permissions 'motions.can_see' (see
|
2015-07-01 23:18:48 +02:00
|
|
|
self.check_view_permissions()). Also the instance method
|
2015-04-30 19:13:28 +02:00
|
|
|
get_allowed_actions() is evaluated.
|
|
|
|
"""
|
|
|
|
# Get motion.
|
|
|
|
motion = self.get_object()
|
|
|
|
|
|
|
|
# Check permissions.
|
|
|
|
if not motion.get_allowed_actions(request.user)['update']:
|
|
|
|
self.permission_denied(request)
|
|
|
|
|
2016-01-14 23:44:19 +01:00
|
|
|
# Check permission to send only some data.
|
|
|
|
if not request.user.has_perm('motions.can_manage'):
|
|
|
|
whitelist = (
|
|
|
|
'title',
|
|
|
|
'text',
|
|
|
|
'reason',)
|
|
|
|
keys = list(request.data.keys())
|
|
|
|
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]
|
2016-07-29 23:33:47 +02:00
|
|
|
if not request.user.has_perm('motions.can_see_and_manage_comments'):
|
|
|
|
try:
|
|
|
|
del request.data['comments']
|
|
|
|
except KeyError:
|
|
|
|
# No comments here. Just do nothing.
|
|
|
|
pass
|
2015-04-30 19:13:28 +02:00
|
|
|
|
|
|
|
# Validate data and update motion.
|
|
|
|
serializer = self.get_serializer(
|
|
|
|
motion,
|
|
|
|
data=request.data,
|
|
|
|
partial=kwargs.get('partial', False))
|
|
|
|
serializer.is_valid(raise_exception=True)
|
2016-01-14 23:13:15 +01:00
|
|
|
updated_motion = serializer.save(disable_versioning=request.data.get('disable_versioning'))
|
2015-04-30 19:13:28 +02:00
|
|
|
|
|
|
|
# Write the log message, check removal of supporters and initiate response.
|
|
|
|
# TODO: Log if a version was updated.
|
|
|
|
updated_motion.write_log([ugettext_noop('Motion updated')], request.user)
|
2015-06-16 18:12:59 +02:00
|
|
|
if (config['motions_remove_supporters'] and updated_motion.state.allow_support and
|
2015-04-30 19:13:28 +02:00
|
|
|
not request.user.has_perm('motions.can_manage')):
|
|
|
|
updated_motion.supporters.clear()
|
|
|
|
updated_motion.write_log([ugettext_noop('All supporters removed')], request.user)
|
|
|
|
return Response(serializer.data)
|
|
|
|
|
|
|
|
@detail_route(methods=['put', 'delete'])
|
|
|
|
def manage_version(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
Special view endpoint to permit and delete a version of a motion.
|
|
|
|
|
|
|
|
Send PUT {'version_number': <number>} to permit and DELETE
|
|
|
|
{'version_number': <number>} to delete a version. Deleting the
|
|
|
|
active version is not allowed. Only managers can use this view.
|
|
|
|
"""
|
|
|
|
# Retrieve motion and version.
|
|
|
|
motion = self.get_object()
|
|
|
|
version_number = request.data.get('version_number')
|
|
|
|
try:
|
|
|
|
version = motion.versions.get(version_number=version_number)
|
|
|
|
except MotionVersion.DoesNotExist:
|
|
|
|
raise Http404('Version %s not found.' % version_number)
|
|
|
|
|
|
|
|
# Permit or delete version.
|
|
|
|
if request.method == 'PUT':
|
|
|
|
# Permit version.
|
|
|
|
motion.active_version = version
|
|
|
|
motion.save(update_fields=['active_version'])
|
|
|
|
motion.write_log(
|
|
|
|
message_list=[ugettext_noop('Version'),
|
|
|
|
' %d ' % version.version_number,
|
|
|
|
ugettext_noop('permitted')],
|
|
|
|
person=self.request.user)
|
|
|
|
message = _('Version %d permitted successfully.') % version.version_number
|
|
|
|
else:
|
|
|
|
# Delete version.
|
|
|
|
# request.method == 'DELETE'
|
|
|
|
if version == motion.active_version:
|
|
|
|
raise ValidationError({'detail': _('You can not delete the active version of a motion.')})
|
|
|
|
version.delete()
|
|
|
|
motion.write_log(
|
|
|
|
message_list=[ugettext_noop('Version'),
|
|
|
|
' %d ' % version.version_number,
|
|
|
|
ugettext_noop('deleted')],
|
|
|
|
person=self.request.user)
|
|
|
|
message = _('Version %d deleted successfully.') % version.version_number
|
|
|
|
|
|
|
|
# Initiate response.
|
|
|
|
return Response({'detail': message})
|
|
|
|
|
|
|
|
@detail_route(methods=['post', 'delete'])
|
|
|
|
def support(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
Special view endpoint to support a motion or withdraw support
|
|
|
|
(unsupport).
|
|
|
|
|
|
|
|
Send POST to support and DELETE to unsupport.
|
|
|
|
"""
|
|
|
|
# Retrieve motion and allowed actions.
|
|
|
|
motion = self.get_object()
|
|
|
|
allowed_actions = motion.get_allowed_actions(request.user)
|
|
|
|
|
|
|
|
# Support or unsupport motion.
|
|
|
|
if request.method == 'POST':
|
|
|
|
# Support motion.
|
|
|
|
if not allowed_actions['support']:
|
|
|
|
raise ValidationError({'detail': _('You can not support this motion.')})
|
|
|
|
motion.supporters.add(request.user)
|
|
|
|
motion.write_log([ugettext_noop('Motion supported')], request.user)
|
|
|
|
message = _('You have supported this motion successfully.')
|
|
|
|
else:
|
|
|
|
# Unsupport motion.
|
|
|
|
# request.method == 'DELETE'
|
|
|
|
if not allowed_actions['unsupport']:
|
|
|
|
raise ValidationError({'detail': _('You can not unsupport this motion.')})
|
|
|
|
motion.supporters.remove(request.user)
|
|
|
|
motion.write_log([ugettext_noop('Motion unsupported')], request.user)
|
|
|
|
message = _('You have unsupported this motion successfully.')
|
|
|
|
|
|
|
|
# Initiate response.
|
|
|
|
return Response({'detail': message})
|
|
|
|
|
|
|
|
@detail_route(methods=['put'])
|
|
|
|
def set_state(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
Special view endpoint to set and reset a state of a motion.
|
|
|
|
|
|
|
|
Send PUT {'state': <state_id>} to set and just PUT {} to reset the
|
|
|
|
state. Only managers can use this view.
|
|
|
|
"""
|
|
|
|
# Retrieve motion and state.
|
|
|
|
motion = self.get_object()
|
|
|
|
state = request.data.get('state')
|
|
|
|
|
|
|
|
# Set or reset state.
|
|
|
|
if state is not None:
|
|
|
|
# Check data and set state.
|
|
|
|
try:
|
|
|
|
state_id = int(state)
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError({'detail': _('Invalid data. State must be an integer.')})
|
|
|
|
if state_id not in [item.id for item in motion.state.next_states.all()]:
|
|
|
|
raise ValidationError(
|
|
|
|
{'detail': _('You can not set the state to %(state_id)d.') % {'state_id': state_id}})
|
|
|
|
motion.set_state(state_id)
|
|
|
|
else:
|
|
|
|
# Reset state.
|
|
|
|
motion.reset_state()
|
|
|
|
|
|
|
|
# Save motion.
|
|
|
|
motion.save(update_fields=['state', 'identifier'])
|
|
|
|
message = _('The state of the motion was set to %s.') % motion.state.name
|
|
|
|
|
|
|
|
# Write the log message and initiate response.
|
|
|
|
motion.write_log(
|
|
|
|
message_list=[ugettext_noop('State set to'), ' ', motion.state.name],
|
|
|
|
person=request.user)
|
|
|
|
return Response({'detail': message})
|
|
|
|
|
2016-09-03 21:43:11 +02:00
|
|
|
@detail_route(methods=['put'])
|
|
|
|
def set_recommendation(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
Special view endpoint to set a recommendation of a motion.
|
|
|
|
|
|
|
|
Send PUT {'recommendation': <state_id>} to set and just PUT {} to
|
|
|
|
reset the recommendation. Only managers can use this view.
|
|
|
|
"""
|
|
|
|
# Retrieve motion and recommendation state.
|
|
|
|
motion = self.get_object()
|
|
|
|
recommendation_state = request.data.get('recommendation')
|
|
|
|
|
|
|
|
# Set or reset recommendation.
|
|
|
|
if recommendation_state is not None:
|
|
|
|
# Check data and set recommendation.
|
|
|
|
try:
|
|
|
|
recommendation_state_id = int(recommendation_state)
|
|
|
|
except ValueError:
|
|
|
|
raise ValidationError({'detail': _('Invalid data. Recommendation must be an integer.')})
|
|
|
|
recommendable_states = State.objects.filter(workflow=motion.workflow, recommendation_label__isnull=False)
|
|
|
|
if recommendation_state_id not in [item.id for item in recommendable_states]:
|
|
|
|
raise ValidationError(
|
|
|
|
{'detail': _('You can not set the recommendation to {recommendation_state_id}.').format(
|
|
|
|
recommendation_state_id=recommendation_state_id)})
|
|
|
|
motion.set_recommendation(recommendation_state_id)
|
|
|
|
else:
|
|
|
|
# Reset recommendation.
|
|
|
|
motion.recommendation = None
|
|
|
|
|
|
|
|
# Save motion.
|
|
|
|
motion.save(update_fields=['recommendation'])
|
|
|
|
label = motion.recommendation.recommendation_label if motion.recommendation else 'None'
|
|
|
|
message = _('The recommendation of the motion was set to %s.') % label
|
|
|
|
|
|
|
|
# Write the log message and initiate response.
|
|
|
|
motion.write_log(
|
|
|
|
message_list=[ugettext_noop('Recommendation set to'), ' ', label],
|
|
|
|
person=request.user)
|
|
|
|
return Response({'detail': message})
|
|
|
|
|
2015-07-22 15:23:57 +02:00
|
|
|
@detail_route(methods=['post'])
|
|
|
|
def create_poll(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
View to create a poll. It is a POST request without any data.
|
|
|
|
"""
|
|
|
|
motion = self.get_object()
|
|
|
|
try:
|
|
|
|
with transaction.atomic():
|
|
|
|
motion.create_poll()
|
|
|
|
except WorkflowError as e:
|
|
|
|
raise ValidationError({'detail': e})
|
2015-12-07 12:40:30 +01:00
|
|
|
return Response({'detail': _('Vote created successfully.')})
|
2015-07-22 15:23:57 +02:00
|
|
|
|
|
|
|
|
|
|
|
class MotionPollViewSet(UpdateModelMixin, DestroyModelMixin, GenericViewSet):
|
|
|
|
"""
|
|
|
|
API endpoint for motion polls.
|
|
|
|
|
|
|
|
There are the following views: update and destroy.
|
|
|
|
"""
|
|
|
|
queryset = MotionPoll.objects.all()
|
|
|
|
serializer_class = MotionPollSerializer
|
|
|
|
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
Returns True if the user has required permissions.
|
|
|
|
"""
|
|
|
|
return (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_manage'))
|
|
|
|
|
2015-01-24 16:35:50 +01:00
|
|
|
|
2016-09-10 18:49:38 +02:00
|
|
|
class MotionChangeRecommendationViewSet(ModelViewSet):
|
|
|
|
"""
|
|
|
|
API endpoint for motion change recommendations.
|
|
|
|
|
|
|
|
There are the following views: metadata, list, retrieve, create,
|
|
|
|
partial_update, update and destroy.
|
|
|
|
"""
|
|
|
|
access_permissions = MotionChangeRecommendationAccessPermissions()
|
|
|
|
queryset = MotionChangeRecommendation.objects.all()
|
|
|
|
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
Returns True if the user has required permissions.
|
|
|
|
"""
|
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action == 'metadata':
|
|
|
|
result = self.request.user.has_perm('motions.can_see')
|
|
|
|
elif self.action in ('create', 'destroy', 'partial_update', 'update'):
|
|
|
|
result = self.request.user.has_perm('motions.can_manage')
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
class CategoryViewSet(ModelViewSet):
|
|
|
|
"""
|
|
|
|
API endpoint for categories.
|
|
|
|
|
2015-08-31 14:07:24 +02:00
|
|
|
There are the following views: metadata, list, retrieve, create,
|
2016-10-01 20:42:44 +02:00
|
|
|
partial_update, update, destroy and numbering.
|
2015-07-01 23:18:48 +02:00
|
|
|
"""
|
2016-02-11 22:58:32 +01:00
|
|
|
access_permissions = CategoryAccessPermissions()
|
2015-07-01 23:18:48 +02:00
|
|
|
queryset = Category.objects.all()
|
|
|
|
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
Returns True if the user has required permissions.
|
|
|
|
"""
|
2016-09-17 22:26:23 +02:00
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action == 'metadata':
|
2015-07-01 23:18:48 +02:00
|
|
|
result = self.request.user.has_perm('motions.can_see')
|
2016-07-13 01:39:28 +02:00
|
|
|
elif self.action in ('create', 'partial_update', 'update', 'destroy', 'numbering'):
|
2015-07-01 23:18:48 +02:00
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_manage'))
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
|
|
|
|
2016-07-13 01:39:28 +02:00
|
|
|
@detail_route(methods=['post'])
|
|
|
|
def numbering(self, request, pk=None):
|
|
|
|
"""
|
|
|
|
Special view endpoint to number all motions in this category.
|
|
|
|
|
|
|
|
Only managers can use this view.
|
2016-08-15 23:53:21 +02:00
|
|
|
|
|
|
|
Send POST {'motions': [<list of motion ids>]} to sort the given
|
|
|
|
motions in a special order. Ids of motions which do not belong to
|
|
|
|
the category are just ignored. Send just POST {} to sort all
|
|
|
|
motions in the category by id.
|
2016-07-13 01:39:28 +02:00
|
|
|
"""
|
|
|
|
category = self.get_object()
|
|
|
|
number = 0
|
|
|
|
if not category.prefix:
|
|
|
|
prefix = ''
|
|
|
|
else:
|
|
|
|
prefix = '%s ' % category.prefix
|
2016-08-15 23:53:21 +02:00
|
|
|
motions = category.motion_set.all()
|
|
|
|
motion_list = request.data.get('motions')
|
|
|
|
if motion_list:
|
|
|
|
motion_dict = {}
|
|
|
|
for motion in motions.filter(id__in=motion_list):
|
|
|
|
motion_dict[motion.pk] = motion
|
|
|
|
motions = [motion_dict[pk] for pk in motion_list]
|
2016-07-13 01:39:28 +02:00
|
|
|
|
2016-07-29 23:33:47 +02:00
|
|
|
try:
|
|
|
|
with transaction.atomic():
|
|
|
|
for motion in motions:
|
|
|
|
motion.identifier = None
|
|
|
|
motion.save()
|
|
|
|
|
|
|
|
for motion in motions:
|
|
|
|
if motion.is_amendment():
|
|
|
|
parent_identifier = motion.parent.identifier or ''
|
|
|
|
prefix = '%s %s ' % (parent_identifier, config['motions_amendments_prefix'])
|
|
|
|
number += 1
|
|
|
|
identifier = '%s%d' % (prefix, number)
|
|
|
|
motion.identifier = identifier
|
|
|
|
motion.identifier_number = number
|
|
|
|
motion.save()
|
|
|
|
except IntegrityError:
|
|
|
|
message = _('Error: At least one identifier of this category does '
|
|
|
|
'already exist in another category.')
|
|
|
|
response = Response({'detail': message}, status_code=400)
|
|
|
|
else:
|
|
|
|
message = _('All motions in category {category} numbered '
|
|
|
|
'successfully.').format(category=category)
|
|
|
|
response = Response({'detail': message})
|
|
|
|
return response
|
2016-07-13 01:39:28 +02:00
|
|
|
|
2016-10-01 20:42:44 +02:00
|
|
|
|
|
|
|
class MotionBlockViewSet(ModelViewSet):
|
|
|
|
"""
|
|
|
|
API endpoint for motion blocks.
|
|
|
|
|
|
|
|
There are the following views: metadata, list, retrieve, create,
|
|
|
|
partial_update, update and destroy.
|
|
|
|
"""
|
|
|
|
access_permissions = MotionBlockAccessPermissions()
|
|
|
|
queryset = MotionBlock.objects.all()
|
|
|
|
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
Returns True if the user has required permissions.
|
|
|
|
"""
|
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action == 'metadata':
|
|
|
|
result = self.request.user.has_perm('motions.can_see')
|
|
|
|
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_manage'))
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
|
|
|
|
2015-07-01 23:18:48 +02:00
|
|
|
|
|
|
|
class WorkflowViewSet(ModelViewSet):
|
|
|
|
"""
|
|
|
|
API endpoint for workflows.
|
|
|
|
|
2015-08-31 14:07:24 +02:00
|
|
|
There are the following views: metadata, list, retrieve, create,
|
|
|
|
partial_update, update and destroy.
|
2015-07-01 23:18:48 +02:00
|
|
|
"""
|
2016-02-11 22:58:32 +01:00
|
|
|
access_permissions = WorkflowAccessPermissions()
|
2015-07-01 23:18:48 +02:00
|
|
|
queryset = Workflow.objects.all()
|
|
|
|
|
|
|
|
def check_view_permissions(self):
|
|
|
|
"""
|
|
|
|
Returns True if the user has required permissions.
|
|
|
|
"""
|
2016-09-17 22:26:23 +02:00
|
|
|
if self.action in ('list', 'retrieve'):
|
|
|
|
result = self.get_access_permissions().check_permissions(self.request.user)
|
|
|
|
elif self.action == 'metadata':
|
2015-07-01 23:18:48 +02:00
|
|
|
result = self.request.user.has_perm('motions.can_see')
|
|
|
|
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
|
|
|
result = (self.request.user.has_perm('motions.can_see') and
|
|
|
|
self.request.user.has_perm('motions.can_manage'))
|
|
|
|
else:
|
|
|
|
result = False
|
|
|
|
return result
|
|
|
|
|
|
|
|
|
2016-09-13 11:54:30 +02:00
|
|
|
# Views to generate PDFs and for the DOCX template
|
2015-07-01 23:18:48 +02:00
|
|
|
|
2016-02-23 22:05:15 +01:00
|
|
|
class MotionPollPDF(PDFView):
|
2013-04-21 19:12:50 +02:00
|
|
|
"""
|
2015-06-16 10:37:23 +02:00
|
|
|
Generates a ballotpaper.
|
2013-04-21 19:12:50 +02:00
|
|
|
"""
|
|
|
|
|
2015-03-26 05:36:10 +01:00
|
|
|
required_permission = 'motions.can_manage'
|
2015-06-16 10:37:23 +02:00
|
|
|
top_space = 0
|
2013-02-01 12:51:54 +01:00
|
|
|
|
2016-02-23 22:05:15 +01:00
|
|
|
def get(self, request, *args, **kwargs):
|
|
|
|
self.poll = MotionPoll.objects.get(pk=self.kwargs['poll_pk'])
|
|
|
|
return super().get(request, *args, **kwargs)
|
2013-02-01 12:51:54 +01:00
|
|
|
|
2013-04-21 19:12:50 +02:00
|
|
|
def get_filename(self):
|
|
|
|
"""
|
|
|
|
Return the filename for the PDF.
|
|
|
|
"""
|
2016-02-23 22:05:15 +01:00
|
|
|
return u'%s_%s' % (_("Motion"), _("Vote"))
|
2013-04-21 19:12:50 +02:00
|
|
|
|
2013-04-24 15:07:39 +02:00
|
|
|
def get_template(self, buffer):
|
|
|
|
return SimpleDocTemplate(
|
|
|
|
buffer, topMargin=-6, bottomMargin=-6, leftMargin=0, rightMargin=0,
|
|
|
|
showBoundary=False)
|
|
|
|
|
|
|
|
def build_document(self, pdf_document, story):
|
|
|
|
pdf_document.build(story)
|
|
|
|
|
2013-04-21 19:12:50 +02:00
|
|
|
def append_to_pdf(self, pdf):
|
|
|
|
"""
|
|
|
|
Append PDF objects.
|
|
|
|
"""
|
2016-02-23 22:05:15 +01:00
|
|
|
motion_poll_to_pdf(pdf, self.poll)
|
2013-04-21 19:12:50 +02:00
|
|
|
|
|
|
|
|
2013-02-03 18:18:29 +01:00
|
|
|
class MotionPDFView(SingleObjectMixin, PDFView):
|
2013-06-01 12:36:42 +02:00
|
|
|
"""
|
2014-12-20 20:13:30 +01:00
|
|
|
Create the PDF for one or for all motions.
|
2013-02-05 18:46:46 +01:00
|
|
|
|
|
|
|
If self.print_all_motions is True, the view returns a PDF with all motions.
|
|
|
|
|
|
|
|
If self.print_all_motions is False, the view returns a PDF with only one
|
2013-06-01 12:36:42 +02:00
|
|
|
motion.
|
|
|
|
"""
|
2013-02-03 18:18:29 +01:00
|
|
|
model = Motion
|
|
|
|
top_space = 0
|
|
|
|
print_all_motions = False
|
|
|
|
|
2014-12-20 20:13:30 +01:00
|
|
|
def check_permission(self, request, *args, **kwargs):
|
2013-06-01 12:36:42 +02:00
|
|
|
"""
|
2014-12-20 20:13:30 +01:00
|
|
|
Checks if the requesting user has the permission to see the motion as
|
|
|
|
PDF.
|
2013-06-01 12:36:42 +02:00
|
|
|
"""
|
2014-12-20 20:13:30 +01:00
|
|
|
if self.print_all_motions:
|
2015-03-26 05:36:10 +01:00
|
|
|
is_allowed = request.user.has_perm('motions.can_see')
|
2014-12-20 20:13:30 +01:00
|
|
|
else:
|
|
|
|
is_allowed = self.get_object().get_allowed_actions(request.user)['see']
|
|
|
|
return is_allowed
|
|
|
|
|
2014-12-22 18:09:05 +01:00
|
|
|
def get_object(self, *args, **kwargs):
|
|
|
|
if self.print_all_motions:
|
|
|
|
obj = None
|
|
|
|
else:
|
2015-01-05 17:14:29 +01:00
|
|
|
obj = super().get_object(*args, **kwargs)
|
2014-12-22 18:09:05 +01:00
|
|
|
return obj
|
2013-02-03 18:18:29 +01:00
|
|
|
|
|
|
|
def get_filename(self):
|
2013-06-01 12:36:42 +02:00
|
|
|
"""
|
|
|
|
Return the filename for the PDF.
|
|
|
|
"""
|
2013-02-03 18:18:29 +01:00
|
|
|
if self.print_all_motions:
|
|
|
|
return _("Motions")
|
|
|
|
else:
|
2014-12-22 18:09:05 +01:00
|
|
|
if self.get_object().identifier:
|
|
|
|
suffix = self.get_object().identifier.replace(' ', '')
|
2013-06-14 20:24:48 +02:00
|
|
|
else:
|
2014-12-22 18:09:05 +01:00
|
|
|
suffix = self.get_object().title.replace(' ', '_')
|
2013-06-14 20:24:48 +02:00
|
|
|
suffix = slugify(suffix)
|
|
|
|
return '%s-%s' % (_("Motion"), suffix)
|
2013-02-03 18:18:29 +01:00
|
|
|
|
|
|
|
def append_to_pdf(self, pdf):
|
2013-06-01 12:36:42 +02:00
|
|
|
"""
|
|
|
|
Append PDF objects.
|
|
|
|
"""
|
2013-02-03 18:18:29 +01:00
|
|
|
if self.print_all_motions:
|
2014-12-20 20:13:30 +01:00
|
|
|
motions = []
|
|
|
|
for motion in Motion.objects.all():
|
|
|
|
if (not motion.state.required_permission_to_see or
|
|
|
|
self.request.user.has_perm(motion.state.required_permission_to_see)):
|
|
|
|
motions.append(motion)
|
|
|
|
motions_to_pdf(pdf, motions)
|
2013-02-03 18:18:29 +01:00
|
|
|
else:
|
2014-12-22 18:09:05 +01:00
|
|
|
motion_to_pdf(pdf, self.get_object())
|
2016-09-13 11:54:30 +02:00
|
|
|
|
|
|
|
|
|
|
|
class MotionDocxTemplateView(APIView):
|
|
|
|
"""
|
|
|
|
Returns the template for motions docx export
|
|
|
|
"""
|
|
|
|
http_method_names = ['get']
|
|
|
|
|
|
|
|
def get_context_data(self, **context):
|
|
|
|
with open(finders.find('templates/docx/motions.docx'), "rb") as file:
|
|
|
|
response = base64.b64encode(file.read())
|
|
|
|
return response
|