Massive refactoring for autoupdate optimization.
This commit is contained in:
parent
3db2f2fc16
commit
4daa61888f
@ -1,9 +1,20 @@
|
|||||||
class AccessPermissions:
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
def get_serializer_class(self, user):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
|
class ItemAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Item and ItemViewSet.
|
||||||
|
"""
|
||||||
def can_retrieve(self, user):
|
def can_retrieve(self, user):
|
||||||
"""
|
"""
|
||||||
TODO
|
Returns True if the user has read access model instances.
|
||||||
"""
|
"""
|
||||||
return user.has_perm('assignments.can_see')
|
return user.has_perm('agenda.can_see')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import ItemSerializer
|
||||||
|
|
||||||
|
return ItemSerializer
|
||||||
|
@ -33,4 +33,4 @@ class AgendaAppConfig(AppConfig):
|
|||||||
dispatch_uid='listen_to_related_object_post_delete')
|
dispatch_uid='listen_to_related_object_post_delete')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register('agenda/item', ItemViewSet)
|
router.register(self.get_model('Item').get_collection_string(), ItemViewSet)
|
||||||
|
@ -15,6 +15,8 @@ from openslides.utils.exceptions import OpenSlidesError
|
|||||||
from openslides.utils.models import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.utils import to_roman
|
from openslides.utils.utils import to_roman
|
||||||
|
|
||||||
|
from .access_permissions import ItemAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
class ItemManager(models.Manager):
|
class ItemManager(models.Manager):
|
||||||
"""
|
"""
|
||||||
@ -184,6 +186,7 @@ class Item(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
An Agenda Item
|
An Agenda Item
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ItemAccessPermissions()
|
||||||
objects = ItemManager()
|
objects = ItemManager()
|
||||||
|
|
||||||
AGENDA_ITEM = 1
|
AGENDA_ITEM = 1
|
||||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext_lazy
|
|||||||
from reportlab.platypus import Paragraph
|
from reportlab.platypus import Paragraph
|
||||||
|
|
||||||
from openslides.core.config import config
|
from openslides.core.config import config
|
||||||
from openslides.agenda.access_permissions import AccessPermissions
|
|
||||||
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 (
|
from openslides.utils.rest_api import (
|
||||||
@ -22,8 +21,8 @@ from openslides.utils.rest_api import (
|
|||||||
)
|
)
|
||||||
from openslides.utils.views import PDFView
|
from openslides.utils.views import PDFView
|
||||||
|
|
||||||
|
from .access_permissions import ItemAccessPermissions
|
||||||
from .models import Item, Speaker
|
from .models import Item, Speaker
|
||||||
from .serializers import ItemSerializer
|
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
@ -35,16 +34,15 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update, destroy, manage_speaker, speak and tree.
|
partial_update, update, destroy, manage_speaker, speak and tree.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ItemAccessPermissions()
|
||||||
queryset = Item.objects.all()
|
queryset = Item.objects.all()
|
||||||
serializer_class = ItemSerializer
|
|
||||||
access_permissions = AccessPermissions()
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action == 'retrieve':
|
if self.action == 'retrieve':
|
||||||
result = self.access_permissions.can_retrieve(self.request.user)
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
elif self.action in ('metadata', 'list', 'manage_speaker', 'tree'):
|
elif self.action in ('metadata', 'list', 'manage_speaker', 'tree'):
|
||||||
result = self.request.user.has_perm('agenda.can_see')
|
result = self.request.user.has_perm('agenda.can_see')
|
||||||
# For manage_speaker and tree requests the rest of the check is
|
# For manage_speaker and tree requests the rest of the check is
|
||||||
@ -65,6 +63,7 @@ class ItemViewSet(ListModelMixin, RetrieveModelMixin, UpdateModelMixin, GenericV
|
|||||||
Checks if the requesting user has permission to see also an
|
Checks if the requesting user has permission to see also an
|
||||||
organizational item if it is one.
|
organizational item if it is one.
|
||||||
"""
|
"""
|
||||||
|
#TODO
|
||||||
if obj.is_hidden() and not request.user.has_perm('agenda.can_see_hidden_items'):
|
if obj.is_hidden() and not request.user.has_perm('agenda.can_see_hidden_items'):
|
||||||
self.permission_denied(request)
|
self.permission_denied(request)
|
||||||
|
|
||||||
|
@ -1,18 +1,24 @@
|
|||||||
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
|
class AssignmentAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Assignment and AssignmentViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('assignments.can_see')
|
||||||
|
|
||||||
class AccessPermissions:
|
|
||||||
def get_serializer_class(self, user):
|
def get_serializer_class(self, user):
|
||||||
"""
|
"""
|
||||||
Returns different serializer classes according to users permissions.
|
Returns different serializer classes according to users permissions.
|
||||||
"""
|
"""
|
||||||
from openslides.assignments.serializers import AssignmentFullSerializer, AssignmentShortSerializer
|
from .serializers import AssignmentFullSerializer, AssignmentShortSerializer
|
||||||
|
|
||||||
if user.has_perm('assignments.can_manage'):
|
if user.has_perm('assignments.can_manage'):
|
||||||
serializer_class = AssignmentFullSerializer
|
serializer_class = AssignmentFullSerializer
|
||||||
else:
|
else:
|
||||||
serializer_class = AssignmentShortSerializer
|
serializer_class = AssignmentShortSerializer
|
||||||
return serializer_class
|
return serializer_class
|
||||||
|
|
||||||
def can_retrieve(self, user):
|
|
||||||
"""
|
|
||||||
TODO
|
|
||||||
"""
|
|
||||||
return user.has_perm('agenda.can_see')
|
|
||||||
|
@ -22,5 +22,5 @@ class AssignmentsAppConfig(AppConfig):
|
|||||||
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
|
config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Assignment').get_collection_name(), AssignmentViewSet)
|
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)
|
||||||
router.register('assignments/poll', AssignmentPollViewSet)
|
router.register('assignments/poll', AssignmentPollViewSet)
|
||||||
|
@ -7,7 +7,6 @@ from django.utils.translation import ugettext as _
|
|||||||
from django.utils.translation import ugettext_lazy, ugettext_noop
|
from django.utils.translation import ugettext_lazy, ugettext_noop
|
||||||
|
|
||||||
from openslides.agenda.models import Item, Speaker
|
from openslides.agenda.models import Item, Speaker
|
||||||
from openslides.assignments.access_permissions import AccessPermissions
|
|
||||||
from openslides.core.config import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import Tag
|
from openslides.core.models import Tag
|
||||||
from openslides.poll.models import (
|
from openslides.poll.models import (
|
||||||
@ -21,6 +20,8 @@ from openslides.utils.exceptions import OpenSlidesError
|
|||||||
from openslides.utils.models import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.search import user_name_helper
|
from openslides.utils.search import user_name_helper
|
||||||
|
|
||||||
|
from .access_permissions import AssignmentAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
@ -50,7 +51,10 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Assignment(RESTModelMixin, models.Model):
|
class Assignment(RESTModelMixin, models.Model):
|
||||||
access_permissions = AccessPermissions()
|
"""
|
||||||
|
Model for assignments.
|
||||||
|
"""
|
||||||
|
access_permissions = AssignmentAccessPermissions()
|
||||||
|
|
||||||
PHASE_SEARCH = 0
|
PHASE_SEARCH = 0
|
||||||
PHASE_VOTING = 1
|
PHASE_VOTING = 1
|
||||||
|
@ -17,7 +17,6 @@ from reportlab.platypus import (
|
|||||||
TableStyle,
|
TableStyle,
|
||||||
)
|
)
|
||||||
|
|
||||||
from openslides.assignments.access_permissions import AccessPermissions
|
|
||||||
from openslides.core.config import config
|
from openslides.core.config import config
|
||||||
from openslides.utils.pdf import stylesheet
|
from openslides.utils.pdf import stylesheet
|
||||||
from openslides.utils.rest_api import (
|
from openslides.utils.rest_api import (
|
||||||
@ -31,12 +30,9 @@ from openslides.utils.rest_api import (
|
|||||||
)
|
)
|
||||||
from openslides.utils.views import PDFView
|
from openslides.utils.views import PDFView
|
||||||
|
|
||||||
|
from .access_permissions import AssignmentAccessPermissions
|
||||||
from .models import Assignment, AssignmentPoll
|
from .models import Assignment, AssignmentPoll
|
||||||
from .serializers import (
|
from .serializers import AssignmentAllPollSerializer
|
||||||
AssignmentAllPollSerializer,
|
|
||||||
AssignmentFullSerializer,
|
|
||||||
AssignmentShortSerializer,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
@ -49,15 +45,15 @@ class AssignmentViewSet(ModelViewSet):
|
|||||||
partial_update, update, destroy, candidature_self, candidature_other,
|
partial_update, update, destroy, candidature_self, candidature_other,
|
||||||
mark_elected and create_poll.
|
mark_elected and create_poll.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = AssignmentAccessPermissions()
|
||||||
queryset = Assignment.objects.all()
|
queryset = Assignment.objects.all()
|
||||||
access_permissions = AccessPermissions()
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action == 'retrieve':
|
if self.action == 'retrieve':
|
||||||
result = self.access_permissions.can_retrieve(self.request.user)
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
elif self.action in ('metadata', 'list'):
|
elif self.action in ('metadata', 'list'):
|
||||||
result = self.request.user.has_perm('assignments.can_see')
|
result = self.request.user.has_perm('assignments.can_see')
|
||||||
elif self.action in ('create', 'partial_update', 'update', 'destroy',
|
elif self.action in ('create', 'partial_update', 'update', 'destroy',
|
||||||
|
@ -1,9 +1,109 @@
|
|||||||
class AccessPermissions:
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
def get_serializer_class(self, user):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectorAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Projector and ProjectorViewSet.
|
||||||
|
"""
|
||||||
def can_retrieve(self, user):
|
def can_retrieve(self, user):
|
||||||
"""
|
"""
|
||||||
TODO
|
Returns True if the user has read access model instances.
|
||||||
"""
|
"""
|
||||||
return user.has_perm('core.can_see_projector')
|
return user.has_perm('core.can_see_projector')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import ProjectorSerializer
|
||||||
|
|
||||||
|
return ProjectorSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class CustomSlideAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for CustomSlide and CustomSlideViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('core.can_manage_projector')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import CustomSlideSerializer
|
||||||
|
|
||||||
|
return CustomSlideSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class TagAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Tag and TagViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
from .config import config
|
||||||
|
|
||||||
|
# Every authenticated user can retrieve tags. Anonymous users can do
|
||||||
|
# so if they are enabled.
|
||||||
|
return user.is_authenticated() or config['general_system_enable_anonymous']
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import TagSerializer
|
||||||
|
|
||||||
|
return TagSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ChatMessageAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for ChatMessage and ChatMessageViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
# Anonymous users can see the chat if the anonymous group has the
|
||||||
|
# permission core.can_use_chat. But they can not use it. See views.py.
|
||||||
|
return user.has_perm('core.can_use_chat')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import ChatMessageSerializer
|
||||||
|
|
||||||
|
return ChatMessageSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for the config (ConfigStore and
|
||||||
|
ConfigViewSet).
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
from .config import config
|
||||||
|
|
||||||
|
# Every authenticated user can see the metadata and list or retrieve
|
||||||
|
# the config. Anonymous users can do so if they are enabled.
|
||||||
|
return user.is_authenticated() or config['general_system_enable_anonymous']
|
||||||
|
|
||||||
|
def get_serialized_data(self, instance, user):
|
||||||
|
"""
|
||||||
|
Returns the serlialized config data or None if the user is not
|
||||||
|
allowed to see it.
|
||||||
|
"""
|
||||||
|
from .config import config
|
||||||
|
|
||||||
|
if self.can_retrieve(user) is not None:
|
||||||
|
return {'key': instance.key, 'value': config[instance.key]}
|
||||||
|
@ -12,11 +12,11 @@ class CoreAppConfig(AppConfig):
|
|||||||
# Load projector elements.
|
# Load projector elements.
|
||||||
# Do this by just importing all from these files.
|
# Do this by just importing all from these files.
|
||||||
from . import projector # noqa
|
from . import projector # noqa
|
||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from openslides.core.signals import config_signal, post_permission_creation
|
from openslides.core.signals import config_signal, post_permission_creation
|
||||||
from openslides.utils.autoupdate import inform_changed_data_receiver
|
from openslides.utils.autoupdate import inform_changed_data_receiver, inform_deleted_data_receiver
|
||||||
from openslides.utils.autoupdate import inform_deleted_data_receiver
|
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
from openslides.utils.search import index_add_instance, index_del_instance
|
from openslides.utils.search import index_add_instance, index_del_instance
|
||||||
from .signals import delete_django_app_permissions, setup_general_config
|
from .signals import delete_django_app_permissions, setup_general_config
|
||||||
@ -37,11 +37,11 @@ class CoreAppConfig(AppConfig):
|
|||||||
dispatch_uid='delete_django_app_permissions')
|
dispatch_uid='delete_django_app_permissions')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register('core/projector', ProjectorViewSet)
|
router.register(self.get_model('Projector').get_collection_string(), ProjectorViewSet)
|
||||||
router.register('core/chatmessage', ChatMessageViewSet)
|
router.register(self.get_model('ChatMessage').get_collection_string(), ChatMessageViewSet)
|
||||||
router.register('core/customslide', CustomSlideViewSet)
|
router.register(self.get_model('CustomSlide').get_collection_string(), CustomSlideViewSet)
|
||||||
router.register('core/tag', TagViewSet)
|
router.register(self.get_model('Tag').get_collection_string(), TagViewSet)
|
||||||
router.register('core/config', ConfigViewSet, 'config')
|
router.register(self.get_model('ConfigStore').get_collection_string(), ConfigViewSet, 'config')
|
||||||
|
|
||||||
# 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.
|
||||||
# TODO: Test if the m2m_changed signal is also needed.
|
# TODO: Test if the m2m_changed signal is also needed.
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from jsonfield import JSONField
|
from jsonfield import JSONField
|
||||||
|
|
||||||
@ -8,6 +7,13 @@ from openslides.mediafiles.models import Mediafile
|
|||||||
from openslides.utils.models import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.projector import ProjectorElement
|
from openslides.utils.projector import ProjectorElement
|
||||||
|
|
||||||
|
from .access_permissions import (
|
||||||
|
ChatMessageAccessPermissions,
|
||||||
|
ConfigAccessPermissions,
|
||||||
|
CustomSlideAccessPermissions,
|
||||||
|
ProjectorAccessPermissions,
|
||||||
|
TagAccessPermissions,
|
||||||
|
)
|
||||||
from .exceptions import ProjectorException
|
from .exceptions import ProjectorException
|
||||||
|
|
||||||
|
|
||||||
@ -51,6 +57,8 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
The projector can be controlled using the REST API with POST requests
|
The projector can be controlled using the REST API with POST requests
|
||||||
on e. g. the URL /rest/core/projector/1/activate_elements/.
|
on e. g. the URL /rest/core/projector/1/activate_elements/.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ProjectorAccessPermissions()
|
||||||
|
|
||||||
config = JSONField()
|
config = JSONField()
|
||||||
|
|
||||||
scale = models.IntegerField(default=0)
|
scale = models.IntegerField(default=0)
|
||||||
@ -121,6 +129,8 @@ class CustomSlide(RESTModelMixin, models.Model):
|
|||||||
"""
|
"""
|
||||||
Model for slides with custom content.
|
Model for slides with custom content.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = CustomSlideAccessPermissions()
|
||||||
|
|
||||||
title = models.CharField(
|
title = models.CharField(
|
||||||
max_length=256)
|
max_length=256)
|
||||||
text = models.TextField(
|
text = models.TextField(
|
||||||
@ -175,6 +185,8 @@ class Tag(RESTModelMixin, models.Model):
|
|||||||
Model for tags. This tags can be used for other models like agenda items,
|
Model for tags. This tags can be used for other models like agenda items,
|
||||||
motions or assignments.
|
motions or assignments.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = TagAccessPermissions()
|
||||||
|
|
||||||
name = models.CharField(
|
name = models.CharField(
|
||||||
max_length=255,
|
max_length=255,
|
||||||
unique=True)
|
unique=True)
|
||||||
@ -189,10 +201,11 @@ class Tag(RESTModelMixin, models.Model):
|
|||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
|
|
||||||
class ConfigStore(models.Model):
|
class ConfigStore(RESTModelMixin, models.Model):
|
||||||
"""
|
"""
|
||||||
A model class to store all config variables in the database.
|
A model class to store all config variables in the database.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ConfigAccessPermissions()
|
||||||
|
|
||||||
key = models.CharField(max_length=255, unique=True, db_index=True)
|
key = models.CharField(max_length=255, unique=True, db_index=True)
|
||||||
"""A string, the key of the config variable."""
|
"""A string, the key of the config variable."""
|
||||||
@ -205,11 +218,15 @@ class ConfigStore(models.Model):
|
|||||||
permissions = (
|
permissions = (
|
||||||
('can_manage_config', 'Can manage configuration'),)
|
('can_manage_config', 'Can manage configuration'),)
|
||||||
|
|
||||||
def get_root_rest_url(self):
|
@classmethod
|
||||||
|
def get_collection_string(cls):
|
||||||
|
return 'core/config'
|
||||||
|
|
||||||
|
def get_rest_pk(self):
|
||||||
"""
|
"""
|
||||||
Returns the detail url of config value.
|
Returns the primary key used in the REST API.
|
||||||
"""
|
"""
|
||||||
return reverse('config-detail', args=[str(self.key)])
|
return self.key
|
||||||
|
|
||||||
|
|
||||||
class ChatMessage(RESTModelMixin, models.Model):
|
class ChatMessage(RESTModelMixin, models.Model):
|
||||||
@ -218,6 +235,8 @@ class ChatMessage(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
At the moment we only have one global chat room for managers.
|
At the moment we only have one global chat room for managers.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ChatMessageAccessPermissions()
|
||||||
|
|
||||||
message = models.TextField()
|
message = models.TextField()
|
||||||
|
|
||||||
timestamp = models.DateTimeField(auto_now_add=True)
|
timestamp = models.DateTimeField(auto_now_add=True)
|
||||||
|
@ -13,7 +13,6 @@ from django.http import Http404, HttpResponse
|
|||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
|
||||||
from openslides import __version__ as version
|
from openslides import __version__ as version
|
||||||
from openslides.core.access_permissions import AccessPermissions
|
|
||||||
from openslides.utils import views as utils_views
|
from openslides.utils import views as utils_views
|
||||||
from openslides.utils.plugins import (
|
from openslides.utils.plugins import (
|
||||||
get_plugin_description,
|
get_plugin_description,
|
||||||
@ -31,15 +30,16 @@ from openslides.utils.rest_api import (
|
|||||||
)
|
)
|
||||||
from openslides.utils.search import search
|
from openslides.utils.search import search
|
||||||
|
|
||||||
|
from .access_permissions import (
|
||||||
|
ChatMessageAccessPermissions,
|
||||||
|
ConfigAccessPermissions,
|
||||||
|
CustomSlideAccessPermissions,
|
||||||
|
ProjectorAccessPermissions,
|
||||||
|
TagAccessPermissions,
|
||||||
|
)
|
||||||
from .config import config
|
from .config import config
|
||||||
from .exceptions import ConfigError, ConfigNotFound
|
from .exceptions import ConfigError, ConfigNotFound
|
||||||
from .models import ChatMessage, CustomSlide, Projector, Tag
|
from .models import ChatMessage, CustomSlide, Projector, Tag
|
||||||
from .serializers import (
|
|
||||||
ChatMessageSerializer,
|
|
||||||
CustomSlideSerializer,
|
|
||||||
ProjectorSerializer,
|
|
||||||
TagSerializer,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Special Django views
|
# Special Django views
|
||||||
@ -153,16 +153,15 @@ class ProjectorViewSet(ReadOnlyModelViewSet):
|
|||||||
activate_elements, prune_elements, update_elements,
|
activate_elements, prune_elements, update_elements,
|
||||||
deactivate_elements, clear_elements and control_view.
|
deactivate_elements, clear_elements and control_view.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ProjectorAccessPermissions()
|
||||||
queryset = Projector.objects.all()
|
queryset = Projector.objects.all()
|
||||||
serializer_class = ProjectorSerializer
|
|
||||||
access_permissions = AccessPermissions()
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action == 'retrieve':
|
if self.action == 'retrieve':
|
||||||
result = self.access_permissions.can_retrieve(self.request.user)
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
elif self.action in ('metadata', 'list'):
|
elif self.action in ('metadata', 'list'):
|
||||||
result = self.request.user.has_perm('core.can_see_projector')
|
result = self.request.user.has_perm('core.can_see_projector')
|
||||||
elif self.action in ('activate_elements', 'prune_elements', 'update_elements',
|
elif self.action in ('activate_elements', 'prune_elements', 'update_elements',
|
||||||
@ -370,14 +369,18 @@ class CustomSlideViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update and destroy.
|
partial_update, update and destroy.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = CustomSlideAccessPermissions()
|
||||||
queryset = CustomSlide.objects.all()
|
queryset = CustomSlide.objects.all()
|
||||||
serializer_class = CustomSlideSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
return self.request.user.has_perm('core.can_manage_projector')
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
else:
|
||||||
|
result = self.request.user.has_perm('core.can_manage_projector')
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class TagViewSet(ModelViewSet):
|
class TagViewSet(ModelViewSet):
|
||||||
@ -387,16 +390,18 @@ class TagViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update and destroy.
|
partial_update, update and destroy.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = TagAccessPermissions()
|
||||||
queryset = Tag.objects.all()
|
queryset = Tag.objects.all()
|
||||||
serializer_class = TagSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve'):
|
if self.action == 'retrieve':
|
||||||
# Every authenticated user can see the metadata and list or
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
# retrieve tags. Anonymous users can do so if they are enabled.
|
elif self.action in ('metadata', 'list'):
|
||||||
|
# Every authenticated user can see the metadata and list tags.
|
||||||
|
# Anonymous users can do so if they are enabled.
|
||||||
result = self.request.user.is_authenticated() or config['general_system_enable_anonymous']
|
result = self.request.user.is_authenticated() or config['general_system_enable_anonymous']
|
||||||
elif self.action in ('create', 'update', 'destroy'):
|
elif self.action in ('create', 'update', 'destroy'):
|
||||||
result = self.request.user.has_perm('core.can_manage_tags')
|
result = self.request.user.has_perm('core.can_manage_tags')
|
||||||
@ -444,13 +449,16 @@ class ConfigViewSet(ViewSet):
|
|||||||
|
|
||||||
There are the following views: metadata, list, retrieve and update.
|
There are the following views: metadata, list, retrieve and update.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ConfigAccessPermissions()
|
||||||
metadata_class = ConfigMetadata
|
metadata_class = ConfigMetadata
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list'):
|
||||||
# Every authenticated user can see the metadata and list or
|
# Every authenticated user can see the metadata and list or
|
||||||
# retrieve the config. Anonymous users can do so if they are
|
# retrieve the config. Anonymous users can do so if they are
|
||||||
# enabled.
|
# enabled.
|
||||||
@ -476,6 +484,8 @@ class ConfigViewSet(ViewSet):
|
|||||||
value = config[key]
|
value = config[key]
|
||||||
except ConfigNotFound:
|
except ConfigNotFound:
|
||||||
raise Http404
|
raise Http404
|
||||||
|
# Attention: The format of this response has to be the same as in
|
||||||
|
# the get_serialized_data method of ConfigAccessPermissions.
|
||||||
return Response({'key': key, 'value': value})
|
return Response({'key': key, 'value': value})
|
||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
@ -508,18 +518,23 @@ class ChatMessageViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve and create.
|
There are the following views: metadata, list, retrieve and create.
|
||||||
The views partial_update, update and destroy are disabled.
|
The views partial_update, update and destroy are disabled.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = ChatMessageAccessPermissions()
|
||||||
queryset = ChatMessage.objects.all()
|
queryset = ChatMessage.objects.all()
|
||||||
serializer_class = ChatMessageSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
# We do not want anonymous users to use the chat even the anonymous
|
if self.action == 'retrieve':
|
||||||
# group has the permission core.can_use_chat.
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
return (self.action in ('metadata', 'list', 'retrieve', 'create') and
|
else:
|
||||||
|
# We do not want anonymous users to use the chat even the anonymous
|
||||||
|
# group has the permission core.can_use_chat.
|
||||||
|
result = (
|
||||||
|
self.action in ('metadata', 'list', 'create') and
|
||||||
self.request.user.is_authenticated() and
|
self.request.user.is_authenticated() and
|
||||||
self.request.user.has_perm('core.can_use_chat'))
|
self.request.user.has_perm('core.can_use_chat'))
|
||||||
|
return result
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
"""
|
"""
|
||||||
|
20
openslides/mediafiles/access_permissions.py
Normal file
20
openslides/mediafiles/access_permissions.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
|
class MediafileAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Mediafile and MediafileViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('mediafiles.can_see')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import MediafileSerializer
|
||||||
|
|
||||||
|
return MediafileSerializer
|
@ -18,4 +18,4 @@ class MediafilesAppConfig(AppConfig):
|
|||||||
from .views import MediafileViewSet
|
from .views import MediafileViewSet
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register('mediafiles/mediafile', MediafileViewSet)
|
router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet)
|
||||||
|
@ -5,12 +5,15 @@ from django.utils.translation import ugettext as _
|
|||||||
from openslides.utils.search import user_name_helper
|
from openslides.utils.search import user_name_helper
|
||||||
|
|
||||||
from ..utils.models import RESTModelMixin
|
from ..utils.models import RESTModelMixin
|
||||||
|
from .access_permissions import MediafileAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
class Mediafile(RESTModelMixin, models.Model):
|
class Mediafile(RESTModelMixin, 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.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = MediafileAccessPermissions()
|
||||||
|
|
||||||
mediafile = models.FileField(upload_to='file')
|
mediafile = models.FileField(upload_to='file')
|
||||||
"""
|
"""
|
||||||
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
|
See https://docs.djangoproject.com/en/dev/ref/models/fields/#filefield
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
from ..utils.rest_api import ModelViewSet, ValidationError
|
from ..utils.rest_api import ModelViewSet, ValidationError
|
||||||
|
from .access_permissions import MediafileAccessPermissions
|
||||||
from .models import Mediafile
|
from .models import Mediafile
|
||||||
from .serializers import MediafileSerializer
|
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
@ -12,14 +12,16 @@ class MediafileViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update and destroy.
|
partial_update, update and destroy.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = MediafileAccessPermissions()
|
||||||
queryset = Mediafile.objects.all()
|
queryset = Mediafile.objects.all()
|
||||||
serializer_class = MediafileSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list'):
|
||||||
result = self.request.user.has_perm('mediafiles.can_see')
|
result = self.request.user.has_perm('mediafiles.can_see')
|
||||||
elif self.action == 'create':
|
elif self.action == 'create':
|
||||||
result = (self.request.user.has_perm('mediafiles.can_see') and
|
result = (self.request.user.has_perm('mediafiles.can_see') and
|
||||||
|
58
openslides/motions/access_permissions.py
Normal file
58
openslides/motions/access_permissions.py
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
|
class MotionAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Motion and MotionViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('motions.can_see')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import MotionSerializer
|
||||||
|
|
||||||
|
return MotionSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class CategoryAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Category and CategoryViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('motions.can_see')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import CategorySerializer
|
||||||
|
|
||||||
|
return CategorySerializer
|
||||||
|
|
||||||
|
|
||||||
|
class WorkflowAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for Workflow and WorkflowViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('motions.can_see')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns serializer class.
|
||||||
|
"""
|
||||||
|
from .serializers import WorkflowSerializer
|
||||||
|
|
||||||
|
return WorkflowSerializer
|
@ -25,7 +25,7 @@ class MotionsAppConfig(AppConfig):
|
|||||||
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')
|
post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register('motions/category', CategoryViewSet)
|
router.register(self.get_model('Category').get_collection_string(), CategoryViewSet)
|
||||||
router.register('motions/motion', MotionViewSet)
|
router.register(self.get_model('Motion').get_collection_string(), MotionViewSet)
|
||||||
|
router.register(self.get_model('Workflow').get_collection_string(), WorkflowViewSet)
|
||||||
router.register('motions/motionpoll', MotionPollViewSet)
|
router.register('motions/motionpoll', MotionPollViewSet)
|
||||||
router.register('motions/workflow', WorkflowViewSet)
|
|
||||||
|
@ -20,6 +20,11 @@ from openslides.poll.models import (
|
|||||||
from openslides.utils.models import RESTModelMixin
|
from openslides.utils.models import RESTModelMixin
|
||||||
from openslides.utils.search import user_name_helper
|
from openslides.utils.search import user_name_helper
|
||||||
|
|
||||||
|
from .access_permissions import (
|
||||||
|
CategoryAccessPermissions,
|
||||||
|
MotionAccessPermissions,
|
||||||
|
WorkflowAccessPermissions,
|
||||||
|
)
|
||||||
from .exceptions import WorkflowError
|
from .exceptions import WorkflowError
|
||||||
|
|
||||||
|
|
||||||
@ -29,6 +34,7 @@ class Motion(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
This class is the main entry point to all other classes related to a motion.
|
This class is the main entry point to all other classes related to a motion.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = MotionAccessPermissions()
|
||||||
|
|
||||||
active_version = models.ForeignKey(
|
active_version = models.ForeignKey(
|
||||||
'MotionVersion',
|
'MotionVersion',
|
||||||
@ -624,6 +630,11 @@ class MotionVersion(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Category(RESTModelMixin, models.Model):
|
class Category(RESTModelMixin, models.Model):
|
||||||
|
"""
|
||||||
|
Model for categories of motions.
|
||||||
|
"""
|
||||||
|
access_permissions = CategoryAccessPermissions()
|
||||||
|
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
"""Name of the category."""
|
"""Name of the category."""
|
||||||
|
|
||||||
@ -879,7 +890,10 @@ class State(RESTModelMixin, models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Workflow(RESTModelMixin, models.Model):
|
class Workflow(RESTModelMixin, models.Model):
|
||||||
"""Defines a workflow for a motion."""
|
"""
|
||||||
|
Defines a workflow for a motion.
|
||||||
|
"""
|
||||||
|
access_permissions = WorkflowAccessPermissions()
|
||||||
|
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
"""A string representing the workflow."""
|
"""A string representing the workflow."""
|
||||||
|
@ -18,15 +18,15 @@ from openslides.utils.rest_api import (
|
|||||||
)
|
)
|
||||||
from openslides.utils.views import PDFView, SingleObjectMixin
|
from openslides.utils.views import PDFView, SingleObjectMixin
|
||||||
|
|
||||||
|
from .access_permissions import (
|
||||||
|
CategoryAccessPermissions,
|
||||||
|
MotionAccessPermissions,
|
||||||
|
WorkflowAccessPermissions,
|
||||||
|
)
|
||||||
from .exceptions import WorkflowError
|
from .exceptions import WorkflowError
|
||||||
from .models import Category, Motion, MotionPoll, MotionVersion, Workflow
|
from .models import Category, Motion, MotionPoll, MotionVersion, 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 (
|
from .serializers import MotionPollSerializer
|
||||||
CategorySerializer,
|
|
||||||
MotionPollSerializer,
|
|
||||||
MotionSerializer,
|
|
||||||
WorkflowSerializer,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
@ -39,14 +39,16 @@ class MotionViewSet(ModelViewSet):
|
|||||||
partial_update, update, destroy, manage_version, support, set_state and
|
partial_update, update, destroy, manage_version, support, set_state and
|
||||||
create_poll.
|
create_poll.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = MotionAccessPermissions()
|
||||||
queryset = Motion.objects.all()
|
queryset = Motion.objects.all()
|
||||||
serializer_class = MotionSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve', 'partial_update', 'update'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list', 'partial_update', 'update'):
|
||||||
result = self.request.user.has_perm('motions.can_see')
|
result = self.request.user.has_perm('motions.can_see')
|
||||||
# For partial_update and update requests the rest of the check is
|
# For partial_update and update requests the rest of the check is
|
||||||
# done in the update method. See below.
|
# done in the update method. See below.
|
||||||
@ -281,14 +283,16 @@ class CategoryViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update and destroy.
|
partial_update, update and destroy.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = CategoryAccessPermissions()
|
||||||
queryset = Category.objects.all()
|
queryset = Category.objects.all()
|
||||||
serializer_class = CategorySerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list'):
|
||||||
result = self.request.user.has_perm('motions.can_see')
|
result = self.request.user.has_perm('motions.can_see')
|
||||||
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
||||||
result = (self.request.user.has_perm('motions.can_see') and
|
result = (self.request.user.has_perm('motions.can_see') and
|
||||||
@ -305,14 +309,16 @@ class WorkflowViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update and destroy.
|
partial_update, update and destroy.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = WorkflowAccessPermissions()
|
||||||
queryset = Workflow.objects.all()
|
queryset = Workflow.objects.all()
|
||||||
serializer_class = WorkflowSerializer
|
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list'):
|
||||||
result = self.request.user.has_perm('motions.can_see')
|
result = self.request.user.has_perm('motions.can_see')
|
||||||
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
elif self.action in ('create', 'partial_update', 'update', 'destroy'):
|
||||||
result = (self.request.user.has_perm('motions.can_see') and
|
result = (self.request.user.has_perm('motions.can_see') and
|
||||||
|
26
openslides/users/access_permissions.py
Normal file
26
openslides/users/access_permissions.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from ..utils.access_permissions import BaseAccessPermissions
|
||||||
|
|
||||||
|
|
||||||
|
class UserAccessPermissions(BaseAccessPermissions):
|
||||||
|
"""
|
||||||
|
Access permissions container for User and UserViewSet.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return user.has_perm('users.can_see_name')
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns different serializer classes with respect user's permissions.
|
||||||
|
"""
|
||||||
|
from .serializers import UserFullSerializer, UserShortSerializer
|
||||||
|
|
||||||
|
if user.has_perm('users.can_see_extra_data'):
|
||||||
|
# Return the UserFullSerializer for requests of users with more
|
||||||
|
# permissions.
|
||||||
|
serializer_class = UserFullSerializer
|
||||||
|
else:
|
||||||
|
serializer_class = UserShortSerializer
|
||||||
|
return serializer_class
|
@ -28,5 +28,5 @@ class UsersAppConfig(AppConfig):
|
|||||||
dispatch_uid='create_builtin_groups_and_admin')
|
dispatch_uid='create_builtin_groups_and_admin')
|
||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register('users/user', UserViewSet)
|
router.register(self.get_model('User').get_collection_string(), UserViewSet)
|
||||||
router.register('users/group', GroupViewSet)
|
router.register('users/group', GroupViewSet)
|
||||||
|
@ -13,6 +13,7 @@ from openslides.utils.search import user_name_helper
|
|||||||
|
|
||||||
from ..core.config import config
|
from ..core.config import config
|
||||||
from ..utils.models import RESTModelMixin
|
from ..utils.models import RESTModelMixin
|
||||||
|
from .access_permissions import UserAccessPermissions
|
||||||
from .exceptions import UsersError
|
from .exceptions import UsersError
|
||||||
|
|
||||||
|
|
||||||
@ -94,6 +95,8 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
|
|||||||
in other OpenSlides apps like motion submitter or (assignment) election
|
in other OpenSlides apps like motion submitter or (assignment) election
|
||||||
candidates.
|
candidates.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = UserAccessPermissions()
|
||||||
|
|
||||||
USERNAME_FIELD = 'username'
|
USERNAME_FIELD = 'username'
|
||||||
|
|
||||||
username = models.CharField(
|
username = models.CharField(
|
||||||
|
@ -14,13 +14,10 @@ from ..utils.rest_api import (
|
|||||||
status,
|
status,
|
||||||
)
|
)
|
||||||
from ..utils.views import APIView, PDFView
|
from ..utils.views import APIView, PDFView
|
||||||
|
from .access_permissions import UserAccessPermissions
|
||||||
from .models import Group, User
|
from .models import Group, User
|
||||||
from .pdf import users_passwords_to_pdf, users_to_pdf
|
from .pdf import users_passwords_to_pdf, users_to_pdf
|
||||||
from .serializers import (
|
from .serializers import GroupSerializer, UserFullSerializer
|
||||||
GroupSerializer,
|
|
||||||
UserFullSerializer,
|
|
||||||
UserShortSerializer,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# Viewsets for the REST API
|
# Viewsets for the REST API
|
||||||
@ -32,13 +29,16 @@ class UserViewSet(ModelViewSet):
|
|||||||
There are the following views: metadata, list, retrieve, create,
|
There are the following views: metadata, list, retrieve, create,
|
||||||
partial_update, update, destroy and reset_password.
|
partial_update, update, destroy and reset_password.
|
||||||
"""
|
"""
|
||||||
|
access_permissions = UserAccessPermissions()
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
|
|
||||||
def check_view_permissions(self):
|
def check_view_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns True if the user has required permissions.
|
Returns True if the user has required permissions.
|
||||||
"""
|
"""
|
||||||
if self.action in ('metadata', 'list', 'retrieve', 'update', 'partial_update'):
|
if self.action == 'retrieve':
|
||||||
|
result = self.get_access_permissions().can_retrieve(self.request.user)
|
||||||
|
elif self.action in ('metadata', 'list', 'update', 'partial_update'):
|
||||||
result = self.request.user.has_perm('users.can_see_name')
|
result = self.request.user.has_perm('users.can_see_name')
|
||||||
elif self.action in ('create', 'destroy', 'reset_password'):
|
elif self.action in ('create', 'destroy', 'reset_password'):
|
||||||
result = (self.request.user.has_perm('users.can_see_name') and
|
result = (self.request.user.has_perm('users.can_see_name') and
|
||||||
@ -50,16 +50,13 @@ class UserViewSet(ModelViewSet):
|
|||||||
|
|
||||||
def get_serializer_class(self):
|
def get_serializer_class(self):
|
||||||
"""
|
"""
|
||||||
Returns different serializer classes with respect to action and user's
|
Returns different serializer classes with respect to action.
|
||||||
permissions.
|
|
||||||
"""
|
"""
|
||||||
if (self.action in ('create', 'partial_update', 'update') or
|
if self.action in ('create', 'partial_update', 'update'):
|
||||||
self.request.user.has_perm('users.can_see_extra_data')):
|
# Return the UserFullSerializer for edit requests.
|
||||||
# Return the UserFullSerializer for edit requests or for
|
|
||||||
# list/retrieve requests of users with more permissions.
|
|
||||||
serializer_class = UserFullSerializer
|
serializer_class = UserFullSerializer
|
||||||
else:
|
else:
|
||||||
serializer_class = UserShortSerializer
|
serializer_class = super().get_serializer_class()
|
||||||
return serializer_class
|
return serializer_class
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
@ -78,6 +75,7 @@ class UserViewSet(ModelViewSet):
|
|||||||
|
|
||||||
Hides the default_password for non admins.
|
Hides the default_password for non admins.
|
||||||
"""
|
"""
|
||||||
|
#TODO: Hide default_password also in case of autoupdate.
|
||||||
response = super().retrieve(request, *args, **kwargs)
|
response = super().retrieve(request, *args, **kwargs)
|
||||||
self.extract_default_password(response)
|
self.extract_default_password(response)
|
||||||
return response
|
return response
|
||||||
|
30
openslides/utils/access_permissions.py
Normal file
30
openslides/utils/access_permissions.py
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
class BaseAccessPermissions:
|
||||||
|
"""
|
||||||
|
Base access permissions container.
|
||||||
|
"""
|
||||||
|
def can_retrieve(self, user):
|
||||||
|
"""
|
||||||
|
Returns True if the user has read access model instances.
|
||||||
|
"""
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_serializer_class(self, user):
|
||||||
|
"""
|
||||||
|
Returns different serializer classes according to users permissions.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError(
|
||||||
|
"You have to add the classmethod 'get_serializer_class' to your "
|
||||||
|
"access permissions class.".format(self))
|
||||||
|
|
||||||
|
def get_serialized_data(self, instance, user):
|
||||||
|
"""
|
||||||
|
Returns the serialized data for the instance prepared for the user.
|
||||||
|
|
||||||
|
Returns None if the user has no read access.
|
||||||
|
"""
|
||||||
|
if self.can_retrieve(user):
|
||||||
|
serializer_class = self.get_serializer_class(user)
|
||||||
|
data = serializer_class(instance).data
|
||||||
|
else:
|
||||||
|
data = None
|
||||||
|
return data
|
@ -1,15 +1,12 @@
|
|||||||
import json
|
|
||||||
import os
|
import os
|
||||||
import posixpath
|
import posixpath
|
||||||
|
from importlib import import_module
|
||||||
from urllib.parse import unquote
|
from urllib.parse import unquote
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from openslides.users.auth import get_user
|
|
||||||
from django.core.wsgi import get_wsgi_application
|
from django.core.wsgi import get_wsgi_application
|
||||||
from django.utils.importlib import import_module
|
|
||||||
from sockjs.tornado import SockJSConnection, SockJSRouter
|
from sockjs.tornado import SockJSConnection, SockJSRouter
|
||||||
from tornado.httpclient import AsyncHTTPClient, HTTPRequest
|
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.httputil import HTTPHeaders
|
|
||||||
from tornado.ioloop import IOLoop
|
from tornado.ioloop import IOLoop
|
||||||
from tornado.options import parse_command_line
|
from tornado.options import parse_command_line
|
||||||
from tornado.web import (
|
from tornado.web import (
|
||||||
@ -19,7 +16,8 @@ from tornado.web import (
|
|||||||
StaticFileHandler,
|
StaticFileHandler,
|
||||||
)
|
)
|
||||||
from tornado.wsgi import WSGIContainer
|
from tornado.wsgi import WSGIContainer
|
||||||
from .rest_api import get_collection_and_id_from_url
|
|
||||||
|
from openslides.users.auth import AnonymousUser, get_user
|
||||||
|
|
||||||
RUNNING_HOST = None
|
RUNNING_HOST = None
|
||||||
RUNNING_PORT = None
|
RUNNING_PORT = None
|
||||||
@ -59,9 +57,6 @@ class DjangoStaticFileHandler(StaticFileHandler):
|
|||||||
return absolute_path
|
return absolute_path
|
||||||
|
|
||||||
|
|
||||||
class FakeRequest:
|
|
||||||
pass
|
|
||||||
|
|
||||||
class OpenSlidesSockJSConnection(SockJSConnection):
|
class OpenSlidesSockJSConnection(SockJSConnection):
|
||||||
"""
|
"""
|
||||||
SockJS connection for OpenSlides.
|
SockJS connection for OpenSlides.
|
||||||
@ -80,36 +75,29 @@ class OpenSlidesSockJSConnection(SockJSConnection):
|
|||||||
"""
|
"""
|
||||||
Sends an OpenSlides object to all connected clients (waiters).
|
Sends an OpenSlides object to all connected clients (waiters).
|
||||||
"""
|
"""
|
||||||
# Send out internal HTTP request to get data from the REST api.
|
|
||||||
for waiter in cls.waiters:
|
for waiter in cls.waiters:
|
||||||
# Read waiter's former cookies and parse session cookie to new header object.
|
# Read waiter's former cookies and parse session cookie to get user instance.
|
||||||
headers = HTTPHeaders()
|
|
||||||
try:
|
try:
|
||||||
session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME]
|
session_cookie = waiter.connection_info.cookies[settings.SESSION_COOKIE_NAME]
|
||||||
|
except KeyError:
|
||||||
|
# There is no session cookie so use anonymous user here.
|
||||||
|
user = AnonymousUser()
|
||||||
|
else:
|
||||||
|
# Get session from session store and use it to retrieve the user.
|
||||||
engine = import_module(settings.SESSION_ENGINE)
|
engine = import_module(settings.SESSION_ENGINE)
|
||||||
session = engine.SessionStore(session_cookie)
|
session = engine.SessionStore(session_cookie.value)
|
||||||
|
fake_request = type('FakeRequest', (), {})()
|
||||||
request = FakeRequest()
|
fake_request.session = session
|
||||||
request.session = session
|
user = get_user(fake_request)
|
||||||
|
# Fetch serialized data and send them out to the waiter (client).
|
||||||
user = get_user(request)
|
serialized_instance_data = instance.get_access_permissions().get_serialized_data(instance, user)
|
||||||
serializer_class = instance.access_permissions.get_serializer_class(user)
|
if serialized_instance_data is not None:
|
||||||
serialized_instance_data = serializer_class(instance).data
|
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
'url': "foobar",
|
'status_code': 404 if is_delete else 200, # TODO: Refactor this. Use strings like 'change' or 'delete'.
|
||||||
'status_code': 404 if is_delete else 200,
|
'collection': instance.get_collection_string(),
|
||||||
'collection': instance.get_collection_name(),
|
'id': instance.get_rest_pk(),
|
||||||
'id': instance.id,
|
|
||||||
'data': serialized_instance_data}
|
'data': serialized_instance_data}
|
||||||
waiter.send(data)
|
waiter.send(data)
|
||||||
except KeyError:
|
|
||||||
# There is no session cookie
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
headers.add('Cookie', '%s=%s' % (settings.SESSION_COOKIE_NAME, session_cookie.value))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def run_tornado(addr, port, *args, **kwargs):
|
def run_tornado(addr, port, *args, **kwargs):
|
||||||
@ -152,22 +140,28 @@ def inform_changed_data(is_delete, *args):
|
|||||||
"""
|
"""
|
||||||
Informs all users about changed data.
|
Informs all users about changed data.
|
||||||
|
|
||||||
The arguments are Django/OpenSlides models.
|
The first argument is whether the object or the objects are deleted.
|
||||||
|
The other arguments are the changed or deleted Django/OpenSlides model
|
||||||
|
instances.
|
||||||
"""
|
"""
|
||||||
root_instances = set()
|
|
||||||
for instance in args:
|
|
||||||
try:
|
|
||||||
root_instances.add(instance.get_root_rest_element())
|
|
||||||
except AttributeError:
|
|
||||||
# Instance has no method get_root_rest_url. Just skip it.
|
|
||||||
pass
|
|
||||||
|
|
||||||
if settings.USE_TORNADO_AS_WSGI_SERVER:
|
if settings.USE_TORNADO_AS_WSGI_SERVER:
|
||||||
for root_instance in root_instances:
|
for instance in args:
|
||||||
OpenSlidesSockJSConnection.send_object(root_instance, is_delete)
|
try:
|
||||||
|
root_instance = instance.get_root_rest_element()
|
||||||
|
except AttributeError:
|
||||||
|
# Instance has no method get_root_rest_element. Just skip it.
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
if is_delete and instance == root_instance:
|
||||||
|
# A root instance is deleted.
|
||||||
|
OpenSlidesSockJSConnection.send_object(root_instance, is_delete)
|
||||||
|
else:
|
||||||
|
# A non root instance is deleted or any instance is just changed.
|
||||||
|
root_instance.refresh_from_db()
|
||||||
|
OpenSlidesSockJSConnection.send_object(root_instance, False)
|
||||||
else:
|
else:
|
||||||
pass
|
pass
|
||||||
# TODO: Implement big varainte with Apache or Nginx as wsgi webserver.
|
# TODO: Implement big variant with Apache or Nginx as WSGI webserver.
|
||||||
|
|
||||||
|
|
||||||
def inform_changed_data_receiver(sender, instance, **kwargs):
|
def inform_changed_data_receiver(sender, instance, **kwargs):
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
|
||||||
|
|
||||||
@ -19,15 +18,11 @@ class MinMaxIntegerField(models.IntegerField):
|
|||||||
|
|
||||||
class RESTModelMixin:
|
class RESTModelMixin:
|
||||||
"""
|
"""
|
||||||
Mixin for django models which are used in our rest api.
|
Mixin for Django models which are used in our REST API.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
access_permissions = None
|
access_permissions = None
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_collection_name(cls):
|
|
||||||
return "{0}/{1}".format(cls._meta.app_label.lower(), cls._meta.object_name.lower())
|
|
||||||
|
|
||||||
def get_root_rest_element(self):
|
def get_root_rest_element(self):
|
||||||
"""
|
"""
|
||||||
Returns the root rest instance.
|
Returns the root rest instance.
|
||||||
@ -36,20 +31,25 @@ class RESTModelMixin:
|
|||||||
"""
|
"""
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def get_root_rest_url(self):
|
def get_access_permissions(self):
|
||||||
"""
|
"""
|
||||||
Returns the detail url of the root model of this object.
|
Returns a container to handle access permissions for this model and
|
||||||
|
its corresponding viewset.
|
||||||
"""
|
"""
|
||||||
# Gets the default url-name in the same way as django rest framework
|
return self.access_permissions
|
||||||
# does in relations.HyperlinkedModelSerializer
|
|
||||||
root_instance = self.get_root_rest_element()
|
|
||||||
rest_url = '%s-detail' % type(root_instance)._meta.object_name.lower()
|
|
||||||
return reverse(rest_url, args=[str(root_instance.pk)])
|
|
||||||
|
|
||||||
def get_collection_string(self):
|
@classmethod
|
||||||
|
def get_collection_string(cls):
|
||||||
"""
|
"""
|
||||||
Returns the string representing the name of the collection.
|
Returns the string representing the name of the collection. Returns
|
||||||
|
None if this is not a so called root rest instance.
|
||||||
"""
|
"""
|
||||||
# TODO: find a way not to use the url. See #1791
|
# TODO Check if this is a root rest element class and return None if not.
|
||||||
from .rest_api import get_collection_and_id_from_url
|
return '/'.join((cls._meta.app_label.lower(), cls._meta.object_name.lower()))
|
||||||
return get_collection_and_id_from_url(self.get_root_rest_url())[0]
|
|
||||||
|
def get_rest_pk(self):
|
||||||
|
"""
|
||||||
|
Returns the primary key used in the REST API. By default this is
|
||||||
|
the database pk.
|
||||||
|
"""
|
||||||
|
return self.pk
|
||||||
|
@ -101,20 +101,17 @@ class PermissionMixin:
|
|||||||
|
|
||||||
The methods check_view_permissions or check_projector_requirements are
|
The methods check_view_permissions or check_projector_requirements are
|
||||||
evaluated. If both return False self.permission_denied() is called.
|
evaluated. If both return False self.permission_denied() is called.
|
||||||
Django REST framework's permission system is disabled.
|
Django REST Framework's permission system is disabled.
|
||||||
"""
|
|
||||||
|
|
||||||
def get_serializer_class(self):
|
Also connects container to handle access permissions for model and
|
||||||
"""
|
viewset.
|
||||||
TODO
|
"""
|
||||||
"""
|
access_permissions = None
|
||||||
serializer_class = self.access_permissions.get_serializer_class(self.request.user) if self.access_permissions is not None else None
|
|
||||||
return super().get_serializer_class() if serializer_class is None else serializer_class
|
|
||||||
|
|
||||||
def get_permissions(self):
|
def get_permissions(self):
|
||||||
"""
|
"""
|
||||||
Overriden method to check view and projector permissions. Returns an
|
Overridden method to check view and projector permissions. Returns an
|
||||||
empty interable so Django REST framework won't do any other
|
empty iterable so Django REST framework won't do any other
|
||||||
permission checks by evaluating Django REST framework style permission
|
permission checks by evaluating Django REST framework style permission
|
||||||
classes and the request passes.
|
classes and the request passes.
|
||||||
"""
|
"""
|
||||||
@ -126,6 +123,8 @@ class PermissionMixin:
|
|||||||
"""
|
"""
|
||||||
Override this and return True if the requesting user should be able to
|
Override this and return True if the requesting user should be able to
|
||||||
get access to your view.
|
get access to your view.
|
||||||
|
|
||||||
|
Use access permissions container for retrieve requests.
|
||||||
"""
|
"""
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -144,6 +143,24 @@ class PermissionMixin:
|
|||||||
break
|
break
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def get_access_permissions(self):
|
||||||
|
"""
|
||||||
|
Returns a container to handle access permissions for this viewset and
|
||||||
|
its corresponding model.
|
||||||
|
"""
|
||||||
|
return self.access_permissions
|
||||||
|
|
||||||
|
def get_serializer_class(self):
|
||||||
|
"""
|
||||||
|
Overridden method to return the serializer class given by the
|
||||||
|
access permissions container.
|
||||||
|
"""
|
||||||
|
if self.get_access_permissions() is not None:
|
||||||
|
serializer_class = self.get_access_permissions().get_serializer_class(self.request.user)
|
||||||
|
else:
|
||||||
|
serializer_class = super().get_serializer_class()
|
||||||
|
return serializer_class
|
||||||
|
|
||||||
|
|
||||||
class ModelSerializer(_ModelSerializer):
|
class ModelSerializer(_ModelSerializer):
|
||||||
"""
|
"""
|
||||||
@ -172,7 +189,7 @@ class GenericViewSet(PermissionMixin, _GenericViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class ModelViewSet(PermissionMixin, _ModelViewSet):
|
class ModelViewSet(PermissionMixin, _ModelViewSet):
|
||||||
access_permissions = None
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ReadOnlyModelViewSet(PermissionMixin, _ReadOnlyModelViewSet):
|
class ReadOnlyModelViewSet(PermissionMixin, _ReadOnlyModelViewSet):
|
||||||
@ -183,12 +200,13 @@ class ViewSet(PermissionMixin, _ViewSet):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
#TODO: Remove this method
|
||||||
def get_collection_and_id_from_url(url):
|
def get_collection_and_id_from_url(url):
|
||||||
"""
|
"""
|
||||||
Helper function. Returns a tuple containing the collection name and the id
|
Helper function. Returns a tuple containing the collection name and the id
|
||||||
extracted out of the given REST api URL.
|
extracted out of the given REST api URL.
|
||||||
|
|
||||||
For example get_collection_and_id_from_url('http://localhost/api/users/user/3/')
|
For example get_collection_and_id_from_url('http://localhost/rest/users/user/3/')
|
||||||
returns ('users/user', '3').
|
returns ('users/user', '3').
|
||||||
|
|
||||||
Raises OpenSlidesError if the URL is invalid.
|
Raises OpenSlidesError if the URL is invalid.
|
||||||
@ -196,5 +214,5 @@ def get_collection_and_id_from_url(url):
|
|||||||
path = urlparse(url).path
|
path = urlparse(url).path
|
||||||
match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path)
|
match = re.match(r'^/rest/(?P<collection>[-\w]+/[-\w]+)/(?P<id>[-\w]+)/$', path)
|
||||||
if not match:
|
if not match:
|
||||||
raise OpenSlidesError('Invalid REST api URL: %s' % url)
|
raise OpenSlidesError('Invalid REST API URL: %s' % url)
|
||||||
return match.group('collection'), match.group('id')
|
return match.group('collection'), match.group('id')
|
||||||
|
@ -10,7 +10,3 @@ def to_roman(number):
|
|||||||
return roman.toRoman(number)
|
return roman.toRoman(number)
|
||||||
except (roman.NotIntegerError, roman.OutOfRangeError):
|
except (roman.NotIntegerError, roman.OutOfRangeError):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def collection_name(model_class):
|
|
||||||
return "{1}/{2}".format(model_class.Meta.app_label.lower(), model_class.Meta.object_name)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user