Merge pull request #3336 from normanjaeckel/RemoveElementsProjector
Unproject elements when they are deleted. Fixed #3292.
This commit is contained in:
commit
0c8bf5c5ba
@ -62,6 +62,7 @@ Core:
|
||||
- Highlight list entries in a light blue, if a related object is projected
|
||||
(e. g. a list of speakers of a motion) [#3301].
|
||||
- Select the projector resolution with a slider and an aspect ratio [#3311].
|
||||
- Fixed bug the elements are projected and the deleted [#3336].
|
||||
- Delay the 'could not load projector' error 3 seconds to not irritate users
|
||||
with a slow internet connection [#3323].
|
||||
- Added config value for standard font size in PDF [#3332].
|
||||
|
@ -10,7 +10,7 @@ from django.utils.translation import ugettext as _
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Countdown
|
||||
from openslides.core.models import Countdown, Projector
|
||||
from openslides.utils.exceptions import OpenSlidesError
|
||||
from openslides.utils.models import RESTModelMixin
|
||||
from openslides.utils.utils import to_roman
|
||||
@ -284,6 +284,17 @@ class Item(RESTModelMixin, models.Model):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete an agenda item. Ensures that a respective
|
||||
list of speakers projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='agenda/list-of-speakers',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def title(self):
|
||||
"""
|
||||
|
@ -34,6 +34,8 @@ def listen_to_related_object_post_delete(sender, instance, **kwargs):
|
||||
if hasattr(instance, 'get_agenda_title'):
|
||||
content_type = ContentType.objects.get_for_model(instance)
|
||||
try:
|
||||
# Attention: This delete() call is also necessary to remove
|
||||
# respective active list of speakers projector elements.
|
||||
Item.objects.get(object_id=instance.pk, content_type=content_type).delete()
|
||||
except Item.DoesNotExist:
|
||||
# Item does not exist so we do not have to delete it.
|
||||
|
@ -8,7 +8,7 @@ from django.utils.translation import ugettext_noop
|
||||
|
||||
from openslides.agenda.models import Item, Speaker
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Tag
|
||||
from openslides.core.models import Projector, Tag
|
||||
from openslides.poll.models import (
|
||||
BaseOption,
|
||||
BasePoll,
|
||||
@ -165,14 +165,16 @@ class Assignment(RESTModelMixin, models.Model):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Retuns the context to generate the assignment slide.
|
||||
Customized method to delete an assignment. Ensures that a respective
|
||||
assignment projector element is disabled.
|
||||
"""
|
||||
return super().get_slide_context(
|
||||
polls=self.polls.filter(published=True),
|
||||
vote_results=self.vote_results(only_published=True),
|
||||
**context)
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='assignments/assignment',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def candidates(self):
|
||||
@ -415,6 +417,18 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
|
||||
class Meta:
|
||||
default_permissions = ()
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete an assignment poll. Ensures that a respective
|
||||
assignment projector element (with poll, so called poll slide) is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='assignments/assignment',
|
||||
id=self.assignment.pk,
|
||||
poll=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
def get_assignment(self):
|
||||
return self.assignment
|
||||
|
||||
@ -432,9 +446,6 @@ class AssignmentPoll(RESTModelMixin, CollectDefaultVotesMixin,
|
||||
def get_percent_base_choice(self):
|
||||
return config['assignments_poll_100_percent_base']
|
||||
|
||||
def get_slide_context(self, **context):
|
||||
return super().get_slide_context(poll=self)
|
||||
|
||||
def get_root_rest_element(self):
|
||||
"""
|
||||
Returns the assignment to this instance which is the root REST element.
|
||||
|
@ -192,6 +192,36 @@ class Projector(RESTModelMixin, models.Model):
|
||||
|
||||
return output
|
||||
|
||||
@classmethod
|
||||
def remove_any(cls, skip_autoupdate=False, **kwargs):
|
||||
"""
|
||||
Removes all projector elements from all projectors with matching kwargs.
|
||||
Additional properties of active projector elements are ignored:
|
||||
|
||||
Example: Sending {'name': 'assignments/assignment', 'id': 1} will remove
|
||||
also projector elements with {'name': 'assignments/assignment', 'id': 1, 'poll': 2}.
|
||||
"""
|
||||
# Loop over all projectors.
|
||||
for projector in cls.objects.all():
|
||||
change_projector_config = False
|
||||
projector_config = {}
|
||||
# Loop over all projector elements of this projector.
|
||||
for key, value in projector.config.items():
|
||||
# Check if the kwargs match this element.
|
||||
for kwarg_key, kwarg_value in kwargs.items():
|
||||
if not value.get(kwarg_key) == kwarg_value:
|
||||
# No match so the element should stay. Write it into
|
||||
# new config field and break the loop.
|
||||
projector_config[key] = value
|
||||
break
|
||||
else:
|
||||
# All kwargs match this projector element. So mark this
|
||||
# projector to be changed. Do not write it into new config field.
|
||||
change_projector_config = True
|
||||
if change_projector_config:
|
||||
projector.config = projector_config
|
||||
projector.save(skip_autoupdate=skip_autoupdate)
|
||||
|
||||
|
||||
class ProjectionDefault(RESTModelMixin, models.Model):
|
||||
"""
|
||||
@ -308,6 +338,17 @@ class ProjectorMessage(RESTModelMixin, models.Model):
|
||||
class Meta:
|
||||
default_permissions = ()
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a projector message. Ensures that a respective
|
||||
projector message projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='core/projector-message',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
|
||||
class Countdown(RESTModelMixin, models.Model):
|
||||
"""
|
||||
@ -326,6 +367,17 @@ class Countdown(RESTModelMixin, models.Model):
|
||||
class Meta:
|
||||
default_permissions = ()
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a countdown. Ensures that a respective
|
||||
countdown projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='core/countdown',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
def control(self, action):
|
||||
if action not in ('start', 'stop', 'reset'):
|
||||
raise ValueError("Action must be 'start', 'stop' or 'reset', not {}.".format(action))
|
||||
|
@ -1343,10 +1343,6 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
Countdown.create(countdown);
|
||||
};
|
||||
$scope.removeCountdown = function (countdown) {
|
||||
var isProjectedIds = countdown.isProjected();
|
||||
_.forEach(isProjectedIds, function(id) {
|
||||
countdown.project(id);
|
||||
});
|
||||
Countdown.destroy(countdown.id);
|
||||
};
|
||||
|
||||
@ -1359,10 +1355,6 @@ angular.module('OpenSlidesApp.core.site', [
|
||||
ProjectorMessage.create(message);
|
||||
};
|
||||
$scope.removeMessage = function (message) {
|
||||
var isProjectedIds = message.isProjected();
|
||||
_.forEach(isProjectedIds, function(id) {
|
||||
message.project(id);
|
||||
});
|
||||
ProjectorMessage.destroy(message.id);
|
||||
};
|
||||
|
||||
|
@ -2,6 +2,7 @@ from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from ..core.models import Projector
|
||||
from ..utils.autoupdate import inform_changed_data
|
||||
from ..utils.models import RESTModelMixin
|
||||
from .access_permissions import MediafileAccessPermissions
|
||||
@ -63,6 +64,17 @@ class Mediafile(RESTModelMixin, models.Model):
|
||||
inform_changed_data(self.uploader)
|
||||
return result
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a mediafile. Ensures that a respective
|
||||
mediafile projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='mediafiles/mediafile',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
def get_filesize(self):
|
||||
"""
|
||||
Transforms bytes to kilobytes or megabytes. Returns the size as string.
|
||||
|
@ -10,7 +10,7 @@ from jsonfield import JSONField
|
||||
|
||||
from openslides.agenda.models import Item
|
||||
from openslides.core.config import config
|
||||
from openslides.core.models import Tag
|
||||
from openslides.core.models import Projector, Tag
|
||||
from openslides.mediafiles.models import Mediafile
|
||||
from openslides.poll.models import (
|
||||
BaseOption,
|
||||
@ -300,6 +300,17 @@ class Motion(RESTModelMixin, models.Model):
|
||||
if not skip_autoupdate:
|
||||
inform_changed_data(self)
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a motion. Ensures that a respective
|
||||
motion projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='motions/motion',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
def version_data_changed(self, version):
|
||||
"""
|
||||
Compare the version with the last version of the motion.
|
||||
@ -859,6 +870,17 @@ class MotionBlock(RESTModelMixin, models.Model):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a motion block. Ensures that a respective
|
||||
motion block projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='motions/motion-block',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def agenda_item(self):
|
||||
"""
|
||||
|
@ -2,6 +2,7 @@ from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.db import models
|
||||
|
||||
from ..agenda.models import Item
|
||||
from ..core.models import Projector
|
||||
from ..mediafiles.models import Mediafile
|
||||
from ..utils.models import RESTModelMixin
|
||||
from .access_permissions import TopicAccessPermissions
|
||||
@ -42,6 +43,17 @@ class Topic(RESTModelMixin, models.Model):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete a topic. Ensures that a respective
|
||||
topic projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='topics/topic',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
@property
|
||||
def agenda_item(self):
|
||||
"""
|
||||
|
@ -13,6 +13,7 @@ from django.db import models
|
||||
from django.db.models import Prefetch, Q
|
||||
from jsonfield import JSONField
|
||||
|
||||
from ..core.models import Projector
|
||||
from ..utils.collection import CollectionElement
|
||||
from ..utils.models import RESTModelMixin
|
||||
from .access_permissions import (
|
||||
@ -210,6 +211,17 @@ class User(RESTModelMixin, PermissionsMixin, AbstractBaseUser):
|
||||
CollectionElement.from_instance(self)
|
||||
return super().save(*args, **kwargs)
|
||||
|
||||
def delete(self, skip_autoupdate=False, *args, **kwargs):
|
||||
"""
|
||||
Customized method to delete an user. Ensures that a respective
|
||||
user projector element is disabled.
|
||||
"""
|
||||
Projector.remove_any(
|
||||
skip_autoupdate=skip_autoupdate,
|
||||
name='users/user',
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs)
|
||||
|
||||
def has_perm(self, perm):
|
||||
"""
|
||||
This method is closed. Do not use it but use openslides.utils.auth.has_perm.
|
||||
|
Loading…
Reference in New Issue
Block a user