Protect element fields from the projector model. Fixes creation of
projectors.
This commit is contained in:
parent
fc5f6f4e54
commit
5a1f638f8d
@ -148,11 +148,7 @@ export class ProjectorListComponent extends BaseViewComponent implements OnInit
|
|||||||
public create(): void {
|
public create(): void {
|
||||||
if (this.createForm.valid && this.projectorToCreate) {
|
if (this.createForm.valid && this.projectorToCreate) {
|
||||||
this.projectorToCreate.patchValues(this.createForm.value as Projector);
|
this.projectorToCreate.patchValues(this.createForm.value as Projector);
|
||||||
// TODO: the server shouldn't want to have element data..
|
|
||||||
this.projectorToCreate.patchValues({
|
this.projectorToCreate.patchValues({
|
||||||
elements: [{ name: 'core/clock', stable: true }],
|
|
||||||
elements_preview: [],
|
|
||||||
elements_history: [],
|
|
||||||
reference_projector_id: this.projectors[0].reference_projector_id
|
reference_projector_id: this.projectors[0].reference_projector_id
|
||||||
});
|
});
|
||||||
this.repo.create(this.projectorToCreate).then(() => (this.projectorToCreate = null), this.raiseError);
|
this.repo.create(this.projectorToCreate).then(() => (this.projectorToCreate = null), this.raiseError);
|
||||||
|
@ -91,7 +91,7 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
show_title = models.BooleanField(default=True)
|
show_title = models.BooleanField(default=True)
|
||||||
show_logo = models.BooleanField(default=True)
|
show_logo = models.BooleanField(default=True)
|
||||||
|
|
||||||
name = models.CharField(max_length=255, unique=True, blank=True)
|
name = models.CharField(max_length=255, unique=True)
|
||||||
|
|
||||||
reference_projector = models.ForeignKey(
|
reference_projector = models.ForeignKey(
|
||||||
"self",
|
"self",
|
||||||
|
@ -1,7 +1,13 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ..utils.projector import projector_slides
|
from ..utils.projector import projector_slides
|
||||||
from ..utils.rest_api import Field, IntegerField, ModelSerializer, ValidationError
|
from ..utils.rest_api import (
|
||||||
|
Field,
|
||||||
|
IdPrimaryKeyRelatedField,
|
||||||
|
IntegerField,
|
||||||
|
ModelSerializer,
|
||||||
|
ValidationError,
|
||||||
|
)
|
||||||
from ..utils.validate import validate_html
|
from ..utils.validate import validate_html
|
||||||
from .models import (
|
from .models import (
|
||||||
ChatMessage,
|
ChatMessage,
|
||||||
@ -77,13 +83,17 @@ class ProjectorSerializer(ModelSerializer):
|
|||||||
Serializer for core.models.Projector objects.
|
Serializer for core.models.Projector objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
elements = JSONSerializerField(validators=[elements_validator])
|
elements = JSONSerializerField(read_only=True)
|
||||||
elements_preview = JSONSerializerField(validators=[elements_validator])
|
elements_preview = JSONSerializerField(read_only=True)
|
||||||
elements_history = JSONSerializerField(validators=[elements_array_validator])
|
elements_history = JSONSerializerField(read_only=True)
|
||||||
|
|
||||||
width = IntegerField(min_value=800, max_value=3840, required=False)
|
width = IntegerField(min_value=800, max_value=3840, required=False)
|
||||||
height = IntegerField(min_value=340, max_value=2880, required=False)
|
height = IntegerField(min_value=340, max_value=2880, required=False)
|
||||||
|
|
||||||
|
projectiondefaults = IdPrimaryKeyRelatedField(
|
||||||
|
many=True, required=False, queryset=ProjectionDefault.objects.all()
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Projector
|
model = Projector
|
||||||
fields = (
|
fields = (
|
||||||
|
@ -57,6 +57,7 @@ from .models import (
|
|||||||
ProjectorMessage,
|
ProjectorMessage,
|
||||||
Tag,
|
Tag,
|
||||||
)
|
)
|
||||||
|
from .serializers import elements_array_validator, elements_validator
|
||||||
|
|
||||||
|
|
||||||
# Special Django views
|
# Special Django views
|
||||||
@ -139,6 +140,11 @@ class ProjectorViewSet(ModelViewSet):
|
|||||||
result = False
|
result = False
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
def perform_create(self, serializer):
|
||||||
|
projector = serializer.save()
|
||||||
|
projector.elements = [{"name": "core/clock", "stable": True}]
|
||||||
|
projector.save()
|
||||||
|
|
||||||
def destroy(self, *args, **kwargs):
|
def destroy(self, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
REST API operation for DELETE requests.
|
REST API operation for DELETE requests.
|
||||||
@ -184,22 +190,24 @@ class ProjectorViewSet(ModelViewSet):
|
|||||||
"delete_last_history_element", False
|
"delete_last_history_element", False
|
||||||
)
|
)
|
||||||
|
|
||||||
changed_data = {}
|
|
||||||
if elements is not None:
|
if elements is not None:
|
||||||
changed_data["elements"] = elements
|
elements_validator(elements)
|
||||||
|
projector.elements = elements
|
||||||
|
|
||||||
if preview is not None:
|
if preview is not None:
|
||||||
changed_data["elements_preview"] = preview
|
elements_validator(preview)
|
||||||
|
projector.elements_preview = preview
|
||||||
|
|
||||||
|
elements_history = None
|
||||||
if history_element is not None and delete_last_history_element is False:
|
if history_element is not None and delete_last_history_element is False:
|
||||||
history = projector.elements_history + [history_element]
|
elements_history = projector.elements_history + [history_element]
|
||||||
changed_data["elements_history"] = history
|
|
||||||
if history_element is None and delete_last_history_element is True:
|
if history_element is None and delete_last_history_element is True:
|
||||||
history = projector.elements_history[:-1]
|
elements_history = projector.elements_history[:-1]
|
||||||
changed_data["elements_history"] = history
|
if elements_history is not None:
|
||||||
|
elements_array_validator(elements_history)
|
||||||
serializer = self.get_serializer(projector, data=changed_data, partial=True)
|
projector.elements_history = elements_history
|
||||||
serializer.is_valid(raise_exception=True)
|
|
||||||
self.perform_update(serializer)
|
|
||||||
|
|
||||||
|
projector.save()
|
||||||
return Response()
|
return Response()
|
||||||
|
|
||||||
@detail_route(methods=["post"])
|
@detail_route(methods=["post"])
|
||||||
|
6
tests/common_groups.py
Normal file
6
tests/common_groups.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# In this file are the default group ids which should be used in all tests.
|
||||||
|
|
||||||
|
GROUP_DEFAULT_PK = 1
|
||||||
|
GROUP_ADMIN_PK = 2
|
||||||
|
GROUP_DELEGATE_PK = 3
|
||||||
|
GROUP_STAFF_PK = 4
|
@ -1,11 +1,14 @@
|
|||||||
import pytest
|
import pytest
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
from openslides.core.config import config
|
from openslides.core.config import config
|
||||||
from openslides.core.models import ChatMessage, Projector, Tag
|
from openslides.core.models import ChatMessage, Projector, Tag
|
||||||
from openslides.users.models import User
|
from openslides.users.models import User
|
||||||
|
from openslides.utils.autoupdate import inform_changed_data
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
from tests.common_groups import GROUP_ADMIN_PK, GROUP_DELEGATE_PK
|
||||||
|
|
||||||
from ..helpers import count_queries
|
from ..helpers import count_queries
|
||||||
|
|
||||||
@ -59,6 +62,146 @@ def test_config_db_queries():
|
|||||||
assert count_queries(Tag.get_elements) == 1
|
assert count_queries(Tag.get_elements) == 1
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectorViewSet(TestCase):
|
||||||
|
"""
|
||||||
|
Tests (currently just parts) of the ProjectorViewSet.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username="admin", password="admin")
|
||||||
|
|
||||||
|
def test_create(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-list"), {"name": "test_name_efIOLJHF32f&EF)NG3fw"}
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
|
||||||
|
# pk=1 should be the default projector and pk=2 the new one
|
||||||
|
self.assertEqual(Projector.objects.all().count(), 2)
|
||||||
|
self.assertTrue(Projector.objects.filter(pk=2).exists())
|
||||||
|
projector = Projector.objects.get(pk=2)
|
||||||
|
self.assertEqual(projector.name, "test_name_efIOLJHF32f&EF)NG3fw")
|
||||||
|
self.assertEqual(projector.elements, [{"name": "core/clock", "stable": True}])
|
||||||
|
|
||||||
|
def test_create_no_data(self):
|
||||||
|
response = self.client.post(reverse("projector-list"))
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(Projector.objects.all().count(), 1)
|
||||||
|
|
||||||
|
def test_no_permission(self):
|
||||||
|
admin = User.objects.get(username="admin")
|
||||||
|
admin.groups.add(GROUP_DELEGATE_PK)
|
||||||
|
admin.groups.remove(GROUP_ADMIN_PK)
|
||||||
|
inform_changed_data(admin)
|
||||||
|
|
||||||
|
response = self.client.post(reverse("projector-list"))
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
self.assertEqual(Projector.objects.all().count(), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class Projection(TestCase):
|
||||||
|
"""
|
||||||
|
Tests the projection view.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.client = APIClient()
|
||||||
|
self.client.login(username="admin", password="admin")
|
||||||
|
self.projector = Projector.objects.get(pk=1) # the default projector
|
||||||
|
|
||||||
|
def test_add_element(self):
|
||||||
|
elements = [{"name": "core/clock"}]
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"elements": elements},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, elements)
|
||||||
|
self.assertEqual(self.projector.elements_preview, [])
|
||||||
|
self.assertEqual(self.projector.elements_history, [])
|
||||||
|
|
||||||
|
def test_add_element_without_name(self):
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"elements": [{}]},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, [])
|
||||||
|
self.assertEqual(self.projector.elements_preview, [])
|
||||||
|
self.assertEqual(self.projector.elements_history, [])
|
||||||
|
|
||||||
|
def test_no_permissions(self):
|
||||||
|
admin = User.objects.get(username="admin")
|
||||||
|
admin.groups.add(GROUP_DELEGATE_PK)
|
||||||
|
admin.groups.remove(GROUP_ADMIN_PK)
|
||||||
|
inform_changed_data(admin)
|
||||||
|
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]), {}, format="json"
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_remove_element(self):
|
||||||
|
self.projector.elements = [{"name": "core/clock"}]
|
||||||
|
self.projector.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"elements": []},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, [])
|
||||||
|
self.assertEqual(self.projector.elements_preview, [])
|
||||||
|
self.assertEqual(self.projector.elements_history, [])
|
||||||
|
|
||||||
|
def test_add_element_to_history(self):
|
||||||
|
element = [{"name": "core/clock"}]
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"append_to_history": element},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, [])
|
||||||
|
self.assertEqual(self.projector.elements_preview, [])
|
||||||
|
self.assertEqual(self.projector.elements_history, [element])
|
||||||
|
|
||||||
|
def test_remove_last_history_element(self):
|
||||||
|
element1 = [{"name": "core/clock"}]
|
||||||
|
element2 = [{"name": "motions/motion"}]
|
||||||
|
self.projector.elements_history = [element1, element2]
|
||||||
|
self.projector.save()
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"delete_last_history_element": True},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, [])
|
||||||
|
self.assertEqual(self.projector.elements_preview, [])
|
||||||
|
self.assertEqual(self.projector.elements_history, [element1])
|
||||||
|
|
||||||
|
def test_set_preview(self):
|
||||||
|
elements = [{"name": "core/clock"}]
|
||||||
|
response = self.client.post(
|
||||||
|
reverse("projector-project", args=[self.projector.pk]),
|
||||||
|
{"preview": elements},
|
||||||
|
format="json",
|
||||||
|
)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.projector = Projector.objects.get(pk=1)
|
||||||
|
self.assertEqual(self.projector.elements, [])
|
||||||
|
self.assertEqual(self.projector.elements_preview, elements)
|
||||||
|
self.assertEqual(self.projector.elements_history, [])
|
||||||
|
|
||||||
|
|
||||||
class ChatMessageViewSet(TestCase):
|
class ChatMessageViewSet(TestCase):
|
||||||
"""
|
"""
|
||||||
Tests requests to deal with chat messages.
|
Tests requests to deal with chat messages.
|
||||||
|
@ -23,16 +23,16 @@ from openslides.motions.models import (
|
|||||||
from openslides.utils.auth import get_group_model
|
from openslides.utils.auth import get_group_model
|
||||||
from openslides.utils.autoupdate import inform_changed_data
|
from openslides.utils.autoupdate import inform_changed_data
|
||||||
from openslides.utils.test import TestCase
|
from openslides.utils.test import TestCase
|
||||||
|
from tests.common_groups import (
|
||||||
|
GROUP_ADMIN_PK,
|
||||||
|
GROUP_DEFAULT_PK,
|
||||||
|
GROUP_DELEGATE_PK,
|
||||||
|
GROUP_STAFF_PK,
|
||||||
|
)
|
||||||
|
|
||||||
from ..helpers import count_queries
|
from ..helpers import count_queries
|
||||||
|
|
||||||
|
|
||||||
GROUP_DEFAULT_PK = 1
|
|
||||||
GROUP_ADMIN_PK = 2
|
|
||||||
GROUP_DELEGATE_PK = 3
|
|
||||||
GROUP_STAFF_PK = 4
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.django_db(transaction=False)
|
@pytest.mark.django_db(transaction=False)
|
||||||
def test_motion_db_queries():
|
def test_motion_db_queries():
|
||||||
"""
|
"""
|
||||||
|
Loading…
Reference in New Issue
Block a user