Unproject elements when they are deleted. Fixed #3292.

This commit is contained in:
Norman Jäckel 2017-07-28 15:42:58 +02:00
parent c8e56b29a6
commit 305356f75f
10 changed files with 147 additions and 20 deletions

View File

@ -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].

View File

@ -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):
"""

View File

@ -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.

View File

@ -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.

View File

@ -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))

View File

@ -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);
};

View File

@ -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.

View File

@ -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):
"""

View File

@ -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):
"""

View File

@ -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.