Added REST api for motion, mediafile and config app. Refactor REST api in other apps.
This commit is contained in:
parent
19fb7018af
commit
7238b8159a
@ -437,6 +437,6 @@ class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the item to this instance which is the root rest element.
|
Returns the item to this instance which is the root REST element.
|
||||||
"""
|
"""
|
||||||
return self.item
|
return self.item
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
from openslides.utils import rest_api
|
|
||||||
from rest_framework.reverse import reverse
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
from .models import Item, Speaker
|
from .models import Item, Speaker
|
||||||
|
|
||||||
|
|
||||||
class SpeakerSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class SpeakerSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for agenda.models.Speaker objects.
|
Serializer for agenda.models.Speaker objects.
|
||||||
"""
|
"""
|
||||||
@ -18,7 +19,7 @@ class SpeakerSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
|||||||
'weight')
|
'weight')
|
||||||
|
|
||||||
|
|
||||||
class RelatedItemRelatedField(rest_api.serializers.RelatedField):
|
class RelatedItemRelatedField(serializers.RelatedField):
|
||||||
"""
|
"""
|
||||||
A custom field to use for the `content_object` generic relationship.
|
A custom field to use for the `content_object` generic relationship.
|
||||||
"""
|
"""
|
||||||
@ -35,17 +36,37 @@ class RelatedItemRelatedField(rest_api.serializers.RelatedField):
|
|||||||
return reverse(view_name, kwargs={'pk': value.pk}, request=request)
|
return reverse(view_name, kwargs={'pk': value.pk}, request=request)
|
||||||
|
|
||||||
|
|
||||||
class ItemSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class ItemSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for agenda.models.Item objects.
|
Serializer for agenda.models.Item objects.
|
||||||
"""
|
"""
|
||||||
get_title = rest_api.serializers.CharField(read_only=True)
|
get_title = serializers.CharField(read_only=True)
|
||||||
get_title_supplement = rest_api.serializers.CharField(read_only=True)
|
get_title_supplement = serializers.CharField(read_only=True)
|
||||||
item_no = rest_api.serializers.CharField(read_only=True)
|
|
||||||
speaker_set = SpeakerSerializer(many=True, read_only=True)
|
|
||||||
tags = rest_api.serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='tag-detail')
|
|
||||||
content_object = RelatedItemRelatedField(read_only=True)
|
content_object = RelatedItemRelatedField(read_only=True)
|
||||||
|
item_no = serializers.CharField(read_only=True)
|
||||||
|
speaker_set = SpeakerSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Item
|
model = Item
|
||||||
exclude = ('content_type', 'object_id')
|
fields = (
|
||||||
|
'url',
|
||||||
|
'item_number',
|
||||||
|
'item_no',
|
||||||
|
'title',
|
||||||
|
'get_title',
|
||||||
|
'get_title_supplement',
|
||||||
|
'text',
|
||||||
|
'comment',
|
||||||
|
'closed',
|
||||||
|
'type',
|
||||||
|
'duration',
|
||||||
|
'speaker_set',
|
||||||
|
'speaker_list_closed',
|
||||||
|
'content_object',
|
||||||
|
'weight',
|
||||||
|
'lft',
|
||||||
|
'rght',
|
||||||
|
'tree_id',
|
||||||
|
'level',
|
||||||
|
'parent',
|
||||||
|
'tags',)
|
||||||
|
@ -23,9 +23,9 @@ from openslides.projector.api import (
|
|||||||
get_active_slide,
|
get_active_slide,
|
||||||
get_projector_overlays_js,
|
get_projector_overlays_js,
|
||||||
get_overlays)
|
get_overlays)
|
||||||
from openslides.utils import rest_api
|
|
||||||
from openslides.utils.exceptions import OpenSlidesError
|
from openslides.utils.exceptions import OpenSlidesError
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
AjaxMixin,
|
AjaxMixin,
|
||||||
@ -773,9 +773,9 @@ class ItemCSVImportView(CSVImportView):
|
|||||||
template_name = 'agenda/item_form_csv_import.html'
|
template_name = 'agenda/item_form_csv_import.html'
|
||||||
|
|
||||||
|
|
||||||
class ItemViewSet(rest_api.viewsets.ModelViewSet):
|
class ItemViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to retrieve, create, edit and delete agenda items.
|
API endpoint to list, retrieve, create, update and destroy agenda items.
|
||||||
"""
|
"""
|
||||||
model = Item
|
model = Item
|
||||||
serializer_class = ItemSerializer
|
serializer_class = ItemSerializer
|
||||||
@ -783,8 +783,9 @@ class ItemViewSet(rest_api.viewsets.ModelViewSet):
|
|||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
"""
|
"""
|
||||||
Calls self.permission_denied() if the requesting user has not the
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
permission to see and in case of create, update or destroy requests
|
permission to see the agenda and in case of create, update or destroy
|
||||||
the permission to manage and to see organizational items.
|
requests the permission to manage the agenda and to see organizational
|
||||||
|
items.
|
||||||
"""
|
"""
|
||||||
if (not request.user.has_perm('agenda.can_see_agenda') or
|
if (not request.user.has_perm('agenda.can_see_agenda') or
|
||||||
(self.action in ('create', 'update', 'destroy') and not
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
@ -802,7 +803,7 @@ class ItemViewSet(rest_api.viewsets.ModelViewSet):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""
|
"""
|
||||||
Filters organizational items if the user has no permission to see it.
|
Filters organizational items if the user has no permission to see them.
|
||||||
"""
|
"""
|
||||||
queryset = Item.objects.all()
|
queryset = Item.objects.all()
|
||||||
if not self.request.user.has_perm('agenda.can_see_orga_items'):
|
if not self.request.user.has_perm('agenda.can_see_orga_items'):
|
||||||
|
@ -36,7 +36,7 @@ class AssignmentCandidate(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the assignment to this instance which is the root rest element.
|
Returns the assignment to this instance which is the root REST element.
|
||||||
"""
|
"""
|
||||||
return self.assignment
|
return self.assignment
|
||||||
|
|
||||||
@ -274,7 +274,7 @@ class AssignmentVote(RESTModelMixin, BaseVote):
|
|||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the assignment to this instance which is the root rest element.
|
Returns the assignment to this instance which is the root REST element.
|
||||||
"""
|
"""
|
||||||
return self.option.poll.assignment
|
return self.option.poll.assignment
|
||||||
|
|
||||||
@ -289,7 +289,7 @@ class AssignmentOption(RESTModelMixin, BaseOption):
|
|||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the assignment to this instance which is the root rest element.
|
Returns the assignment to this instance which is the root REST element.
|
||||||
"""
|
"""
|
||||||
return self.poll.assignment
|
return self.poll.assignment
|
||||||
|
|
||||||
@ -348,6 +348,6 @@ class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
|
|||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the assignment to this instance which is the root rest element.
|
Returns the assignment to this instance which is the root REST element.
|
||||||
"""
|
"""
|
||||||
return self.assignment
|
return self.assignment
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
from openslides.utils import rest_api
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
from .models import (
|
from .models import (
|
||||||
models,
|
models,
|
||||||
@ -9,7 +9,7 @@ from .models import (
|
|||||||
AssignmentVote)
|
AssignmentVote)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentCandidateSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class AssignmentCandidateSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for assignment.models.AssignmentCandidate objects.
|
Serializer for assignment.models.AssignmentCandidate objects.
|
||||||
"""
|
"""
|
||||||
@ -22,7 +22,7 @@ class AssignmentCandidateSerializer(rest_api.serializers.HyperlinkedModelSeriali
|
|||||||
'blocked')
|
'blocked')
|
||||||
|
|
||||||
|
|
||||||
class AssignmentVoteSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class AssignmentVoteSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for assignment.models.AssignmentVote objects.
|
Serializer for assignment.models.AssignmentVote objects.
|
||||||
"""
|
"""
|
||||||
@ -33,7 +33,7 @@ class AssignmentVoteSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
|||||||
'value')
|
'value')
|
||||||
|
|
||||||
|
|
||||||
class AssignmentOptionSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class AssignmentOptionSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for assignment.models.AssignmentOption objects.
|
Serializer for assignment.models.AssignmentOption objects.
|
||||||
"""
|
"""
|
||||||
@ -46,9 +46,9 @@ class AssignmentOptionSerializer(rest_api.serializers.HyperlinkedModelSerializer
|
|||||||
'assignmentvote_set')
|
'assignmentvote_set')
|
||||||
|
|
||||||
|
|
||||||
class FilterPollListSerializer(rest_api.serializers.ListSerializer):
|
class FilterPollListSerializer(serializers.ListSerializer):
|
||||||
"""
|
"""
|
||||||
Customized serilizer to filter polls and exclude unpublished ones.
|
Customized serializer to filter polls (exclude unpublished).
|
||||||
"""
|
"""
|
||||||
def to_representation(self, data):
|
def to_representation(self, data):
|
||||||
"""
|
"""
|
||||||
@ -62,7 +62,7 @@ class FilterPollListSerializer(rest_api.serializers.ListSerializer):
|
|||||||
return [self.child.to_representation(item) for item in iterable]
|
return [self.child.to_representation(item) for item in iterable]
|
||||||
|
|
||||||
|
|
||||||
class AssignmentAllPollSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class AssignmentAllPollSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for assignment.models.AssignmentPoll objects.
|
Serializer for assignment.models.AssignmentPoll objects.
|
||||||
|
|
||||||
@ -103,25 +103,25 @@ class AssignmentShortPollSerializer(AssignmentAllPollSerializer):
|
|||||||
'votescast')
|
'votescast')
|
||||||
|
|
||||||
|
|
||||||
class AssignmentFullSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class AssignmentFullSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for assignment.models.Assignment objects. With all polls.
|
Serializer for assignment.models.Assignment objects. With all polls.
|
||||||
"""
|
"""
|
||||||
assignmentcandidate_set = AssignmentCandidateSerializer(many=True, read_only=True)
|
assignmentcandidate_set = AssignmentCandidateSerializer(many=True, read_only=True)
|
||||||
poll_set = AssignmentAllPollSerializer(many=True, read_only=True)
|
poll_set = AssignmentAllPollSerializer(many=True, read_only=True)
|
||||||
tags = rest_api.serializers.HyperlinkedRelatedField(many=True, read_only=True, view_name='tag-detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Assignment
|
model = Assignment
|
||||||
fields = (
|
fields = (
|
||||||
|
'url',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'posts',
|
'posts',
|
||||||
'poll_description_default',
|
|
||||||
'status',
|
'status',
|
||||||
'assignmentcandidate_set',
|
'assignmentcandidate_set',
|
||||||
|
'poll_description_default',
|
||||||
'poll_set',
|
'poll_set',
|
||||||
'tags')
|
'tags',)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentShortSerializer(AssignmentFullSerializer):
|
class AssignmentShortSerializer(AssignmentFullSerializer):
|
||||||
@ -133,11 +133,12 @@ class AssignmentShortSerializer(AssignmentFullSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = Assignment
|
model = Assignment
|
||||||
fields = (
|
fields = (
|
||||||
|
'url',
|
||||||
'name',
|
'name',
|
||||||
'description',
|
'description',
|
||||||
'posts',
|
'posts',
|
||||||
'poll_description_default',
|
|
||||||
'status',
|
'status',
|
||||||
'assignmentcandidate_set',
|
'assignmentcandidate_set',
|
||||||
|
'poll_description_default',
|
||||||
'poll_set',
|
'poll_set',
|
||||||
'tags')
|
'tags',)
|
||||||
|
@ -14,8 +14,8 @@ from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelate
|
|||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.users.models import Group, User # TODO: remove this
|
from openslides.users.models import Group, User # TODO: remove this
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
from openslides.utils import rest_api
|
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.utils import html_strong
|
from openslides.utils.utils import html_strong
|
||||||
from openslides.utils.views import (CreateView, DeleteView, DetailView,
|
from openslides.utils.views import (CreateView, DeleteView, DetailView,
|
||||||
ListView, PDFView, PermissionMixin,
|
ListView, PDFView, PermissionMixin,
|
||||||
@ -190,9 +190,9 @@ class AssignmentRunOtherDeleteView(SingleObjectMixin, QuestionView):
|
|||||||
self.is_blocked = self.get_object().is_blocked(self.person)
|
self.is_blocked = self.get_object().is_blocked(self.person)
|
||||||
|
|
||||||
|
|
||||||
class AssignmentViewSet(rest_api.viewsets.ModelViewSet):
|
class AssignmentViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to retrieve, create, edit and delete assignments.
|
API endpoint to list, retrieve, create, update and destroy assignments.
|
||||||
"""
|
"""
|
||||||
model = Assignment
|
model = Assignment
|
||||||
queryset = Assignment.objects.all()
|
queryset = Assignment.objects.all()
|
||||||
@ -200,8 +200,8 @@ class AssignmentViewSet(rest_api.viewsets.ModelViewSet):
|
|||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
"""
|
"""
|
||||||
Calls self.permission_denied() if the requesting user has not the
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
permission to see and in case of create, update or destroy requests
|
permission to see assignments and in case of create, update or destroy
|
||||||
the permission to manage and to see organizational items.
|
requests the permission to manage assignments.
|
||||||
"""
|
"""
|
||||||
if (not request.user.has_perm('assignment.can_see_assignment') or
|
if (not request.user.has_perm('assignment.can_see_assignment') or
|
||||||
(self.action in ('create', 'update', 'destroy') and not
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
@ -39,6 +39,14 @@ class ConfigHandler(object):
|
|||||||
config_variable.on_change()
|
config_variable.on_change()
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def get_data_as_dict(self):
|
||||||
|
"""
|
||||||
|
Returns all config variables as dictionary retrieved from the config cache.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, '_cache'):
|
||||||
|
self.setup_cache()
|
||||||
|
return self._cache
|
||||||
|
|
||||||
def get_default(self, key):
|
def get_default(self, key):
|
||||||
"""
|
"""
|
||||||
Returns the default value for 'key'.
|
Returns the default value for 'key'.
|
||||||
|
@ -9,3 +9,10 @@ class ConfigAppConfig(AppConfig):
|
|||||||
# Load main menu entry.
|
# Load main menu entry.
|
||||||
# Do this by just importing all from this file.
|
# Do this by just importing all from this file.
|
||||||
from . import main_menu # noqa
|
from . import main_menu # noqa
|
||||||
|
|
||||||
|
# Import all required stuff.
|
||||||
|
from openslides.utils.rest_api import router
|
||||||
|
from .views import ConfigViewSet
|
||||||
|
|
||||||
|
# Register viewsets.
|
||||||
|
router.register('config/config', ConfigViewSet, 'config')
|
||||||
|
@ -3,6 +3,7 @@ from django.contrib import messages
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
|
from openslides.utils.rest_api import response, viewsets
|
||||||
from openslides.utils.views import FormView
|
from openslides.utils.views import FormView
|
||||||
|
|
||||||
from .api import config
|
from .api import config
|
||||||
@ -100,3 +101,22 @@ class ConfigView(FormView):
|
|||||||
config[key] = form.cleaned_data[key]
|
config[key] = form.cleaned_data[key]
|
||||||
messages.success(self.request, _('%s settings successfully saved.') % _(self.config_collection.title))
|
messages.success(self.request, _('%s settings successfully saved.') % _(self.config_collection.title))
|
||||||
return super(ConfigView, self).form_valid(form)
|
return super(ConfigView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigViewSet(viewsets.ViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list and update the config.
|
||||||
|
"""
|
||||||
|
def list(self, request):
|
||||||
|
"""
|
||||||
|
Lists als config variables. Everybody can see this.
|
||||||
|
"""
|
||||||
|
# TODO: Check if we need permission check here.
|
||||||
|
return response.Response(config.get_data_as_dict())
|
||||||
|
|
||||||
|
def update(self, request, pk=None):
|
||||||
|
if not request.user.has_perm('config.can_manage'):
|
||||||
|
self.permission_denied(request)
|
||||||
|
else:
|
||||||
|
# TODO: Implement update method
|
||||||
|
self.permission_denied(request)
|
||||||
|
@ -30,7 +30,11 @@ class CoreAppConfig(AppConfig):
|
|||||||
router.register('core/customslide', CustomSlideViewSet)
|
router.register('core/customslide', CustomSlideViewSet)
|
||||||
router.register('core/tag', TagViewSet)
|
router.register('core/tag', TagViewSet)
|
||||||
|
|
||||||
# Update data when any model of any installed app is saved or deleted
|
# Update data when any model of any installed app is saved or deleted.
|
||||||
signals.post_save.connect(inform_changed_data_receiver, dispatch_uid='inform_changed_data_receiver')
|
# TODO: Test if the m2m_changed signal is also needed.
|
||||||
signals.post_delete.connect(inform_changed_data_receiver, dispatch_uid='inform_changed_data_receiver')
|
signals.post_save.connect(
|
||||||
# TODO: test if the m2m_changed signal is also needed
|
inform_changed_data_receiver,
|
||||||
|
dispatch_uid='inform_changed_data_receiver')
|
||||||
|
signals.post_delete.connect(
|
||||||
|
inform_changed_data_receiver,
|
||||||
|
dispatch_uid='inform_changed_data_receiver')
|
||||||
|
@ -43,7 +43,7 @@ class CustomSlide(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
elif link == 'delete':
|
elif link == 'delete':
|
||||||
url = reverse('customslide_delete', args=[str(self.pk)])
|
url = reverse('customslide_delete', args=[str(self.pk)])
|
||||||
else:
|
else:
|
||||||
url = super(CustomSlide, self).get_absolute_url(link)
|
url = super().get_absolute_url(link)
|
||||||
return url
|
return url
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,19 +1,21 @@
|
|||||||
from openslides.utils import rest_api
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
from .models import CustomSlide, Tag
|
from .models import CustomSlide, Tag
|
||||||
|
|
||||||
|
|
||||||
class CustomSlideSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class CustomSlideSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for core.models.CustomSlide objects.
|
Serializer for core.models.CustomSlide objects.
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomSlide
|
model = CustomSlide
|
||||||
|
fields = ('url', 'title', 'text', 'weight',)
|
||||||
|
|
||||||
|
|
||||||
class TagSerializer(rest_api.serializers.HyperlinkedModelSerializer):
|
class TagSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for core.models.Tag objects.
|
Serializer for core.models.Tag objects.
|
||||||
"""
|
"""
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Tag
|
model = Tag
|
||||||
|
fields = ('url', 'name',)
|
||||||
|
@ -12,9 +12,9 @@ from haystack.views import SearchView as _SearchView
|
|||||||
from openslides import get_version as get_openslides_version
|
from openslides import get_version as get_openslides_version
|
||||||
from openslides import get_git_commit_id, RELEASE
|
from openslides import get_git_commit_id, RELEASE
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.utils import rest_api
|
|
||||||
from openslides.utils import views as utils_views
|
from openslides.utils import views as utils_views
|
||||||
from openslides.utils.plugins import get_plugin_description, get_plugin_verbose_name, get_plugin_version
|
from openslides.utils.plugins import get_plugin_description, get_plugin_verbose_name, get_plugin_version
|
||||||
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.signals import template_manipulation
|
from openslides.utils.signals import template_manipulation
|
||||||
from openslides.utils.widgets import Widget
|
from openslides.utils.widgets import Widget
|
||||||
|
|
||||||
@ -218,9 +218,9 @@ class CustomSlideDeleteView(CustomSlideViewMixin, utils_views.DeleteView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CustomSlideViewSet(rest_api.viewsets.ModelViewSet):
|
class CustomSlideViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to retrieve, create, update and delete custom slides.
|
API endpoint to list, retrieve, create, update and destroy custom slides.
|
||||||
"""
|
"""
|
||||||
model = CustomSlide
|
model = CustomSlide
|
||||||
queryset = CustomSlide.objects.all()
|
queryset = CustomSlide.objects.all()
|
||||||
@ -229,7 +229,7 @@ class CustomSlideViewSet(rest_api.viewsets.ModelViewSet):
|
|||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
"""
|
"""
|
||||||
Calls self.permission_denied() if the requesting user has not the
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
permission to manage.
|
permission to manage projector.
|
||||||
"""
|
"""
|
||||||
if not request.user.has_perm('core.can_manage_projector'):
|
if not request.user.has_perm('core.can_manage_projector'):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
@ -313,9 +313,9 @@ class TagListView(utils_views.AjaxMixin, utils_views.ListView):
|
|||||||
**context)
|
**context)
|
||||||
|
|
||||||
|
|
||||||
class TagViewSet(rest_api.viewsets.ModelViewSet):
|
class TagViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to retrieve, create, edit and delete tags.
|
API endpoint to list, retrieve, create, update and destroy tags.
|
||||||
"""
|
"""
|
||||||
model = Tag
|
model = Tag
|
||||||
queryset = Tag.objects.all()
|
queryset = Tag.objects.all()
|
||||||
@ -324,7 +324,7 @@ class TagViewSet(rest_api.viewsets.ModelViewSet):
|
|||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
"""
|
"""
|
||||||
Calls self.permission_denied() if the requesting user has not the
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
permission to manage and it's a create, update or detroy request.
|
permission to manage tags and it is a create, update or detroy request.
|
||||||
"""
|
"""
|
||||||
if (self.action in ('create', 'update', 'destroy')
|
if (self.action in ('create', 'update', 'destroy')
|
||||||
and not request.user.has_perm('core.can_manage_tags')):
|
and not request.user.has_perm('core.can_manage_tags')):
|
||||||
|
@ -12,9 +12,11 @@ class MediafileAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from openslides.projector.api import register_slide
|
from openslides.projector.api import register_slide
|
||||||
|
from openslides.utils.rest_api import router
|
||||||
from openslides.utils.signals import template_manipulation
|
from openslides.utils.signals import template_manipulation
|
||||||
from .slides import mediafile_presentation_as_slide
|
from .slides import mediafile_presentation_as_slide
|
||||||
from .template import add_mediafile_stylesheets
|
from .template import add_mediafile_stylesheets
|
||||||
|
from .views import MediafileViewSet
|
||||||
|
|
||||||
# Connect template signal.
|
# Connect template signal.
|
||||||
template_manipulation.connect(add_mediafile_stylesheets, dispatch_uid='add_mediafile_stylesheets')
|
template_manipulation.connect(add_mediafile_stylesheets, dispatch_uid='add_mediafile_stylesheets')
|
||||||
@ -22,3 +24,6 @@ class MediafileAppConfig(AppConfig):
|
|||||||
# Register slides.
|
# Register slides.
|
||||||
Mediafile = self.get_model('Mediafile')
|
Mediafile = self.get_model('Mediafile')
|
||||||
register_slide('mediafile', mediafile_presentation_as_slide, Mediafile)
|
register_slide('mediafile', mediafile_presentation_as_slide, Mediafile)
|
||||||
|
|
||||||
|
# Register viewsets.
|
||||||
|
router.register('mediafile/mediafile', MediafileViewSet)
|
||||||
|
@ -7,10 +7,11 @@ from django.utils.translation import ugettext_lazy, ugettext_noop
|
|||||||
|
|
||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
|
from openslides.utils.rest_api import RESTModelMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
|
||||||
|
|
||||||
class Mediafile(SlideMixin, AbsoluteUrlMixin, models.Model):
|
class Mediafile(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
Class for uploaded files which can be delivered under a certain url.
|
Class for uploaded files which can be delivered under a certain url.
|
||||||
"""
|
"""
|
||||||
|
16
openslides/mediafile/serializers.py
Normal file
16
openslides/mediafile/serializers.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
|
from .models import Mediafile
|
||||||
|
|
||||||
|
|
||||||
|
class MediafileSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for mediafile.models.Mediafile objects.
|
||||||
|
"""
|
||||||
|
filesize = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Mediafile
|
||||||
|
|
||||||
|
def get_filesize(self, mediafile):
|
||||||
|
return mediafile.get_filesize()
|
@ -2,11 +2,13 @@ from django.http import HttpResponse
|
|||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
|
from openslides.utils.views import (AjaxView, CreateView, DeleteView, RedirectView, ListView,
|
||||||
UpdateView)
|
UpdateView)
|
||||||
|
|
||||||
from .forms import MediafileManagerForm, MediafileNormalUserForm
|
from .forms import MediafileManagerForm, MediafileNormalUserForm
|
||||||
from .models import Mediafile
|
from .models import Mediafile
|
||||||
|
from .serializers import MediafileSerializer
|
||||||
|
|
||||||
|
|
||||||
class MediafileListView(ListView):
|
class MediafileListView(ListView):
|
||||||
@ -198,3 +200,26 @@ class PdfToggleFullscreenView(RedirectView):
|
|||||||
def get_ajax_context(self, *args, **kwargs):
|
def get_ajax_context(self, *args, **kwargs):
|
||||||
config['pdf_fullscreen'] = not config['pdf_fullscreen']
|
config['pdf_fullscreen'] = not config['pdf_fullscreen']
|
||||||
return {'fullscreen': config['pdf_fullscreen']}
|
return {'fullscreen': config['pdf_fullscreen']}
|
||||||
|
|
||||||
|
|
||||||
|
class MediafileViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list, retrieve, create, update and destroy mediafile
|
||||||
|
objects.
|
||||||
|
"""
|
||||||
|
model = Mediafile
|
||||||
|
queryset = Mediafile.objects.all()
|
||||||
|
serializer_class = MediafileSerializer
|
||||||
|
|
||||||
|
def check_permissions(self, request):
|
||||||
|
"""
|
||||||
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
|
permission to see mediafile objects and in case of create, update or
|
||||||
|
destroy requests the permission to manage mediafile objects.
|
||||||
|
"""
|
||||||
|
# TODO: Use mediafile.can_upload permission to create and update some
|
||||||
|
# objects but restricted concerning the uploader.
|
||||||
|
if (not request.user.has_perm('mediafile.can_see') or
|
||||||
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
request.user.has_perm('mediafile.can_manage'))):
|
||||||
|
self.permission_denied(request)
|
||||||
|
@ -13,8 +13,10 @@ class MotionAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from openslides.config.signals import config_signal
|
from openslides.config.signals import config_signal
|
||||||
|
from openslides.utils.rest_api import router
|
||||||
from openslides.projector.api import register_slide_model
|
from openslides.projector.api import register_slide_model
|
||||||
from .signals import create_builtin_workflows, setup_motion_config
|
from .signals import create_builtin_workflows, setup_motion_config
|
||||||
|
from .views import CategoryViewSet, MotionViewSet, WorkflowViewSet
|
||||||
|
|
||||||
# Connect signals.
|
# Connect signals.
|
||||||
config_signal.connect(setup_motion_config, dispatch_uid='setup_motion_config')
|
config_signal.connect(setup_motion_config, dispatch_uid='setup_motion_config')
|
||||||
@ -25,3 +27,8 @@ class MotionAppConfig(AppConfig):
|
|||||||
MotionPoll = self.get_model('MotionPoll')
|
MotionPoll = self.get_model('MotionPoll')
|
||||||
register_slide_model(Motion, 'motion/slide.html')
|
register_slide_model(Motion, 'motion/slide.html')
|
||||||
register_slide_model(MotionPoll, 'motion/motionpoll_slide.html')
|
register_slide_model(MotionPoll, 'motion/motionpoll_slide.html')
|
||||||
|
|
||||||
|
# Register viewsets.
|
||||||
|
router.register('motion/category', CategoryViewSet)
|
||||||
|
router.register('motion/motion', MotionViewSet)
|
||||||
|
router.register('motion/workflow', WorkflowViewSet)
|
||||||
|
@ -12,12 +12,13 @@ from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefau
|
|||||||
from openslides.projector.models import SlideMixin
|
from openslides.projector.models import SlideMixin
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
from openslides.utils.models import AbsoluteUrlMixin
|
from openslides.utils.models import AbsoluteUrlMixin
|
||||||
|
from openslides.utils.rest_api import RESTModelMixin
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
|
||||||
from .exceptions import WorkflowError
|
from .exceptions import WorkflowError
|
||||||
|
|
||||||
|
|
||||||
class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
class Motion(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
The Motion Class.
|
The Motion Class.
|
||||||
|
|
||||||
@ -57,7 +58,7 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
Counts the number of the motion in one category.
|
Counts the number of the motion in one category.
|
||||||
|
|
||||||
Needed to find the next free motion-identifier.
|
Needed to find the next free motion identifier.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
category = models.ForeignKey('Category', null=True, blank=True)
|
category = models.ForeignKey('Category', null=True, blank=True)
|
||||||
@ -553,7 +554,7 @@ class Motion(SlideMixin, AbsoluteUrlMixin, models.Model):
|
|||||||
return config['motion_amendments_enabled'] and self.parent is not None
|
return config['motion_amendments_enabled'] and self.parent is not None
|
||||||
|
|
||||||
|
|
||||||
class MotionVersion(AbsoluteUrlMixin, models.Model):
|
class MotionVersion(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
A MotionVersion object saves some date of the motion.
|
A MotionVersion object saves some date of the motion.
|
||||||
"""
|
"""
|
||||||
@ -611,8 +612,14 @@ class MotionVersion(AbsoluteUrlMixin, models.Model):
|
|||||||
"""Return True, if the version is the active version of a motion. Else: False."""
|
"""Return True, if the version is the active version of a motion. Else: False."""
|
||||||
return self.active_version.exists()
|
return self.active_version.exists()
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.motion
|
||||||
|
|
||||||
class MotionSubmitter(models.Model):
|
|
||||||
|
class MotionSubmitter(RESTModelMixin, models.Model):
|
||||||
"""Save the submitter of a Motion."""
|
"""Save the submitter of a Motion."""
|
||||||
|
|
||||||
motion = models.ForeignKey('Motion', related_name="submitter")
|
motion = models.ForeignKey('Motion', related_name="submitter")
|
||||||
@ -625,8 +632,14 @@ class MotionSubmitter(models.Model):
|
|||||||
"""Return the name of the submitter as string."""
|
"""Return the name of the submitter as string."""
|
||||||
return str(self.person)
|
return str(self.person)
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.motion
|
||||||
|
|
||||||
class MotionSupporter(models.Model):
|
|
||||||
|
class MotionSupporter(RESTModelMixin, models.Model):
|
||||||
"""Save the submitter of a Motion."""
|
"""Save the submitter of a Motion."""
|
||||||
|
|
||||||
motion = models.ForeignKey('Motion', related_name="supporter")
|
motion = models.ForeignKey('Motion', related_name="supporter")
|
||||||
@ -639,8 +652,14 @@ class MotionSupporter(models.Model):
|
|||||||
"""Return the name of the supporter as string."""
|
"""Return the name of the supporter as string."""
|
||||||
return str(self.person)
|
return str(self.person)
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.motion
|
||||||
|
|
||||||
class Category(AbsoluteUrlMixin, models.Model):
|
|
||||||
|
class Category(RESTModelMixin, AbsoluteUrlMixin, models.Model):
|
||||||
name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
|
name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
|
||||||
"""Name of the category."""
|
"""Name of the category."""
|
||||||
|
|
||||||
@ -666,7 +685,7 @@ class Category(AbsoluteUrlMixin, models.Model):
|
|||||||
ordering = ['prefix']
|
ordering = ['prefix']
|
||||||
|
|
||||||
|
|
||||||
class MotionLog(models.Model):
|
class MotionLog(RESTModelMixin, models.Model):
|
||||||
"""Save a logmessage for a motion."""
|
"""Save a logmessage for a motion."""
|
||||||
|
|
||||||
motion = models.ForeignKey(Motion, related_name='log_messages')
|
motion = models.ForeignKey(Motion, related_name='log_messages')
|
||||||
@ -674,7 +693,7 @@ class MotionLog(models.Model):
|
|||||||
|
|
||||||
message_list = JSONField()
|
message_list = JSONField()
|
||||||
"""
|
"""
|
||||||
The log message. It should be a list of strings in english.
|
The log message. It should be a list of strings in English.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
person = models.ForeignKey(User, null=True)
|
person = models.ForeignKey(User, null=True)
|
||||||
@ -697,8 +716,14 @@ class MotionLog(models.Model):
|
|||||||
'person': self.person}
|
'person': self.person}
|
||||||
return time_and_messages
|
return time_and_messages
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.motion
|
||||||
|
|
||||||
class MotionVote(BaseVote):
|
|
||||||
|
class MotionVote(RESTModelMixin, BaseVote):
|
||||||
"""Saves the votes for a MotionPoll.
|
"""Saves the votes for a MotionPoll.
|
||||||
|
|
||||||
There should allways be three MotionVote objects for each poll,
|
There should allways be three MotionVote objects for each poll,
|
||||||
@ -707,8 +732,14 @@ class MotionVote(BaseVote):
|
|||||||
option = models.ForeignKey('MotionOption')
|
option = models.ForeignKey('MotionOption')
|
||||||
"""The option object, to witch the vote belongs."""
|
"""The option object, to witch the vote belongs."""
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.option.poll.motion
|
||||||
|
|
||||||
class MotionOption(BaseOption):
|
|
||||||
|
class MotionOption(RESTModelMixin, BaseOption):
|
||||||
"""Links between the MotionPollClass and the MotionVoteClass.
|
"""Links between the MotionPollClass and the MotionVoteClass.
|
||||||
|
|
||||||
There should be one MotionOption object for each poll."""
|
There should be one MotionOption object for each poll."""
|
||||||
@ -719,8 +750,14 @@ class MotionOption(BaseOption):
|
|||||||
vote_class = MotionVote
|
vote_class = MotionVote
|
||||||
"""The VoteClass, to witch this Class links."""
|
"""The VoteClass, to witch this Class links."""
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.poll.motion
|
||||||
|
|
||||||
class MotionPoll(SlideMixin, CollectDefaultVotesMixin,
|
|
||||||
|
class MotionPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin,
|
||||||
AbsoluteUrlMixin, BasePoll):
|
AbsoluteUrlMixin, BasePoll):
|
||||||
"""The Class to saves the vote result for a motion poll."""
|
"""The Class to saves the vote result for a motion poll."""
|
||||||
|
|
||||||
@ -778,8 +815,14 @@ class MotionPoll(SlideMixin, CollectDefaultVotesMixin,
|
|||||||
def get_slide_context(self, **context):
|
def get_slide_context(self, **context):
|
||||||
return super(MotionPoll, self).get_slide_context(poll=self)
|
return super(MotionPoll, self).get_slide_context(poll=self)
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the motion to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.motion
|
||||||
|
|
||||||
class State(models.Model):
|
|
||||||
|
class State(RESTModelMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
Defines a state for a motion.
|
Defines a state for a motion.
|
||||||
|
|
||||||
@ -867,8 +910,14 @@ class State(models.Model):
|
|||||||
if not state.workflow == self.workflow:
|
if not state.workflow == self.workflow:
|
||||||
raise WorkflowError('%s can not be next state of %s because it does not belong to the same workflow.' % (state, self))
|
raise WorkflowError('%s can not be next state of %s because it does not belong to the same workflow.' % (state, self))
|
||||||
|
|
||||||
|
def get_root_rest_element(self):
|
||||||
|
"""
|
||||||
|
Returns the workflow to this instance which is the root REST element.
|
||||||
|
"""
|
||||||
|
return self.workflow
|
||||||
|
|
||||||
class Workflow(models.Model):
|
|
||||||
|
class Workflow(RESTModelMixin, models.Model):
|
||||||
"""Defines a workflow for a motion."""
|
"""Defines a workflow for a motion."""
|
||||||
|
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
|
180
openslides/motion/serializers.py
Normal file
180
openslides/motion/serializers.py
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
|
from .models import (
|
||||||
|
Category,
|
||||||
|
Motion,
|
||||||
|
MotionLog,
|
||||||
|
MotionOption,
|
||||||
|
MotionPoll,
|
||||||
|
MotionSubmitter,
|
||||||
|
MotionSupporter,
|
||||||
|
MotionVersion,
|
||||||
|
MotionVote,
|
||||||
|
State,
|
||||||
|
Workflow,)
|
||||||
|
|
||||||
|
|
||||||
|
class CategorySerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.Category objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = Category
|
||||||
|
fields = ('url', 'name', 'prefix',)
|
||||||
|
|
||||||
|
|
||||||
|
class StateSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.State objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = State
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'action_word',
|
||||||
|
'icon',
|
||||||
|
'required_permission_to_see',
|
||||||
|
'allow_support',
|
||||||
|
'allow_create_poll',
|
||||||
|
'allow_submitter_edit',
|
||||||
|
'versioning',
|
||||||
|
'leave_old_version_active',
|
||||||
|
'dont_set_identifier',
|
||||||
|
'next_states',)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.Workflow objects.
|
||||||
|
"""
|
||||||
|
state_set = StateSerializer(many=True, read_only=True)
|
||||||
|
first_state = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Workflow
|
||||||
|
fields = ('url', 'name', 'state_set', 'first_state',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionSubmitterSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionSubmitter objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MotionSubmitter
|
||||||
|
fields = ('person',) # TODO: Rename this to 'user', see #1348
|
||||||
|
|
||||||
|
|
||||||
|
class MotionSupporterSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionSupporter objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MotionSupporter
|
||||||
|
fields = ('person',) # TODO: Rename this to 'user', see #1348
|
||||||
|
|
||||||
|
|
||||||
|
class MotionLogSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionLog objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MotionLog
|
||||||
|
fields = ('message_list', 'person', 'time',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionVoteSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionVote objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MotionVote
|
||||||
|
fields = ('value', 'weight',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionOptionSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionOption objects.
|
||||||
|
"""
|
||||||
|
motionvote_set = MotionVoteSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MotionOption
|
||||||
|
fields = ('motionvote_set',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionPollSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionPoll objects.
|
||||||
|
"""
|
||||||
|
motionoption_set = MotionOptionSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = MotionPoll
|
||||||
|
fields = (
|
||||||
|
'poll_number',
|
||||||
|
'motionoption_set',
|
||||||
|
'votesvalid',
|
||||||
|
'votesinvalid',
|
||||||
|
'votescast',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionVersionSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.MotionVersion objects.
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
model = MotionVersion
|
||||||
|
fields = (
|
||||||
|
'id',
|
||||||
|
'version_number',
|
||||||
|
'creation_time',
|
||||||
|
'title',
|
||||||
|
'text',
|
||||||
|
'reason',)
|
||||||
|
|
||||||
|
|
||||||
|
class MotionSerializer(serializers.HyperlinkedModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for motion.models.Motion objects.
|
||||||
|
"""
|
||||||
|
versions = MotionVersionSerializer(many=True, read_only=True)
|
||||||
|
active_version = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
|
submitter = MotionSubmitterSerializer(many=True, read_only=True)
|
||||||
|
supporter = MotionSupporterSerializer(many=True, read_only=True)
|
||||||
|
state = StateSerializer(read_only=True)
|
||||||
|
workflow = serializers.SerializerMethodField()
|
||||||
|
polls = MotionPollSerializer(many=True, read_only=True)
|
||||||
|
log_messages = MotionLogSerializer(many=True, read_only=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Motion
|
||||||
|
fields = (
|
||||||
|
'url',
|
||||||
|
'identifier',
|
||||||
|
'identifier_number',
|
||||||
|
'parent',
|
||||||
|
'category',
|
||||||
|
'tags',
|
||||||
|
'versions',
|
||||||
|
'active_version',
|
||||||
|
'submitter',
|
||||||
|
'supporter',
|
||||||
|
'state',
|
||||||
|
'workflow',
|
||||||
|
'attachments',
|
||||||
|
'polls',
|
||||||
|
'log_messages',)
|
||||||
|
|
||||||
|
def get_workflow(self, motion):
|
||||||
|
"""
|
||||||
|
Returns the hyperlink to the workflow of the motion.
|
||||||
|
"""
|
||||||
|
request = self.context.get('request', None)
|
||||||
|
assert request is not None, (
|
||||||
|
"`%s` requires the request in the serializer"
|
||||||
|
" context. Add `context={'request': request}` when instantiating "
|
||||||
|
"the serializer." % self.__class__.__name__)
|
||||||
|
return reverse('workflow-detail', kwargs={'pk': motion.state.workflow.pk}, request=request)
|
@ -10,6 +10,7 @@ from django.shortcuts import get_object_or_404
|
|||||||
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
from openslides.agenda.views import CreateRelatedAgendaItemView as _CreateRelatedAgendaItemView
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.poll.views import PollFormView
|
from openslides.poll.views import PollFormView
|
||||||
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.utils import html_strong, htmldiff
|
from openslides.utils.utils import html_strong, htmldiff
|
||||||
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView,
|
from openslides.utils.views import (CreateView, CSVImportView, DeleteView, DetailView,
|
||||||
ListView, PDFView, QuestionView,
|
ListView, PDFView, QuestionView,
|
||||||
@ -21,8 +22,9 @@ from .forms import (BaseMotionForm, MotionCategoryMixin,
|
|||||||
MotionCSVImportForm, MotionSubmitterMixin,
|
MotionCSVImportForm, MotionSubmitterMixin,
|
||||||
MotionSupporterMixin, MotionWorkflowMixin)
|
MotionSupporterMixin, MotionWorkflowMixin)
|
||||||
from .models import (Category, Motion, MotionPoll, MotionSubmitter,
|
from .models import (Category, Motion, MotionPoll, MotionSubmitter,
|
||||||
MotionSupporter, MotionVersion, State)
|
MotionSupporter, MotionVersion, State, Workflow)
|
||||||
from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf
|
from .pdf import motion_poll_to_pdf, motion_to_pdf, motions_to_pdf
|
||||||
|
from .serializers import CategorySerializer, MotionSerializer, WorkflowSerializer
|
||||||
|
|
||||||
|
|
||||||
class MotionListView(ListView):
|
class MotionListView(ListView):
|
||||||
@ -537,6 +539,29 @@ class SupportView(SingleObjectMixin, QuestionView):
|
|||||||
return _("You have unsupported this motion successfully.")
|
return _("You have unsupported this motion successfully.")
|
||||||
|
|
||||||
|
|
||||||
|
class MotionViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list, retrieve, create, update and destroy motions.
|
||||||
|
"""
|
||||||
|
model = Motion
|
||||||
|
queryset = Motion.objects.all()
|
||||||
|
serializer_class = MotionSerializer
|
||||||
|
|
||||||
|
def check_permissions(self, request):
|
||||||
|
"""
|
||||||
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
|
permission to see motions and in case of create, update or
|
||||||
|
destroy requests the permission to manage motions.
|
||||||
|
"""
|
||||||
|
# TODO: Use motion.can_create_motion permission and
|
||||||
|
# motion.can_support_motion permission to create and update some
|
||||||
|
# objects but restricted concerning the requesting user.
|
||||||
|
if (not request.user.has_perm('motion.can_see_motion') or
|
||||||
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
request.user.has_perm('motion.can_manage_motion'))):
|
||||||
|
self.permission_denied(request)
|
||||||
|
|
||||||
|
|
||||||
class PollCreateView(SingleObjectMixin, RedirectView):
|
class PollCreateView(SingleObjectMixin, RedirectView):
|
||||||
"""
|
"""
|
||||||
View to create a poll for a motion.
|
View to create a poll for a motion.
|
||||||
@ -815,6 +840,26 @@ class CategoryDeleteView(DeleteView):
|
|||||||
success_url_name = 'motion_category_list'
|
success_url_name = 'motion_category_list'
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list, retrieve, create, update and destroy categories.
|
||||||
|
"""
|
||||||
|
model = Category
|
||||||
|
queryset = Category.objects.all()
|
||||||
|
serializer_class = CategorySerializer
|
||||||
|
|
||||||
|
def check_permissions(self, request):
|
||||||
|
"""
|
||||||
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
|
permission to see motions and in case of create, update or destroy
|
||||||
|
requests the permission to manage motions.
|
||||||
|
"""
|
||||||
|
if (not request.user.has_perm('motion.can_see_motion') or
|
||||||
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
request.user.has_perm('motion.can_manage_motion'))):
|
||||||
|
self.permission_denied(request)
|
||||||
|
|
||||||
|
|
||||||
class MotionCSVImportView(CSVImportView):
|
class MotionCSVImportView(CSVImportView):
|
||||||
"""
|
"""
|
||||||
Imports motions from an uploaded csv file.
|
Imports motions from an uploaded csv file.
|
||||||
@ -839,3 +884,23 @@ class MotionCSVImportView(CSVImportView):
|
|||||||
messages.error(self.request, error)
|
messages.error(self.request, error)
|
||||||
# Overleap method of CSVImportView
|
# Overleap method of CSVImportView
|
||||||
return super(CSVImportView, self).form_valid(form)
|
return super(CSVImportView, self).form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowViewSet(viewsets.ModelViewSet):
|
||||||
|
"""
|
||||||
|
API endpoint to list, retrieve, create, update and destroy workflows.
|
||||||
|
"""
|
||||||
|
model = Workflow
|
||||||
|
queryset = Workflow.objects.all()
|
||||||
|
serializer_class = WorkflowSerializer
|
||||||
|
|
||||||
|
def check_permissions(self, request):
|
||||||
|
"""
|
||||||
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
|
permission to see motions and in case of create, update or destroy
|
||||||
|
requests the permission to manage motions.
|
||||||
|
"""
|
||||||
|
if (not request.user.has_perm('motion.can_see_motion') or
|
||||||
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
request.user.has_perm('motion.can_manage_motion'))):
|
||||||
|
self.permission_denied(request)
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
from openslides.utils import rest_api
|
from openslides.utils.rest_api import serializers
|
||||||
|
|
||||||
from .models import User
|
from .models import User
|
||||||
|
|
||||||
|
|
||||||
class UserShortSerializer(rest_api.serializers.ModelSerializer):
|
class UserShortSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for users.models.User objects.
|
Serializer for users.models.User objects.
|
||||||
|
|
||||||
@ -12,6 +12,7 @@ class UserShortSerializer(rest_api.serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = (
|
fields = (
|
||||||
|
'url',
|
||||||
'username',
|
'username',
|
||||||
'title',
|
'title',
|
||||||
'first_name',
|
'first_name',
|
||||||
@ -19,7 +20,7 @@ class UserShortSerializer(rest_api.serializers.ModelSerializer):
|
|||||||
'structure_level')
|
'structure_level')
|
||||||
|
|
||||||
|
|
||||||
class UserFullSerializer(rest_api.serializers.ModelSerializer):
|
class UserFullSerializer(serializers.ModelSerializer):
|
||||||
"""
|
"""
|
||||||
Serializer for users.models.User objects.
|
Serializer for users.models.User objects.
|
||||||
|
|
||||||
@ -28,6 +29,7 @@ class UserFullSerializer(rest_api.serializers.ModelSerializer):
|
|||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = (
|
fields = (
|
||||||
|
'url',
|
||||||
'is_present',
|
'is_present',
|
||||||
'username',
|
'username',
|
||||||
'title',
|
'title',
|
||||||
|
@ -6,7 +6,7 @@ from django.core.urlresolvers import reverse
|
|||||||
from django.utils.translation import ugettext as _, ugettext_lazy, activate
|
from django.utils.translation import ugettext as _, ugettext_lazy, activate
|
||||||
|
|
||||||
from openslides.config.api import config
|
from openslides.config.api import config
|
||||||
from openslides.utils import rest_api
|
from openslides.utils.rest_api import viewsets
|
||||||
from openslides.utils.utils import delete_default_permissions, html_strong
|
from openslides.utils.utils import delete_default_permissions, html_strong
|
||||||
from openslides.utils.views import (
|
from openslides.utils.views import (
|
||||||
CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
|
CreateView, CSVImportView, DeleteView, DetailView, FormView, ListView,
|
||||||
@ -261,17 +261,18 @@ class ResetPasswordView(SingleObjectMixin, QuestionView):
|
|||||||
return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
|
return _('The Password for %s was successfully reset.') % html_strong(self.get_object())
|
||||||
|
|
||||||
|
|
||||||
class UserViewSet(rest_api.viewsets.ModelViewSet):
|
class UserViewSet(viewsets.ModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to create, view, edit and delete users.
|
API endpoint to list, retrive, create, update and delete users.
|
||||||
"""
|
"""
|
||||||
model = User
|
model = User
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
|
|
||||||
def check_permissions(self, request):
|
def check_permissions(self, request):
|
||||||
"""
|
"""
|
||||||
Calls self.permission_denied() if the requesting user has not all
|
Calls self.permission_denied() if the requesting user has not the
|
||||||
permissions to see users.
|
permission to see users and in case of create, update or destroy
|
||||||
|
requests the permission to see extra user data and to manage users.
|
||||||
"""
|
"""
|
||||||
if (not request.user.has_perm('users.can_see_name') or
|
if (not request.user.has_perm('users.can_see_name') or
|
||||||
(self.action in ('create', 'update', 'destroy') and not
|
(self.action in ('create', 'update', 'destroy') and not
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
from django.core.urlresolvers import reverse
|
from django.core.urlresolvers import reverse
|
||||||
from rest_framework import permissions, routers, serializers, viewsets # noqa
|
from rest_framework import response, routers, serializers, viewsets # noqa
|
||||||
|
|
||||||
router = routers.DefaultRouter()
|
router = routers.DefaultRouter()
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user