diff --git a/CHANGELOG b/CHANGELOG index cd5ca7186..37dfc1c8f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -11,7 +11,7 @@ Version 2.0.0 (unreleased) Agenda: - Updated the tests and changed only small internal parts of method of the agenda model. No API changes. -- Deprecated mptt. +- Removed mptt. Assignments: - Renamed app from assignment to assignments. - Massive refactoring and cleanup of the app. @@ -48,6 +48,7 @@ Other: Sheets libraries. - Used setup.cfg for development tools. - Fixed bug in LocalizedModelMultipleChoiceField. +- Removed the django error pages Version 1.7 (2015-02-16) diff --git a/openslides/__main__.py b/openslides/__main__.py index 003ed36c7..9eb0b4a39 100644 --- a/openslides/__main__.py +++ b/openslides/__main__.py @@ -7,14 +7,15 @@ from django.core.management import execute_from_command_line from openslides import __version__ as openslides_version from openslides.utils.main import ( - get_default_settings_path, - setup_django_settings_module, - write_settings, - UnknownCommand, ExceptionArgumentParser, + UnknownCommand, + get_default_settings_path, get_development_settings_path, + is_development, + setup_django_settings_module, start_browser, - is_development) + write_settings, +) def main(): diff --git a/openslides/account/__init__.py b/openslides/account/__init__.py deleted file mode 100644 index 217ee87bc..000000000 --- a/openslides/account/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'openslides.account.apps.AccountAppConfig' diff --git a/openslides/account/apps.py b/openslides/account/apps.py deleted file mode 100644 index bc1350505..000000000 --- a/openslides/account/apps.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.apps import AppConfig - - -class AccountAppConfig(AppConfig): - name = 'openslides.account' - verbose_name = 'OpenSlides Account' - - def ready(self): - # Load widget. - from . import widgets # noqa diff --git a/openslides/account/templates/account/widget_personal_info.html b/openslides/account/templates/account/widget_personal_info.html deleted file mode 100644 index d8b37ba8e..000000000 --- a/openslides/account/templates/account/widget_personal_info.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} - {% for infoblock in infoblocks %} - {% if infoblock.is_active %} - - {% if not forloop.last %} -
- {% endif %} - {% endif %} - {% endfor %} -{% endblock %} diff --git a/openslides/account/widgets.py b/openslides/account/widgets.py deleted file mode 100644 index 09fb46d23..000000000 --- a/openslides/account/widgets.py +++ /dev/null @@ -1,42 +0,0 @@ -from django.contrib.auth.models import AnonymousUser -from django.utils.translation import ugettext_lazy - -from openslides.utils.personal_info import PersonalInfo -from openslides.utils.widgets import Widget - - -class PersonalInfoWidget(Widget): - """ - Provides a widget for personal info. It shows all info block given by the - personal info api. See openslides.utils.personal_info.PersonalInfo. - """ - name = 'personal_info' - verbose_name = ugettext_lazy('My personal info') - default_column = 1 - default_weight = 80 - template_name = 'account/widget_personal_info.html' - icon_css_class = 'icon-flag' - - def check_permission(self): - """ - The widget is disabled for anonymous users. - """ - return not isinstance(self.request.user, AnonymousUser) - - def is_active(self): - """ - The widget is disabled if there are no info blocks at the moment. - """ - for infoblock in PersonalInfo.get_all(self.request): - if infoblock.is_active(): - active = super(PersonalInfoWidget, self).is_active() - break - else: - active = False - return active - - def get_context_data(self, **context): - """ - Adds the context to the widget. - """ - return super(PersonalInfoWidget, self).get_context_data(infoblocks=PersonalInfo.get_all(self.request), **context) diff --git a/openslides/agenda/apps.py b/openslides/agenda/apps.py index e7a02aefc..629075e07 100644 --- a/openslides/agenda/apps.py +++ b/openslides/agenda/apps.py @@ -6,28 +6,16 @@ class AgendaAppConfig(AppConfig): verbose_name = 'OpenSlides Agenda' def ready(self): - # Load main menu entry, personal info and widgets. - # Do this by just importing all from these files. - from . import main_menu, personal_info, widgets # noqa - # Import all required stuff. from django.db.models.signals import pre_delete from openslides.config.signals import config_signal - from openslides.projector.api import register_slide - from openslides.projector.signals import projector_overlays from openslides.utils.rest_api import router - from .signals import agenda_list_of_speakers, setup_agenda_config, listen_to_related_object_delete_signal - from .slides import agenda_slide + from .signals import setup_agenda_config, listen_to_related_object_delete_signal from .views import ItemViewSet # Connect signals. config_signal.connect(setup_agenda_config, dispatch_uid='setup_agenda_config') - projector_overlays.connect(agenda_list_of_speakers, dispatch_uid='agenda_list_of_speakers') pre_delete.connect(listen_to_related_object_delete_signal, dispatch_uid='agenda_listen_to_related_object_delete_signal') - # Register slides. - Item = self.get_model('Item') - register_slide('agenda', agenda_slide, Item) - # Register viewsets. router.register('agenda/item', ItemViewSet) diff --git a/openslides/agenda/csv_import.py b/openslides/agenda/csv_import.py deleted file mode 100644 index 65fb90ff0..000000000 --- a/openslides/agenda/csv_import.py +++ /dev/null @@ -1,53 +0,0 @@ -import csv -import re - -from django.db import transaction -from django.utils.translation import ugettext as _ - -from openslides.utils import csv_ext - -from .models import Item - - -def import_agenda_items(csvfile): - """ - Performs the import of agenda items form a csv file. - """ - # Check encoding - try: - csvfile.read().decode('utf8') - except UnicodeDecodeError: - return_value = '', '', _('Import file has wrong character encoding, only UTF-8 is supported!') - else: - csvfile.seek(0) - # Check dialect - dialect = csv.Sniffer().sniff(csvfile.readline().decode('utf8')) - dialect = csv_ext.patchup(dialect) - csvfile.seek(0) - # Parse CSV file - with transaction.atomic(): - success_lines = [] - error_lines = [] - for (line_no, line) in enumerate(csv.reader( - (line.decode('utf8') for line in csvfile.readlines()), dialect=dialect)): - if line_no == 0: - # Do not read the header line - continue - # Check format - try: - title, text, duration = line[:3] - except ValueError: - error_lines.append(line_no + 1) - continue - if duration and re.match('^(?:[0-9]{1,2}:[0-5][0-9]|[0-9]+)$', duration) is None: - error_lines.append(line_no + 1) - continue - Item.objects.create(title=title, text=text, duration=duration) - success_lines.append(line_no + 1) - success = _('%d items successfully imported.') % len(success_lines) - if error_lines: - error = _('Error in the following lines: %s.') % ', '.join(str(number) for number in error_lines) - else: - error = '' - return_value = success, '', error - return return_value diff --git a/openslides/agenda/forms.py b/openslides/agenda/forms.py deleted file mode 100644 index aab531b27..000000000 --- a/openslides/agenda/forms.py +++ /dev/null @@ -1,80 +0,0 @@ -import re - -from ckeditor.widgets import CKEditorWidget -from django import forms -from django.utils.translation import ugettext_lazy -from mptt.forms import TreeNodeChoiceField - -from openslides.utils.forms import CssClassMixin, CleanHtmlFormMixin -from openslides.users.models import User - -from .models import Item, Speaker - - -class ItemForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm): - """ - Form to create of update an item. - """ - clean_html_fields = ('text', ) - - parent = TreeNodeChoiceField( - queryset=Item.objects.all(), label=ugettext_lazy("Parent item"), required=False) - - duration = forms.RegexField( - regex=re.compile('^(?:[0-9]{1,2}:[0-5][0-9]|[0-9]+)$'), - error_message=ugettext_lazy("Invalid format. Hours from 0 to 99 and minutes from 00 to 59"), - max_length=5, - required=False, - label=ugettext_lazy("Duration"), - help_text=ugettext_lazy('Input format: HH:MM or M or MM or MMM')) - - class Meta: - model = Item - fields = ('item_number', 'title', 'text', 'comment', 'tags', 'type', 'duration', 'parent', 'speaker_list_closed') - widgets = {'text': CKEditorWidget(config_name='images')} - - -class RelatedItemForm(ItemForm): - """ - Form to update an related item. - """ - class Meta: - model = Item - fields = ('comment', 'duration', 'parent', 'speaker_list_closed') - - -class ItemOrderForm(CssClassMixin, forms.Form): - """ - Form to change the order of the items. - """ - weight = forms.IntegerField( - widget=forms.HiddenInput(attrs={'class': 'menu-weight'})) - self = forms.IntegerField( - widget=forms.HiddenInput(attrs={'class': 'menu-mlid'})) - parent = forms.IntegerField( - widget=forms.HiddenInput(attrs={'class': 'menu-plid'})) - - -class AppendSpeakerForm(CssClassMixin, forms.Form): - """ - Form to set an user to a list of speakers. - """ - speaker = forms.ModelChoiceField( - User.objects.all(), - widget=forms.Select(attrs={'class': 'medium-input'}), - label=ugettext_lazy("Add participant")) - - def __init__(self, item, *args, **kwargs): - self.item = item - return super(AppendSpeakerForm, self).__init__(*args, **kwargs) - - def clean_speaker(self): - """ - Checks, that the user is not already on the list. - """ - speaker = self.cleaned_data['speaker'] - if Speaker.objects.filter(user=speaker, item=self.item, begin_time=None).exists(): - raise forms.ValidationError(ugettext_lazy( - '%s is already on the list of speakers.' - % str(speaker))) - return speaker diff --git a/openslides/agenda/main_menu.py b/openslides/agenda/main_menu.py deleted file mode 100644 index f97d00804..000000000 --- a/openslides/agenda/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class AgendaMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the agenda app. - """ - verbose_name = ugettext_lazy('Agenda') - required_permission = 'agenda.can_see' - default_weight = 20 - pattern_name = '/agenda' # TODO: use generic solution, see issue #1469 - icon_css_class = 'glyphicon-calendar' diff --git a/openslides/agenda/models.py b/openslides/agenda/models.py index edf383215..0000866e5 100644 --- a/openslides/agenda/models.py +++ b/openslides/agenda/models.py @@ -4,30 +4,22 @@ from django.contrib.auth.models import AnonymousUser from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError -from django.core.urlresolvers import reverse from django.db import models from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop -from mptt.models import MPTTModel, TreeForeignKey from openslides.config.api import config from openslides.core.models import Tag -from openslides.projector.api import (reset_countdown, - start_countdown, stop_countdown) from openslides.projector.models import SlideMixin +from openslides.users.models import User from openslides.utils.exceptions import OpenSlidesError -from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.rest_api import RESTModelMixin from openslides.utils.utils import to_roman -from openslides.users.models import User -# TODO: remove mptt after removing the django views and forms -class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel): +class Item(RESTModelMixin, SlideMixin, models.Model): """ An Agenda Item - - MPTT-model. See http://django-mptt.github.com/django-mptt/ """ slide_callback_name = 'agenda' @@ -76,8 +68,7 @@ class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel): The intended duration for the topic. """ - parent = TreeForeignKey('self', null=True, blank=True, - related_name='children') + parent = models.ForeignKey('self', null=True, blank=True, related_name='children') """ The parent item in the agenda tree. """ @@ -119,44 +110,35 @@ class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel): ('can_manage', ugettext_noop("Can manage agenda")), ('can_see_orga_items', ugettext_noop("Can see orga items and time scheduling of agenda"))) - class MPTTMeta: - order_insertion_by = ['weight'] + def __str__(self): + return self.get_title() def clean(self): """ Ensures that the children of orga items are only orga items. """ - if self.type == self.AGENDA_ITEM and self.parent is not None and self.parent.type == self.ORGANIZATIONAL_ITEM: + if (self.type == self.AGENDA_ITEM and + self.parent is not None and + self.parent.type == self.ORGANIZATIONAL_ITEM): raise ValidationError(_('Agenda items can not be child elements of an organizational item.')) - if self.type == self.ORGANIZATIONAL_ITEM and self.get_descendants().filter(type=self.AGENDA_ITEM).exists(): + if (self.type == self.ORGANIZATIONAL_ITEM and + self.children.filter(type=self.AGENDA_ITEM).exists()): raise ValidationError(_('Organizational items can not have agenda items as child elements.')) return super().clean() - def __str__(self): - return self.get_title() - - def get_absolute_url(self, link='detail'): + def delete(self, with_children=False): """ - Return the URL to this item. + Delete the Item. - The link can be detail, update or delete. + If with_children is True, all children of the item will be deleted as + well. If with_children is False, all children will be children of the + parent of the item. """ - if link == 'detail': - url = reverse('item_view', args=[str(self.id)]) - elif link == 'update': - url = reverse('item_edit', args=[str(self.id)]) - elif link == 'delete': - url = reverse('item_delete', args=[str(self.id)]) - elif link == 'projector_list_of_speakers': - url = '%s&type=list_of_speakers' % super().get_absolute_url('projector') - elif link == 'projector_summary': - url = '%s&type=summary' % super().get_absolute_url('projector') - elif (link in ('projector', 'projector_preview') and - self.content_object and isinstance(self.content_object, SlideMixin)): - url = self.content_object.get_absolute_url(link) - else: - url = super().get_absolute_url(link) - return url + if not with_children: + for child in self.children.all(): + child.parent = self.parent + child.save() + super().delete() def get_title(self): """ @@ -183,39 +165,6 @@ class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel): except AttributeError: raise NotImplementedError('You have to provide a get_agenda_title_supplement method on your related model.') - @property - def weight_form(self): - """ - Return the WeightForm for this item. - """ - from openslides.agenda.forms import ItemOrderForm - try: - parent = self.parent.id - except AttributeError: - parent = 0 - initial = { - 'weight': self.weight, - 'self': self.id, - 'parent': parent, - } - return ItemOrderForm(initial=initial, prefix="i%d" % self.id) - - def delete(self, with_children=False): - """ - Delete the Item. - - If with_children is True, all children of the item will be deleted as - well. If with_children is False, all children will be children of the - parent of the item. - """ - if not with_children: - for child in self.get_children(): - child.move_to(self.parent) - child.save() - super().delete() - # TODO: Try to remove the rebuild call - Item.objects.rebuild() - def get_list_of_speakers(self, old_speakers_count=None, coming_speakers_count=None): """ Returns the list of speakers as a list of dictionaries. Each @@ -316,27 +265,28 @@ class Item(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, MPTTModel): Returns the number of this agenda item. """ if self.type == self.AGENDA_ITEM: - if self.is_root_node(): + if self.parent is None: + sibling_no = self.sibling_no() if config['agenda_numeral_system'] == 'arabic': - return str(self._calc_sibling_no()) + return str(sibling_no) else: # config['agenda_numeral_system'] == 'roman' - return to_roman(self._calc_sibling_no()) + return to_roman(sibling_no) else: - return '%s.%s' % (self.parent.calc_item_no(), self._calc_sibling_no()) + return '%s.%s' % (self.parent.calc_item_no(), self.sibling_no()) else: return '' - def _calc_sibling_no(self): + def sibling_no(self): """ - Counts all siblings on the same level which are AGENDA_ITEMs. + Counts how many AGENDA_ITEMS with the same parent (siblings) have a + smaller weight then this item. + + Returns this number + 1 or 0 when self is not an AGENDA_ITEM. """ - sibling_no = 0 - prev_sibling = self.get_previous_sibling() - while prev_sibling is not None: - if prev_sibling.type == self.AGENDA_ITEM: - sibling_no += 1 - prev_sibling = prev_sibling.get_previous_sibling() - return sibling_no + 1 + return Item.objects.filter( + parent=self.parent, + type=self.AGENDA_ITEM, + weight__lte=self.weight).count() class SpeakerManager(models.Manager): @@ -353,7 +303,7 @@ class SpeakerManager(models.Manager): return self.create(item=item, user=user, weight=weight + 1) -class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model): +class Speaker(RESTModelMixin, models.Model): """ Model for the Speaker list. """ @@ -393,16 +343,6 @@ class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model): def __str__(self): return str(self.user) - def get_absolute_url(self, link='detail'): - if link == 'detail': - url = self.user.get_absolute_url('detail') - elif link == 'delete': - url = reverse('agenda_speaker_delete', - args=[self.item.pk, self.pk]) - else: - url = super(Speaker, self).get_absolute_url(link) - return url - def begin_speach(self): """ Let the user speak. @@ -421,8 +361,10 @@ class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model): self.save() # start countdown if config['agenda_couple_countdown_and_speakers']: - reset_countdown() - start_countdown() + # TODO: Fix me with the new countdown api + # reset_countdown() + # start_countdown() + pass def end_speach(self): """ @@ -432,7 +374,9 @@ class Speaker(RESTModelMixin, AbsoluteUrlMixin, models.Model): self.save() # stop countdown if config['agenda_couple_countdown_and_speakers']: - stop_countdown() + # TODO: Fix me with the new countdown api + # stop_countdown() + pass def get_root_rest_element(self): """ diff --git a/openslides/agenda/personal_info.py b/openslides/agenda/personal_info.py deleted file mode 100644 index b4dba94a7..000000000 --- a/openslides/agenda/personal_info.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.personal_info import PersonalInfo - -from .models import Item - - -class AgendaPersonalInfo(PersonalInfo): - """ - Class for user info block for the agenda app. - """ - headline = ugettext_lazy('I am on the list of speakers of the following items') - default_weight = 10 - - def get_queryset(self): - return Item.objects.filter( - speaker__user=self.request.user, - speaker__begin_time=None) diff --git a/openslides/agenda/search_indexes.py b/openslides/agenda/search_indexes.py index 48325529f..98319de4c 100644 --- a/openslides/agenda/search_indexes.py +++ b/openslides/agenda/search_indexes.py @@ -1,4 +1,5 @@ from haystack import indexes + from .models import Item diff --git a/openslides/agenda/serializers.py b/openslides/agenda/serializers.py index c00443b8a..c8c423d4e 100644 --- a/openslides/agenda/serializers.py +++ b/openslides/agenda/serializers.py @@ -1,6 +1,11 @@ from django.core.urlresolvers import reverse -from openslides.utils.rest_api import CharField, ModelSerializer, RelatedField, get_collection_and_id_from_url +from openslides.utils.rest_api import ( + CharField, + ModelSerializer, + RelatedField, + get_collection_and_id_from_url, +) from .models import Item, Speaker diff --git a/openslides/agenda/signals.py b/openslides/agenda/signals.py index f87fae70e..f48a914bb 100644 --- a/openslides/agenda/signals.py +++ b/openslides/agenda/signals.py @@ -3,13 +3,10 @@ from datetime import datetime from django import forms from django.contrib.contenttypes.models import ContentType from django.core.exceptions import ValidationError -from django.template.loader import render_to_string from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop -from openslides.config.api import config, ConfigCollection, ConfigVariable -from openslides.projector.api import get_active_slide, get_active_object -from openslides.projector.projector import Overlay +from openslides.config.api import ConfigCollection, ConfigVariable from .models import Item @@ -90,54 +87,6 @@ def setup_agenda_config(sender, **kwargs): 'extra_javascript': extra_javascript}) -def agenda_list_of_speakers(sender, **kwargs): - """ - Receiver function to setup the list of speaker overlay. It is connected - to the signal openslides.projector.signals.projector_overlays during - app loading. - """ - name = 'agenda_speaker' - - def get_widget_html(): - """ - Returns the the html-code to show in the overly-widget. - """ - return render_to_string('agenda/overlay_speaker_widget.html') - - def get_projector_html(): - """ - Returns an html-code to show on the projector. - - The overlay is only shown on agenda-items and not on the - list-of-speakers slide. - """ - slide = get_active_object() - if slide is None or isinstance(slide, Item): - item = slide - else: - # TODO: If there is more than one item, use the first one in the - # mptt tree that is not closed. - try: - item = Item.objects.filter( - content_type=ContentType.objects.get_for_model(slide), - object_id=slide.pk)[0] - except IndexError: - item = None - if item and get_active_slide().get('type', None) != 'list_of_speakers': - list_of_speakers = item.get_list_of_speakers( - old_speakers_count=config['agenda_show_last_speakers'], - coming_speakers_count=5) - - value = render_to_string('agenda/overlay_speaker_projector.html', { - 'list_of_speakers': list_of_speakers, - 'closed': item.speaker_list_closed}) - else: - value = None - return value - - return Overlay(name, get_widget_html, get_projector_html) - - def listen_to_related_object_delete_signal(sender, instance, **kwargs): """ Receiver function to changed agenda items of a related items that is to diff --git a/openslides/agenda/slides.py b/openslides/agenda/slides.py deleted file mode 100644 index 171a84086..000000000 --- a/openslides/agenda/slides.py +++ /dev/null @@ -1,60 +0,0 @@ -from django.template.loader import render_to_string - -from openslides.config.api import config -from openslides.projector.api import get_projector_content - -from .models import Item - - -def agenda_slide(**kwargs): - """ - Return the html code for all slides of the agenda app. - - If no id is given, show a summary of all parent items. - - If an id is given, show the item depending of the argument 'type'. - - If 'type' is not set, show only the item. - - If 'type' is 'summary', show a summary of all children of the item. - - If 'type' is 'list_of_speakers', show the list of speakers for the item. - - The function is registered during app loading. - """ - item_pk = kwargs.get('pk', None) - slide_type = kwargs.get('type', None) - - try: - item = Item.objects.get(pk=item_pk) - except Item.DoesNotExist: - item = None - - if slide_type == 'summary' or item is None: - context = {} - if item is None: - items = Item.objects.filter(parent=None, type__exact=Item.AGENDA_ITEM) - else: - items = item.get_children().filter(type__exact=Item.AGENDA_ITEM) - context['title'] = item.get_title() - context['items'] = items - slide = render_to_string('agenda/item_slide_summary.html', context) - - elif slide_type == 'list_of_speakers': - list_of_speakers = item.get_list_of_speakers( - old_speakers_count=config['agenda_show_last_speakers']) - context = {'title': item.get_title(), - 'item': item, - 'list_of_speakers': list_of_speakers} - slide = render_to_string('agenda/item_slide_list_of_speaker.html', context) - - elif item.content_object: - slide_dict = { - 'callback': item.content_object.slide_callback_name, - 'pk': item.content_object.pk} - slide = get_projector_content(slide_dict) - - else: - context = {'item': item} - slide = render_to_string('agenda/item_slide.html', context) - return slide diff --git a/openslides/agenda/templates/agenda/current_list_of_speakers_projector.html b/openslides/agenda/templates/agenda/current_list_of_speakers_projector.html deleted file mode 100644 index 10dd26791..000000000 --- a/openslides/agenda/templates/agenda/current_list_of_speakers_projector.html +++ /dev/null @@ -1,4 +0,0 @@ -{% extends 'projector.html' %} -{% load i18n %} - -{% block title %}{% trans 'List of speakers' %} – {{ block.super }}{% endblock %} diff --git a/openslides/agenda/templates/agenda/item_slide.html b/openslides/agenda/templates/agenda/item_slide.html deleted file mode 100644 index 4d0022053..000000000 --- a/openslides/agenda/templates/agenda/item_slide.html +++ /dev/null @@ -1,12 +0,0 @@ -{% load i18n %} -{% load staticfiles %} - - - -

- {{ item }} -

- -{% if item.text %} - {{ item.text|safe }} -{% endif %} diff --git a/openslides/agenda/templates/agenda/item_slide_list_of_speaker.html b/openslides/agenda/templates/agenda/item_slide_list_of_speaker.html deleted file mode 100644 index 201912753..000000000 --- a/openslides/agenda/templates/agenda/item_slide_list_of_speaker.html +++ /dev/null @@ -1,27 +0,0 @@ -{% load i18n %} -{% load staticfiles %} - - - -

- {{ title }} - - {% trans 'List of speakers' %} - {% if item.speaker_list_closed %}({% trans 'closed' %}){% endif %} - -

- -{% if list_of_speakers %} - -{% else %} - {% trans 'The list of speakers is empty.' %} -{% endif %} diff --git a/openslides/agenda/templates/agenda/item_slide_summary.html b/openslides/agenda/templates/agenda/item_slide_summary.html deleted file mode 100644 index 5f29881fb..000000000 --- a/openslides/agenda/templates/agenda/item_slide_summary.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load i18n %} -{% load staticfiles %} - - - -

- {% if title %}{{ title }}{% else %}{% trans "Agenda" %}{% endif %} -

- - diff --git a/openslides/agenda/templates/agenda/overlay_speaker_projector.html b/openslides/agenda/templates/agenda/overlay_speaker_projector.html deleted file mode 100644 index 209969bd6..000000000 --- a/openslides/agenda/templates/agenda/overlay_speaker_projector.html +++ /dev/null @@ -1,22 +0,0 @@ -{% load i18n %} -{% load staticfiles %} - - - -
-

{% trans "List of speakers" %} {% if closed %}({% trans 'closed' %}){% endif %}

- {% if list_of_speakers %} - - {% else %} - {% trans 'The list of speakers is empty.' %} - {% endif %} -
diff --git a/openslides/agenda/templates/agenda/overlay_speaker_widget.html b/openslides/agenda/templates/agenda/overlay_speaker_widget.html deleted file mode 100644 index e04535cad..000000000 --- a/openslides/agenda/templates/agenda/overlay_speaker_widget.html +++ /dev/null @@ -1,6 +0,0 @@ -{% load i18n %} - - - {% trans 'List of speakers' %} - ({% trans 'This overlay only appears on agenda slides if it is activated.' %}) - diff --git a/openslides/agenda/templates/agenda/widget_item.html b/openslides/agenda/templates/agenda/widget_item.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/openslides/agenda/templates/agenda/widget_list_of_speakers.html b/openslides/agenda/templates/agenda/widget_list_of_speakers.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/openslides/agenda/urls.py b/openslides/agenda/urls.py index 922cdef70..0167c21a9 100644 --- a/openslides/agenda/urls.py +++ b/openslides/agenda/urls.py @@ -4,14 +4,7 @@ from . import views urlpatterns = patterns( '', - - # PDF url(r'^print/$', views.AgendaPDF.as_view(), name='agenda_pdf'), - - # TODO: remove it after implement projector rest api - url(r'^list_of_speakers/projector/$', - views.CurrentListOfSpeakersProjectorView.as_view(), - name='agenda_current_list_of_speakers_projector'), ) diff --git a/openslides/agenda/views.py b/openslides/agenda/views.py index dc3f34935..0cdf00d40 100644 --- a/openslides/agenda/views.py +++ b/openslides/agenda/views.py @@ -1,24 +1,11 @@ -# TODO: Rename all views and template names - from cgi import escape from collections import defaultdict -from json import dumps from django.contrib.auth import get_user_model -from django.contrib.contenttypes.models import ContentType -from django.contrib.staticfiles.templatetags.staticfiles import static -from django.template.loader import render_to_string -from django.utils.datastructures import SortedDict -from django.utils.safestring import mark_safe from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from reportlab.platypus import Paragraph -from openslides.config.api import config -from openslides.projector.api import ( - get_active_object, - get_projector_overlays_js, - get_overlays) from openslides.utils.exceptions import OpenSlidesError from openslides.utils.pdf import stylesheet from openslides.utils.rest_api import ( @@ -28,35 +15,12 @@ from openslides.utils.rest_api import ( detail_route, list_route, ) -from openslides.utils.views import ( - AjaxMixin, - PDFView, - RedirectView, - SingleObjectMixin, - TemplateView) +from openslides.utils.views import PDFView from .models import Item, Speaker from .serializers import ItemSerializer -class CreateRelatedAgendaItemView(SingleObjectMixin, RedirectView): - """ - View to create and agenda item for a related object. - - This view is only for subclassing in views of related apps. You - have to define 'model = ....' - """ - required_permission = 'agenda.can_manage' - url_name = 'item_overview' - url_name_args = [] - - def pre_redirect(self, request, *args, **kwargs): - """ - Create the agenda item. - """ - self.item = Item.objects.create(content_object=self.get_object()) - - class AgendaPDF(PDFView): """ Create a full agenda-PDF. @@ -77,108 +41,6 @@ class AgendaPDF(PDFView): story.append(Paragraph(escape(item.get_title()), stylesheet['Item'])) -class CurrentListOfSpeakersProjectorView(AjaxMixin, TemplateView): - """ - View with the current list of speakers depending on the active slide. - Usefule for the projector. - """ - template_name = 'agenda/current_list_of_speakers_projector.html' - - def get(self, request, *args, **kwargs): - """ - Returns response object depending on request type (ajax or normal). - """ - if request.is_ajax(): - value = self.ajax_get(request, *args, **kwargs) - else: - value = super(CurrentListOfSpeakersProjectorView, self).get(request, *args, **kwargs) - return value - - def get_item(self): - """ - Returns the item of the current slide is an agenda item slide or a - slide of a related model else returns None. - """ - slide_object = get_active_object() - if slide_object is None or isinstance(slide_object, Item): - item = slide_object - else: - # TODO: If there is more than one item, use the first one in the - # mptt tree that is not closed. - try: - item = Item.objects.filter( - content_type=ContentType.objects.get_for_model(slide_object), - object_id=slide_object.pk)[0] - except IndexError: - item = None - return item - - def get_content(self): - """ - Returns the content of this slide. - """ - item = self.get_item() - if item is None: - content = mark_safe('

%s

%s\n' % (_('List of speakers'), _('Not available.'))) - else: - content_dict = { - 'title': item.get_title(), - 'item': item, - 'list_of_speakers': item.get_list_of_speakers( - old_speakers_count=config['agenda_show_last_speakers'])} - content = render_to_string('agenda/item_slide_list_of_speaker.html', content_dict) - return content - - def get_overlays_and_overlay_js(self): - """ - Returns the overlays and their JavaScript for this slide as a - two-tuple. The overlay 'agenda_speaker' is always excluded. - - The required JavaScript fot this view is inserted. - """ - overlays = get_overlays(only_active=True) - overlays.pop('agenda_speaker', None) - overlay_js = get_projector_overlays_js(as_json=True) - # Note: The JavaScript content of overlay 'agenda_speaker' is not - # excluded because this overlay has no such content at the moment. - extra_js = SortedDict() - extra_js['load_file'] = static('js/agenda_current_list_of_speakers_projector.js') - extra_js['call'] = 'reloadListOfSpeakers();' - extra_js = dumps(extra_js) - overlay_js.append(extra_js) - return overlays, overlay_js - - def get_context_data(self, **context): - """ - Returns the context for the projector template. Contains the content - of this slide. - """ - overlays, overlay_js = self.get_overlays_and_overlay_js() - return super(CurrentListOfSpeakersProjectorView, self).get_context_data( - content=self.get_content(), - overlays=overlays, - overlay_js=overlay_js, - **context) - - def get_ajax_context(self, **context): - """ - Returns the context including the slide content for ajax response. The - overlay 'agenda_speaker' is always excluded. - """ - overlay_dict = {} - for overlay in get_overlays().values(): - if overlay.is_active() and overlay.name != 'agenda_speaker': - overlay_dict[overlay.name] = { - 'html': overlay.get_projector_html(), - 'javascript': overlay.get_javascript()} - else: - overlay_dict[overlay.name] = None - return super(CurrentListOfSpeakersProjectorView, self).get_ajax_context( - content=self.get_content(), - overlays=overlay_dict, - **context) - - class ItemViewSet(ModelViewSet): """ API endpoint to list, retrieve, create, update and destroy agenda items. diff --git a/openslides/agenda/widgets.py b/openslides/agenda/widgets.py deleted file mode 100644 index 32290a2ca..000000000 --- a/openslides/agenda/widgets.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget -from openslides.projector.api import get_active_slide - -from .models import Item - - -class AgendaWidget(Widget): - """ - Agenda widget. - """ - name = 'agenda' - verbose_name = ugettext_lazy('Agenda') - required_permission = 'core.can_manage_projector' - default_column = 1 - default_weight = 20 - template_name = 'agenda/widget_item.html' - icon_css_class = 'icon-calendar' - more_link_pattern_name = 'item_overview' - - def get_context_data(self, **context): - active_slide = get_active_slide() - if active_slide['callback'] == 'agenda': - agenda_is_active = active_slide.get('pk', 'agenda') == 'agenda' - active_type = active_slide.get('type', 'text') - else: - agenda_is_active = None - active_type = None - context.update({ - 'agenda_is_active': agenda_is_active, - 'items': Item.objects.all(), - 'active_type': active_type}) - return super(AgendaWidget, self).get_context_data(**context) - - -class ListOfSpeakersWidget(Widget): - """ - Widget to control the list of speakers. - """ - name = 'append_to_list_of_speakers' - verbose_name = ugettext_lazy('List of speakers') - default_column = 1 - default_weight = 30 - template_name = 'agenda/widget_list_of_speakers.html' - icon_css_class = 'icon-bell' - - def check_permission(self): - return (self.request.user.has_perm('agenda.can_manage') or - self.request.user.has_perm('agenda.can_be_speaker')) diff --git a/openslides/assignments/apps.py b/openslides/assignments/apps.py index d144d057f..0aa89656c 100644 --- a/openslides/assignments/apps.py +++ b/openslides/assignments/apps.py @@ -6,31 +6,15 @@ class AssignmentAppConfig(AppConfig): verbose_name = 'OpenSlides Assignments' def ready(self): - # Load main menu entry, personal info and widgets. - # Do this by just importing all from these files. - from . import main_menu, personal_info, widgets # noqa - # Import all required stuff. from openslides.config.signals import config_signal - from openslides.projector.api import register_slide_model from openslides.utils.rest_api import router - from openslides.utils.signals import template_manipulation from .signals import setup_assignment_config - from .template import add_assignment_stylesheets from .views import AssignmentViewSet, AssignmentPollViewSet # Connect signals. config_signal.connect(setup_assignment_config, dispatch_uid='setup_assignment_config') - # Connect template signal. - template_manipulation.connect(add_assignment_stylesheets, dispatch_uid='add_assignment_stylesheets') - - # Register slides. - Assignment = self.get_model('Assignment') - AssignmentPoll = self.get_model('AssignmentPoll') - register_slide_model(Assignment, 'assignments/slide.html') - register_slide_model(AssignmentPoll, 'assignments/assignmentpoll_slide.html') - # Register viewsets. router.register('assignments/assignment', AssignmentViewSet) router.register('assignments/assignmentpoll', AssignmentPollViewSet) diff --git a/openslides/assignments/forms.py b/openslides/assignments/forms.py deleted file mode 100644 index 97ffcd268..000000000 --- a/openslides/assignments/forms.py +++ /dev/null @@ -1,23 +0,0 @@ -from django import forms -from django.utils.translation import ugettext_lazy - -from openslides.users.models import User -from openslides.utils.forms import CssClassMixin - -from .models import Assignment - - -class AssignmentForm(CssClassMixin, forms.ModelForm): - open_posts = forms.IntegerField( - min_value=1, initial=1, label=ugettext_lazy("Number of available posts")) - - class Meta: - model = Assignment - fields = ('title', 'description', 'open_posts', 'poll_description_default') - - -class AssignmentRunForm(CssClassMixin, forms.Form): - candidate = forms.ModelChoiceField( - queryset=User.objects.all(), - widget=forms.Select(attrs={'class': 'medium-input'}), - label=ugettext_lazy("Nominate a participant")) diff --git a/openslides/assignments/main_menu.py b/openslides/assignments/main_menu.py deleted file mode 100644 index 6e7eeb517..000000000 --- a/openslides/assignments/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class AssignmentMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the assignment app. - """ - verbose_name = ugettext_lazy('Elections') - required_permission = 'assignments.can_see' - default_weight = 40 - pattern_name = '/assignments' # TODO: use generic solution, see issue #1469 - icon_css_class = 'icon-assignment' diff --git a/openslides/assignments/models.py b/openslides/assignments/models.py index b26f8e420..75d29ecb6 100644 --- a/openslides/assignments/models.py +++ b/openslides/assignments/models.py @@ -1,21 +1,23 @@ from django.contrib.contenttypes.fields import GenericRelation -from django.core.urlresolvers import reverse from django.db import models from django.utils.datastructures import SortedDict from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop from openslides.agenda.models import Item, Speaker -from openslides.core.models import Tag from openslides.config.api import config -from openslides.poll.models import (BaseOption, BasePoll, BaseVote, - CollectDefaultVotesMixin, - PublishPollMixin) +from openslides.core.models import Tag +from openslides.poll.models import ( + BaseOption, + BasePoll, + BaseVote, + CollectDefaultVotesMixin, + PublishPollMixin, +) from openslides.projector.models import SlideMixin -from openslides.utils.exceptions import OpenSlidesError -from openslides.utils.models import AbsoluteUrlMixin -from openslides.utils.rest_api import RESTModelMixin from openslides.users.models import User +from openslides.utils.exceptions import OpenSlidesError +from openslides.utils.rest_api import RESTModelMixin class AssignmentRelatedUser(RESTModelMixin, models.Model): @@ -53,7 +55,7 @@ class AssignmentRelatedUser(RESTModelMixin, models.Model): return self.assignment -class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): +class Assignment(RESTModelMixin, SlideMixin, models.Model): slide_callback_name = 'assignment' PHASE_SEARCH = 0 @@ -133,20 +135,6 @@ class Assignment(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): def __str__(self): return self.title - def get_absolute_url(self, link='detail'): - """ - Returns absolute url to the assignment instance. - """ - if link == 'detail': - url = reverse('assignment_detail', args=[str(self.pk)]) - elif link == 'update': - url = reverse('assignment_update', args=[str(self.pk)]) - elif link == 'delete': - url = reverse('assignment_delete', args=[str(self.pk)]) - else: - url = super().get_absolute_url(link) - return url - def get_slide_context(self, **context): """ Retuns the context to generate the assignment slide. @@ -351,7 +339,7 @@ class AssignmentOption(RESTModelMixin, BaseOption): class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin, - PublishPollMixin, AbsoluteUrlMixin, BasePoll): + PublishPollMixin, BasePoll): slide_callback_name = 'assignmentpoll' option_class = AssignmentOption @@ -365,20 +353,6 @@ class AssignmentPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin, def __str__(self): return _("Ballot %d") % self.get_ballot() - def get_absolute_url(self, link='update'): - """ - Return an URL for the poll. - - The keyargument 'link' can be 'update' or 'delete'. - """ - if link == 'update': - url = reverse('assignmentpoll_update', args=[str(self.pk)]) - elif link == 'delete': - url = reverse('assignmentpoll_delete', args=[str(self.pk)]) - else: - url = super().get_absolute_url(link) - return url - def get_assignment(self): return self.assignment diff --git a/openslides/assignments/personal_info.py b/openslides/assignments/personal_info.py deleted file mode 100644 index c9630466a..000000000 --- a/openslides/assignments/personal_info.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.personal_info import PersonalInfo - -from .models import Assignment, AssignmentRelatedUser - - -class AssignmentPersonalInfo(PersonalInfo): - """ - Class for personal info block for the assignment app. - """ - headline = ugettext_lazy('I am candidate for the following elections') - default_weight = 40 - - def get_queryset(self): - return (Assignment.objects.filter(assignment_related_users__user=self.request.user) - .exclude(assignment_related_users__status=AssignmentRelatedUser.STATUS_BLOCKED)) diff --git a/openslides/assignments/search_indexes.py b/openslides/assignments/search_indexes.py index 8a9dd4bc8..6bb39dfcc 100644 --- a/openslides/assignments/search_indexes.py +++ b/openslides/assignments/search_indexes.py @@ -1,4 +1,5 @@ from haystack import indexes + from .models import Assignment diff --git a/openslides/assignments/serializers.py b/openslides/assignments/serializers.py index 2ce6204e4..3effc730b 100644 --- a/openslides/assignments/serializers.py +++ b/openslides/assignments/serializers.py @@ -7,15 +7,17 @@ from openslides.utils.rest_api import ( ListField, ListSerializer, ModelSerializer, - ValidationError) + ValidationError, +) from .models import ( - models, Assignment, - AssignmentRelatedUser, AssignmentOption, AssignmentPoll, - AssignmentVote) + AssignmentRelatedUser, + AssignmentVote, + models, +) class AssignmentRelatedUserSerializer(ModelSerializer): diff --git a/openslides/assignments/signals.py b/openslides/assignments/signals.py index 56774c30e..7fbbb0b9a 100644 --- a/openslides/assignments/signals.py +++ b/openslides/assignments/signals.py @@ -2,7 +2,11 @@ from django import forms from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop -from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable +from openslides.config.api import ( + ConfigGroup, + ConfigGroupedCollection, + ConfigVariable, +) from openslides.poll.models import PERCENT_BASE_CHOICES diff --git a/openslides/assignments/template.py b/openslides/assignments/template.py deleted file mode 100644 index 5cb21bb03..000000000 --- a/openslides/assignments/template.py +++ /dev/null @@ -1,7 +0,0 @@ -def add_assignment_stylesheets(sender, request, context, **kwargs): - """ - Receiver function to add the assignment.css to the context. It is - connected to the signal openslides.utils.signals.template_manipulation - during app loading. - """ - context['extra_stylefiles'].append('css/assignment.css') diff --git a/openslides/assignments/templates/assignments/assignment_detail.html b/openslides/assignments/templates/assignments/assignment_detail.html deleted file mode 100644 index 2601333c3..000000000 --- a/openslides/assignments/templates/assignments/assignment_detail.html +++ /dev/null @@ -1,356 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load staticfiles %} -{% load tags %} -{% load humanize %} - -{% block title %}{% trans "Election" %} "{{ assignment }}" – {{ block.super }}{% endblock %} - - -{% block javascript %} - -{% endblock %} - -{% block content %} -

- {{ assignment }} -
- {% trans "Election" %} - - - - {% trans "Back to overview" %} - - - - PDF - - - {% if perms.core.can_manage_projector %} - - - - {% endif %} - {% if perms.assignments.can_manage or perms.agenda.can_manage %} -
- - {% trans 'More actions' %} - - -
- {% endif %} -
-

- -
-
- - {% for tag in assignment.tags.all %} - {{ tag }} - {% endfor %} - - -

{% trans "Description" %}

- {% if assignment.description %} - {{ assignment.description|linebreaks }} - {% else %} - – - {% endif %} -
- -{% if assignment.phase != assignment.PHASE_FINISHED %} -

{% trans "Candidates" %}

-
    - {% for person in assignment.candidates %} -
  1. - {{ person }} - {% if perms.assignments.can_manage %} - - - - {% endif %} - {% if person in assignment.elected %} - | {% trans "elected" %} - {% if perms.assignments.can_manage %} - - - - {% endif %} - {% endif %} -
  2. - {% empty %} -
  3. - {% trans "No candidates available." %} -
  4. - {% endfor %} -
- {% if assignment.phase == assignment.PHASE_SEARCH or perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - {% if perms.assignments.can_nominate_self or perms.assignments.can_nominate_other %} -
{% csrf_token %} - {% if perms.assignments.can_nominate_self %} -

- {% if user_is_candidate %} - - - {% trans 'Withdraw self candidature' %} - - {% else %} - - - {% trans 'Self candidature' %} - - {% endif %} -

- {% endif %} - {% if perms.assignments.can_nominate_other %} - {% for field in form %} - - {{ field }} - - {% if perms.users.can_see and perms.users.can_manage %} - - - - {% endif %} - {% endfor %} - {% endif %} -
- {% endif %} - {% endif %} -{% endif %} - -{% if perms.assignments.can_manage and blocked_candidates and assignment.phase != assignment.PHASE_FINISHED %} -

{% trans "Blocked Candidates" %}

- -{% endif %} - - - -{% if assignment.phase != assignment.PHASE_SEARCH or polls.exists %} -

{% trans "Election result" %}

- {% if polls.exists %} - - - - {% for poll in polls %} - - {% endfor %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - - {% endif %} - - {% for candidate, poll_list in vote_results.items %} - - - {% for vote in poll_list %} - - {% endfor %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - - {% endif %} - - {% endfor %} - - - {% for poll in polls %} - {% if poll.published or perms.assignments.can_manage %} - - {% endif %} - {% endfor %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - - {% endif %} - - - - {% for poll in polls %} - {% if poll.published or perms.assignments.can_manage %} - - {% endif %} - {% endfor %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - - {% endif %} - - - - {% for poll in polls %} - {% if poll.published or perms.assignments.can_manage %} - - {% endif %} - {% endfor %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} - - {% endif %} - -
{% trans "Candidates" %} - {% if perms.assignments.can_manage %}

{% endif %} - {{ poll.get_ballot|ordinal|safe }} {% trans 'ballot' %} - {% if perms.assignments.can_manage %} - - {% if poll.published %} - - {% else %} - - {% endif %} - -

-

- - - - - - -

- {% endif %} -
- - - {% trans 'New ballot' %} - -
- {% if candidate in assignment.elected %} - {% if perms.assignments.can_manage %} - - {% else %} - - - - {% endif %} - {% else %} - {% if perms.assignments.can_manage %} - - {% endif %} - {% endif %} - {{ candidate }} - - {% if 'Yes' in vote and 'No' in vote and 'Abstain' in vote %} - {{ vote.Yes }}
- {{ vote.No }}
- {{ vote.Abstain }}
- {% elif 'Votes' in vote %} - {{ vote.Votes }} - {% elif vote == None %} - {% trans 'was not a
candidate'%} - {% else %} -   - {% endif %} -
{% trans 'Valid votes' %} - {% if poll.has_votes %} - - {{ poll.print_votesvalid }} - {% endif %} -
{% trans 'Invalid votes' %} - {% if poll.has_votes %} - - {{ poll.print_votesinvalid }} - {% endif %} -
{% trans 'Votes cast' %} - {% if poll.has_votes %} - - {{ poll.print_votescast }} - {% endif %} -
- {% else %} - {% trans "No ballots available." %} - {% if assignment.candidates and perms.assignments.can_manage and assignment.phase == assignment.PHASE_VOTING %} -

- - - {% trans 'New ballot' %} - -

- {% endif %} - {% endif %} -{% endif %} -
- -
-
- -
{% trans "Phases" %}:
- {% trans assignment.get_phase_display %} - -
{% trans "Number of available posts" %}:
- {{ assignment.posts }} -
- - {% if perms.assignments.can_manage %} - - {% endif %} -
-
-{% endblock %} diff --git a/openslides/assignments/templates/assignments/assignmentpoll_form.html b/openslides/assignments/templates/assignments/assignmentpoll_form.html deleted file mode 100644 index 45f5fcb3c..000000000 --- a/openslides/assignments/templates/assignments/assignmentpoll_form.html +++ /dev/null @@ -1,112 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} -{% load humanize %} -{% load tags %} - -{% block title %}{% trans "Election" %} "{{ assignment }}", {{ ballotnumber }}. {% trans "ballot" %} – {{ block.super }}{% endblock %} - -{% block content %} -

- {{ assignment }} - - {{ ballotnumber|ordinal|safe }} {% trans "ballot" %} - - - {% trans "Back to election" %} - - {% if perms.core.can_manage_projector %} - - - - - {% trans "Election result" %} - {% endif %} - {% if perms.motions.can_manage %} - - {% endif %} - -

- -
{% csrf_token %} -

- {% trans "Special values" %}: - -1 = {% trans 'majority' %}| - -2 = {% trans 'undocumented' %} -

- - - - - {% for value in poll.get_vote_values %} - - {% endfor %} - - {% for form in forms %} - - - {% for value in form %} - - {% endfor %} - - {% endfor %} - - - {% for value in poll.get_vote_values %} - {% if forloop.first %} - - {% else %} - - {% endif %} - {% endfor %} - - - - {% for value in poll.get_vote_values %} - {% if forloop.first %} - - {% else %} - - {% endif %} - {% endfor %} - - - - {% for value in poll.get_vote_values %} - {% if forloop.first %} - - {% else %} - - {% endif %} - {% endfor %} - -
{% trans "Candidates" %}{% trans value %}
{{ form.option }} - {{ value.errors }} - {{ value }} -
{% trans "Valid votes" %}{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}
{% trans "Invalid votes" %}{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}
{% trans "Votes cast" %}{{ pollform.votescast.errors }}{{ pollform.votescast }}
- -

{% trans "Short description (for ballot paper)" %}:

-

{{ pollform.description }}

-

- - {% trans 'Ballot paper as PDF' %} - -

- -
- - - - {% trans 'Cancel' %} - -
-
-{% endblock %} diff --git a/openslides/assignments/templates/assignments/assignmentpoll_slide.html b/openslides/assignments/templates/assignments/assignmentpoll_slide.html deleted file mode 100644 index ac702cdd9..000000000 --- a/openslides/assignments/templates/assignments/assignmentpoll_slide.html +++ /dev/null @@ -1,59 +0,0 @@ -{% load i18n %} -{% load humanize %} -{% load staticfiles %} -{% load tags %} - -

- {{ poll.assignment }} -
- - {% trans "Election" %} - {% if poll.get_ballot > 1 %}| {{ poll.get_ballot|ordinal|safe }} {% trans "ballot" %}{% endif %} - -

- -

-{% if poll.has_votes and poll.published %} - - {% for option in poll.get_options %} - - - - - {% endfor %} - {% if poll.votesvalid != None %} - - - - - {% endif %} - {% if poll.votesinvalid != None %} - - - - - {% endif %} - {% if poll.votescast != None %} - - - - - {% endif %} -
{{ option }} - {% if not "assignment_publish_winner_results_only"|get_config or option.candidate in poll.assignment.elected %} - {% if poll.yesnoabstain %} - {% trans 'Yes' %}: - {{ option.Yes }}
- {% trans 'No' %}: - {{ option.No }}
- {% trans 'Abstention' %}: - {{ option.Abstain }} - {% else %} - {{ option.Votes }} - {% endif %} - {% endif %} -
{% trans 'Valid votes' %}:{{ poll.print_votesvalid }}
{% trans 'Invalid votes' %}:{{ poll.print_votesinvalid }}
{% trans 'Votes cast' %}:{{ poll.print_votescast }}
-{% else %} - {% trans "No result available." %} -{% endif %} -

diff --git a/openslides/assignments/templates/assignments/slide.html b/openslides/assignments/templates/assignments/slide.html deleted file mode 100644 index 96c8dca1b..000000000 --- a/openslides/assignments/templates/assignments/slide.html +++ /dev/null @@ -1,53 +0,0 @@ -{% load i18n %} -{% load staticfiles %} -{% load tags %} -{% load humanize %} - - - -

{{ assignment }} - - {% trans "Election" %} - -

- -{% if not assignment.candidates %} -

-

{{ assignment.description|linebreaks }}
-

-{% endif %} - -{% if assignment.candidates and assignment.status != "fin" %} -

{% trans "Candidates" %}

-
    - {% for candidate in assignment.candidates %} -
  1. {{ candidate }}
  2. - {% empty %} -
  3. - {% trans "No candidates available." %} -
  4. - {% endfor %} -
-


-{% endif %} - -{% if assignment.status == "fin" %} -

{% trans "Elected candidates" %}

-
    - {% for person in assignment.elected %} -
  1. {{ person }}
  2. - {% empty %} -
  3. - {% trans "No candidates elected." %} -
  4. - {% endfor %} -
-{% endif %} diff --git a/openslides/assignments/templates/assignments/widget_assignment.html b/openslides/assignments/templates/assignments/widget_assignment.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/openslides/assignments/urls.py b/openslides/assignments/urls.py index cad5efc48..ff5f8d511 100644 --- a/openslides/assignments/urls.py +++ b/openslides/assignments/urls.py @@ -4,8 +4,6 @@ from . import views urlpatterns = patterns( '', - - # PDF url(r'^print/$', views.AssignmentPDF.as_view(), name='assignments_pdf'), diff --git a/openslides/assignments/views.py b/openslides/assignments/views.py index a430a9e2f..433679c7b 100644 --- a/openslides/assignments/views.py +++ b/openslides/assignments/views.py @@ -5,8 +5,15 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ungettext from reportlab.lib import colors from reportlab.lib.units import cm -from reportlab.platypus import (PageBreak, Paragraph, SimpleDocTemplate, Spacer, - LongTable, Table, TableStyle) +from reportlab.platypus import ( + LongTable, + PageBreak, + Paragraph, + SimpleDocTemplate, + Spacer, + Table, + TableStyle, +) from openslides.config.api import config from openslides.users.models import Group, User # TODO: remove this @@ -18,14 +25,15 @@ from openslides.utils.rest_api import ( Response, UpdateModelMixin, ValidationError, - detail_route) + detail_route, +) from openslides.utils.views import PDFView from .models import Assignment, AssignmentPoll from .serializers import ( AssignmentAllPollSerializer, AssignmentFullSerializer, - AssignmentShortSerializer + AssignmentShortSerializer, ) diff --git a/openslides/assignments/widgets.py b/openslides/assignments/widgets.py deleted file mode 100644 index f227353b8..000000000 --- a/openslides/assignments/widgets.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget - -from .models import Assignment - - -class AssignmentWidget(Widget): - """ - Assignment widget. - """ - name = 'assignment' - verbose_name = ugettext_lazy('Elections') - required_permission = 'core.can_manage_projector' - default_column = 1 - default_weight = 50 - template_name = 'assignments/widget_assignment.html' - more_link_pattern_name = 'assignment_list' - - def get_context_data(self, **context): - return super(AssignmentWidget, self).get_context_data( - assignments=Assignment.objects.all(), - **context) diff --git a/openslides/config/apps.py b/openslides/config/apps.py index d1047212c..5629cf41f 100644 --- a/openslides/config/apps.py +++ b/openslides/config/apps.py @@ -6,10 +6,6 @@ class ConfigAppConfig(AppConfig): verbose_name = 'OpenSlides Config' def ready(self): - # Load main menu entry. - # Do this by just importing all from this file. - from . import main_menu # noqa - # Import all required stuff. from openslides.utils.rest_api import router from .views import ConfigViewSet diff --git a/openslides/config/main_menu.py b/openslides/config/main_menu.py deleted file mode 100644 index 86a6b943e..000000000 --- a/openslides/config/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class ConfigMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the config app. - """ - verbose_name = ugettext_lazy('Configuration') - required_permission = 'config.can_manage' - default_weight = 70 - pattern_name = 'config_first_config_collection_view' - icon_css_class = 'glyphicon-cog' diff --git a/openslides/config/models.py b/openslides/config/models.py index c5dfd841e..6b739811d 100644 --- a/openslides/config/models.py +++ b/openslides/config/models.py @@ -1,6 +1,5 @@ from django.db import models from django.utils.translation import ugettext_noop - from jsonfield import JSONField diff --git a/openslides/config/templates/config/config_form.html b/openslides/config/templates/config/config_form.html deleted file mode 100644 index 868936614..000000000 --- a/openslides/config/templates/config/config_form.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %}{% trans "Configuration" %} – {{ block.super }}{% endblock %} - -{% block content %} -

- {% trans 'Configuration' %} - {% trans active_config_collection_view.title %} - -
-
- {% for config_collection_dict in config_collection_list %} - - {% trans config_collection_dict.config_collection.title %} - - {% endfor %} -
-
-
-

-
{% csrf_token %} - {% for group in groups %} -
- {{ group.title }} - {% for field in form %} - {% if field.name in group.get_field_names %} -
- - {{ field }} - {% if field.errors %} - {{ field.errors }} - {% endif %} - {% if field.help_text %} - {{ field.help_text }} - {% endif %} -
- {% endif %} - {% endfor %} -
- {% empty %} - {% include 'form.html' %} - {% endfor %} -

- {% include 'formbuttons_save.html' %} - {% trans 'Cancel' %} -

- * {% trans 'required' %} -
-{% endblock %} diff --git a/openslides/config/urls.py b/openslides/config/urls.py deleted file mode 100644 index f7b636911..000000000 --- a/openslides/config/urls.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.conf.urls import patterns, url - -from openslides.utils.views import RedirectView - -from .signals import config_signal -from .views import ConfigView - -urlpatterns = patterns( - '', - url(r'^$', - RedirectView.as_view(url_name='config_general'), - name='config_first_config_collection_view') -) - -for receiver, config_collection in config_signal.send(sender='config_urls'): - if config_collection.is_shown(): - urlpatterns += patterns('', url(r'^%s/$' % config_collection.url, - ConfigView.as_view(config_collection=config_collection), - name='config_%s' % config_collection.url)) diff --git a/openslides/config/views.py b/openslides/config/views.py index ba04465c7..7ebc7f5a0 100644 --- a/openslides/config/views.py +++ b/openslides/config/views.py @@ -1,109 +1,10 @@ -from django import forms -from django.contrib import messages -from django.core.urlresolvers import reverse from django.core.exceptions import ValidationError as DjangoValidationError from django.http import Http404 -from django.utils.translation import ugettext as _ from openslides.utils.rest_api import Response, ValidationError, ViewSet -from openslides.utils.views import FormView from .api import config from .exceptions import ConfigNotFound -from .signals import config_signal - - -class ConfigView(FormView): - """ - The view for a config collection. - """ - required_permission = 'config.can_manage' - template_name = 'config/config_form.html' - config_collection = None - form_class = forms.Form - - def get_form(self, *args): - """ - Gets the form for the view. Includes all form fields given by the - config collection. - """ - form = super(ConfigView, self).get_form(*args) - for name, field in self.generate_form_fields_from_config_collection(): - form.fields[name] = field - return form - - def generate_form_fields_from_config_collection(self): - """ - Generates the fields for the get_form function. - """ - for variable in self.config_collection.variables: - if variable.form_field is not None: - yield (variable.name, variable.form_field) - - def get_initial(self): - """ - Returns a dictonary with the actual values of the config variables - as intial value for the form. - """ - initial = super(ConfigView, self).get_initial() - for variable in self.config_collection.variables: - initial.update({variable.name: config[variable.name]}) - return initial - - def get_context_data(self, **kwargs): - """ - Adds to the context the active config view, a list of dictionaries - containing all config collections each with a flag which is true if its - view is the active one and adds a flag whether the config collection - has groups. Adds also extra_stylefiles and extra_javascript. - """ - context = super(ConfigView, self).get_context_data(**kwargs) - - context['active_config_collection_view'] = self.config_collection - - config_collection_list = [] - for receiver, config_collection in config_signal.send(sender=self): - if config_collection.is_shown(): - config_collection_list.append({ - 'config_collection': config_collection, - 'active': self.request.path == reverse('config_%s' % config_collection.url)}) - context['config_collection_list'] = sorted( - config_collection_list, key=lambda config_collection_dict: config_collection_dict['config_collection'].weight) - - if hasattr(self.config_collection, 'groups'): - context['groups'] = self.config_collection.groups - else: - context['groups'] = None - - if 'extra_stylefiles' in self.config_collection.extra_context: - if 'extra_stylefiles' in context: - context['extra_stylefiles'].extend(self.config_collection.extra_context['extra_stylefiles']) - else: - context['extra_stylefiles'] = self.config_collection.extra_context['extra_stylefiles'] - - if 'extra_javascript' in self.config_collection.extra_context: - if 'extra_javascript' in context: - context['extra_javascript'].extend(self.config_collection.extra_context['extra_javascript']) - else: - context['extra_javascript'] = self.config_collection.extra_context['extra_javascript'] - - return context - - def get_success_url(self): - """ - Returns the success url when changes are saved. Here it is the same - url as the main menu entry. - """ - return reverse('config_%s' % self.config_collection.url) - - def form_valid(self, form): - """ - Saves all data of a valid form. - """ - for key in form.cleaned_data: - config[key] = form.cleaned_data[key] - messages.success(self.request, _('%s settings successfully saved.') % _(self.config_collection.title)) - return super(ConfigView, self).form_valid(form) class ConfigViewSet(ViewSet): diff --git a/openslides/core/apps.py b/openslides/core/apps.py index ca50324a6..e46a65943 100644 --- a/openslides/core/apps.py +++ b/openslides/core/apps.py @@ -6,9 +6,9 @@ class CoreAppConfig(AppConfig): verbose_name = 'OpenSlides Core' def ready(self): - # Load main menu entry, projector elements and widgets. + # Load projector elements. # Do this by just importing all from these files. - from . import main_menu, projector, widgets # noqa + from . import projector # noqa # Import all required stuff. from django.db.models import signals diff --git a/openslides/core/chatbox.py b/openslides/core/chatbox.py index 2a9e89a5c..a84314e9c 100644 --- a/openslides/core/chatbox.py +++ b/openslides/core/chatbox.py @@ -3,7 +3,6 @@ from datetime import datetime from django.conf import settings from django.utils.html import urlize from django.utils.importlib import import_module - from sockjs.tornado import SockJSConnection diff --git a/openslides/core/forms.py b/openslides/core/forms.py deleted file mode 100644 index 208a9de4a..000000000 --- a/openslides/core/forms.py +++ /dev/null @@ -1,10 +0,0 @@ -from django import forms - -from openslides.utils.forms import CssClassMixin - - -class SelectWidgetsForm(CssClassMixin, forms.Form): - """ - Form to select the widgets. - """ - widget = forms.BooleanField(required=False) diff --git a/openslides/core/main_menu.py b/openslides/core/main_menu.py deleted file mode 100644 index 2d4e5850d..000000000 --- a/openslides/core/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class DashboardMainMenuEntry(MainMenuEntry): - """ - Main menu entry to the dashboard. - """ - verbose_name = ugettext_lazy('Dashboard') - required_permission = 'core.can_see_dashboard' - default_weight = 10 - icon_css_class = 'glyphicon-home' - pattern_name = '/' # TODO: use generic solution, see issue #1469 diff --git a/openslides/core/management/commands/backupdb.py b/openslides/core/management/commands/backupdb.py index 21755a8e8..a074965ec 100644 --- a/openslides/core/management/commands/backupdb.py +++ b/openslides/core/management/commands/backupdb.py @@ -1,7 +1,7 @@ -from optparse import make_option # TODO: Use argpase in Django 1.8 import shutil +from optparse import make_option # TODO: Use argpase in Django 1.8 -from django.core.management.base import NoArgsCommand, CommandError +from django.core.management.base import CommandError, NoArgsCommand from django.db import connection, transaction from openslides.utils.main import get_database_path_from_settings diff --git a/openslides/core/management/commands/runserver.py b/openslides/core/management/commands/runserver.py index f96cc9978..6760426e7 100644 --- a/openslides/core/management/commands/runserver.py +++ b/openslides/core/management/commands/runserver.py @@ -1,10 +1,10 @@ -from datetime import datetime -import sys -import socket import errno +import socket +import sys +from datetime import datetime -from django.core.management.commands.runserver import Command as _Command from django.core.exceptions import ImproperlyConfigured +from django.core.management.commands.runserver import Command as _Command from django.utils import translation from django.utils.encoding import force_text diff --git a/openslides/core/migrations/0001_initial.py b/openslides/core/migrations/0001_initial.py index 79267669e..ff25b5cae 100644 --- a/openslides/core/migrations/0001_initial.py +++ b/openslides/core/migrations/0001_initial.py @@ -40,7 +40,7 @@ class Migration(migrations.Migration): options={ 'ordering': ('weight', 'title'), }, - bases=(openslides.utils.rest_api.RESTModelMixin, openslides.utils.models.AbsoluteUrlMixin, models.Model), + bases=(openslides.utils.rest_api.RESTModelMixin, models.Model), ), migrations.CreateModel( name='Projector', @@ -67,7 +67,7 @@ class Migration(migrations.Migration): 'permissions': (('can_manage_tags', 'Can manage tags'),), 'ordering': ('name',), }, - bases=(openslides.utils.rest_api.RESTModelMixin, openslides.utils.models.AbsoluteUrlMixin, models.Model), + bases=(openslides.utils.rest_api.RESTModelMixin, models.Model), ), migrations.RunPython( add_default_projector, diff --git a/openslides/core/models.py b/openslides/core/models.py index c1d36f8ab..e7a9c36a3 100644 --- a/openslides/core/models.py +++ b/openslides/core/models.py @@ -3,7 +3,6 @@ from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop from jsonfield import JSONField -from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.projector import ProjectorElement from openslides.utils.rest_api import RESTModelMixin @@ -83,7 +82,7 @@ class Projector(RESTModelMixin, models.Model): yield requirement -class CustomSlide(RESTModelMixin, AbsoluteUrlMixin, models.Model): +class CustomSlide(RESTModelMixin, models.Model): """ Model for slides with custom content. """ @@ -104,7 +103,7 @@ class CustomSlide(RESTModelMixin, AbsoluteUrlMixin, models.Model): return self.title -class Tag(RESTModelMixin, AbsoluteUrlMixin, models.Model): +class Tag(RESTModelMixin, models.Model): """ Model for tags. This tags can be used for other models like agenda items, motions or assignments. diff --git a/openslides/core/signals.py b/openslides/core/signals.py index f549468a3..877e87b9c 100644 --- a/openslides/core/signals.py +++ b/openslides/core/signals.py @@ -3,7 +3,11 @@ from django.dispatch import Signal from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop -from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable +from openslides.config.api import ( + ConfigGroup, + ConfigGroupedCollection, + ConfigVariable, +) # This signal is sent when the migrate command is done. That means it is sent # after post_migrate sending and creating all Permission objects. Don't use it diff --git a/openslides/core/templates/base.html b/openslides/core/templates/base.html deleted file mode 100644 index 1d8bc534e..000000000 --- a/openslides/core/templates/base.html +++ /dev/null @@ -1,163 +0,0 @@ -{% load tags %} -{% load i18n %} -{% load staticfiles %} - - - - - - - {% block title %}{% get_config 'event_name' %}{% endblock %} - - - - - - - - {% for stylefile in extra_stylefiles %} - - {% endfor %} - {% block header %}{% endblock %} - - - - - - - - {% block body %} - -
-
- - -
- -
- - -
-
-
-
- - {% for message in messages %} -
- - {{ message|safe }} -
- {% endfor %} -
- {% block content %}{% endblock %} -
-
-
- -
- -
-
- {% endblock %} - - {% include 'core/chatbox.html' %} - - - - - - - - - - {% for javascript in extra_javascript %} - - {% endfor %} - {% block javascript %}{% endblock %} - - diff --git a/openslides/core/templates/core/chatbox.html b/openslides/core/templates/core/chatbox.html deleted file mode 100644 index e5dfb8448..000000000 --- a/openslides/core/templates/core/chatbox.html +++ /dev/null @@ -1,22 +0,0 @@ -{% load i18n %} - -{% if chat_messages != None %} - -{% endif %} diff --git a/openslides/core/templates/core/customslide_slide.html b/openslides/core/templates/core/customslide_slide.html deleted file mode 100644 index a14e39914..000000000 --- a/openslides/core/templates/core/customslide_slide.html +++ /dev/null @@ -1,9 +0,0 @@ -{% load i18n %} - -

- {{ slide.title }} -

- -{% if slide.text %} - {{ slide.text|safe|linebreaks }} -{% endif %} diff --git a/openslides/core/templates/core/customslide_update.html b/openslides/core/templates/core/customslide_update.html deleted file mode 100644 index 6d6c088a4..000000000 --- a/openslides/core/templates/core/customslide_update.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block title %}{% trans "Custom slide" %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans 'Custom slide' %} - - {% trans "Back to overview" %} - -

-
{% csrf_token %} - {% include "form.html" %} -

- {% include "formbuttons_saveapply.html" %} -

- * {% trans "required" %} -
-{% endblock %} diff --git a/openslides/core/templates/core/dashboard.html b/openslides/core/templates/core/dashboard.html deleted file mode 100644 index 6754d6cce..000000000 --- a/openslides/core/templates/core/dashboard.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} -{% load staticfiles %} - -{% block header %} - -{% endblock %} - -{% block javascript %} - - -{% endblock %} - -{% block title %}{% trans 'Dashboard' %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans 'Dashboard' %} - - - - {% trans 'Widgets' %} - - -

-
-
- {% for widget in widgets %} - {% if widget.default_column == 1 %} - {{ widget.get_html }} - {% endif %} - {% endfor %} -
-
- {% for widget in widgets %} - {% if widget.default_column == 2 %} - {{ widget.get_html }} - {% endif %} - {% endfor %} -
-
-{% endblock %} diff --git a/openslides/core/templates/core/error.html b/openslides/core/templates/core/error.html deleted file mode 100644 index ebf045dd5..000000000 --- a/openslides/core/templates/core/error.html +++ /dev/null @@ -1,8 +0,0 @@ -{% extends 'base.html' %} - -{% block title %}{{ http_error.status_code }} {{ http_error.name }} – {{ block.super }}{% endblock %} - -{% block content %} -

{{ http_error.name }}

-

{{ http_error.description }}

-{% endblock %} diff --git a/openslides/core/templates/core/search.html b/openslides/core/templates/core/search.html deleted file mode 100644 index b296672c9..000000000 --- a/openslides/core/templates/core/search.html +++ /dev/null @@ -1,58 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %}{{ block.super }} – {% trans "Search" %}{% endblock %} - -{% block content %} -

{% trans 'Search results' %}

- - - {% if query %} - {% for result in page.object_list %} - {% if forloop.first %} -
    - {% endif %} - {% if result.app_label %} - {% with result_template=result.app_label|add:"-results.html" %} - {% include "search/"|add:result_template %} - {% endwith %} - {% endif %} - {% if forloop.last %} -
- {% endif %} - {% empty %} -

{% trans "No results found." %}

- {% endfor %} - - {% if page.has_previous or page.has_next %} -
- {% if page.has_previous %}{% endif %}« Previous{% if page.has_previous %}{% endif %} - | - {% if page.has_next %}{% endif %}Next »{% if page.has_next %}{% endif %} -
- {% endif %} - {% endif %} -{% endblock %} diff --git a/openslides/core/templates/core/select_widgets.html b/openslides/core/templates/core/select_widgets.html deleted file mode 100644 index fc9c2f838..000000000 --- a/openslides/core/templates/core/select_widgets.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %}{% trans 'Select widgets' %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans 'Select widgets' %} - - {% trans 'Back to overview' %} - -

- -
{% csrf_token %} - -

- -

-
-{% endblock %} diff --git a/openslides/core/templates/core/tag_list.html b/openslides/core/templates/core/tag_list.html deleted file mode 100644 index 731844b68..000000000 --- a/openslides/core/templates/core/tag_list.html +++ /dev/null @@ -1,61 +0,0 @@ -{% extends "base.html" %} -{% load i18n %} -{% load staticfiles %} - -{% block title %}{% trans "Tags" %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans "Tags" %} - - - {% trans "Back to overview" %} - - -

- -
- - - {% trans 'Save' %} -
- - - - - - - - - - - {% for tag in tag_list %} - - - - - {% endfor %} -
{% trans "Tag" %}{% trans "Actions" %}
{{ tag }} - - - - - - - - -
-

{% trans "You can use these tags for agenda items, motions and elections." %}

-{% endblock %} - -{% block javascript %} - -{% endblock %} diff --git a/openslides/core/templates/core/version.html b/openslides/core/templates/core/version.html deleted file mode 100644 index 9805f862b..000000000 --- a/openslides/core/templates/core/version.html +++ /dev/null @@ -1,20 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %}{% trans 'Version' %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans 'Version' %}

- -{% endblock %} diff --git a/openslides/core/templates/core/widget.html b/openslides/core/templates/core/widget.html deleted file mode 100644 index f44f32239..000000000 --- a/openslides/core/templates/core/widget.html +++ /dev/null @@ -1,40 +0,0 @@ -{% load i18n %} - -
-
- {% block header %} -

- -
- -
-
- -
- {{ widget.get_verbose_name }} -

- {% endblock %} -
-
- {% block content-wrapper %} -
- {% block content %} - {% endblock %} -
-
- {% block footer %} - {% if widget.get_url_for_more %} - -

{% trans 'More ...' %}

-
- {% endif %} - {% endblock %} -
- {% endblock %} -
-
diff --git a/openslides/core/templates/core/widget_customslide.html b/openslides/core/templates/core/widget_customslide.html deleted file mode 100644 index a2ff73294..000000000 --- a/openslides/core/templates/core/widget_customslide.html +++ /dev/null @@ -1,49 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} - -
- - - {% trans 'New' %} - -{% endblock %} diff --git a/openslides/core/templates/core/widget_welcome.html b/openslides/core/templates/core/widget_welcome.html deleted file mode 100644 index 07b366ebb..000000000 --- a/openslides/core/templates/core/widget_welcome.html +++ /dev/null @@ -1,12 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} - {% with 'welcome_text'|get_config as welcometext %} - {% if welcometext %} -

{{ welcometext|safe|linebreaks }}

- {% endif %} - {% endwith %} -{% endblock %} diff --git a/openslides/core/templates/form.html b/openslides/core/templates/form.html deleted file mode 100644 index 9d5fa4a7b..000000000 --- a/openslides/core/templates/form.html +++ /dev/null @@ -1,22 +0,0 @@ -{{ form.media }} -{% if form.non_field_errors %} - -{% endif %} -{% for field in form %} -
- - {{ field }} - {% if field.errors %} - {{ field.errors }} - {% endif %} - {% if field.help_text %} - {{ field.help_text }} - {% endif %} -
-{% endfor %} diff --git a/openslides/core/templates/formbuttons_save.html b/openslides/core/templates/formbuttons_save.html deleted file mode 100644 index 25dd6fbf4..000000000 --- a/openslides/core/templates/formbuttons_save.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n %} - - diff --git a/openslides/core/templates/formbuttons_saveapply.html b/openslides/core/templates/formbuttons_saveapply.html deleted file mode 100644 index d1be3e218..000000000 --- a/openslides/core/templates/formbuttons_saveapply.html +++ /dev/null @@ -1,8 +0,0 @@ -{% load i18n %} - - - diff --git a/openslides/core/templatetags/__init__.py b/openslides/core/templatetags/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/openslides/core/templatetags/tags.py b/openslides/core/templatetags/tags.py deleted file mode 100644 index 6367a8e4b..000000000 --- a/openslides/core/templatetags/tags.py +++ /dev/null @@ -1,53 +0,0 @@ -from django import template -from django.utils.translation import ugettext as _ - -from openslides.config.api import config - -register = template.Library() - - -# TODO: remove the tag get_config -@register.simple_tag -def get_config(key): - return config[key] - - -@register.filter # noqa -def get_config(key): - return config[key] - - -@register.filter -def trans(value): - return _(value) - - -@register.filter -def absolute_url(model, link=None): - """ - Returns the absolute_url to a model. The 'link' argument decides which url - will be returned. See get_absolute_url() in the model. - - Example: {{ motion|absolute_url:'delete' }} - """ - try: - if link is None: - url = model.get_absolute_url() - else: - url = model.get_absolute_url(link) - except ValueError: - url = '' - return url - - -@register.filter -def first_line(text): - try: - lines = text.split('\n') - except AttributeError: - return '' - if len(lines) > 1 or len(lines[0]) > 30: - s = "%s ..." - else: - s = "%s" - return s % lines[0][:30] diff --git a/openslides/core/urls.py b/openslides/core/urls.py index 3e556ddea..88093fd89 100644 --- a/openslides/core/urls.py +++ b/openslides/core/urls.py @@ -1,46 +1,9 @@ from django.conf.urls import patterns, url -from openslides.utils.views import RedirectView - from . import views urlpatterns = patterns( '', - # Redirect to dashboard URL - url(r'^$', - RedirectView.as_view(url_name='core_dashboard'), - name='home',), - - url(r'^dashboard/$', - views.DashboardView.as_view(), - name='core_dashboard'), - - url(r'^dashboard/select_widgets/$', - views.SelectWidgetsView.as_view(), - name='core_select_widgets'), - - url(r'^version/$', - views.VersionView.as_view(), - name='core_version',), - - url(r'^search/$', - views.SearchView(), - name='core_search',), - - # CustomSlide urls - url(r'^customslide/new/$', - views.CustomSlideCreateView.as_view(), - name='customslide_create'), - - url(r'^customslide/(?P\d+)/edit/$', - views.CustomSlideUpdateView.as_view(), - name='customslide_update'), - - url(r'^customslide/(?P\d+)/del/$', - views.CustomSlideDeleteView.as_view(), - name='customslide_delete'), - - # Ajax Urls url(r'^core/url_patterns/$', views.UrlPatternsView.as_view(), name='core_url_patterns'), diff --git a/openslides/core/views.py b/openslides/core/views.py index 935d1e132..ae11d6b5c 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -1,38 +1,18 @@ import re -from django.conf import settings -from django.contrib import messages from django.contrib.staticfiles import finders -from django.core.exceptions import PermissionDenied -from django.core.urlresolvers import get_resolver, reverse -from django.db import IntegrityError +from django.core.urlresolvers import get_resolver from django.http import HttpResponse -from django.shortcuts import redirect, render_to_response -from django.template import RequestContext -from django.utils.importlib import import_module -from django.utils.translation import ugettext as _ -from haystack.views import SearchView as _SearchView -from openslides import __version__ as openslides_version -from openslides.config.api import config from openslides.utils import views as utils_views -from openslides.utils.plugins import ( - get_plugin_description, - get_plugin_verbose_name, - get_plugin_version, -) from openslides.utils.rest_api import ( ModelViewSet, ReadOnlyModelViewSet, Response, ValidationError, - detail_route + detail_route, ) -from openslides.utils.signals import template_manipulation -from openslides.utils.widgets import Widget -from .exceptions import TagException -from .forms import SelectWidgetsForm from .models import CustomSlide, Projector, Tag from .serializers import ( CustomSlideSerializer, @@ -211,272 +191,3 @@ class UrlPatternsView(utils_views.APIView): url, url_kwargs = normalized_regex_bits[0] result[pattern_name] = self.URL_KWARGS_REGEX.sub(r':\1', url) return result - - -class ErrorView(utils_views.View): - """ - View for Http 403, 404 and 500 error pages. - """ - status_code = None - - def dispatch(self, request, *args, **kwargs): - http_error_strings = { - 403: {'name': _('Forbidden'), - 'description': _('Sorry, you have no permission to see this page.'), - 'status_code': '403'}, - 404: {'name': _('Not Found'), - 'description': _('Sorry, the requested page could not be found.'), - 'status_code': '404'}, - 500: {'name': _('Internal Server Error'), - 'description': _('Sorry, there was an unknown error. Please contact the event manager.'), - 'status_code': '500'}} - context = {} - context['http_error'] = http_error_strings[self.status_code] - template_manipulation.send(sender=self.__class__, request=request, context=context) - response = render_to_response( - 'core/error.html', - context_instance=RequestContext(request, context)) - response.status_code = self.status_code - return response - - -# TODO: Remove the following classes one by one. - -class DashboardView(utils_views.AjaxMixin, utils_views.TemplateView): - """ - Overview over all possible slides, the overlays and a live view: the - Dashboard of OpenSlides. This main view uses the widget api to collect all - widgets from all apps. See openslides.utils.widgets.Widget for more details. - """ - required_permission = 'core.can_see_dashboard' - template_name = 'core/dashboard.html' - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - widgets = [] - for widget in Widget.get_all(self.request): - if widget.is_active(): - widgets.append(widget) - context['extra_stylefiles'].extend(widget.get_stylesheets()) - context['extra_javascript'].extend(widget.get_javascript_files()) - context['widgets'] = widgets - return context - - -class SelectWidgetsView(utils_views.TemplateView): - """ - Shows a form to select which widgets should be displayed on the own - dashboard. The setting is saved in the session. - """ - # TODO: Use another base view class here, e. g. a FormView - required_permission = 'core.can_see_dashboard' - template_name = 'core/select_widgets.html' - - def get_context_data(self, **kwargs): - context = super().get_context_data(**kwargs) - widgets = Widget.get_all(self.request) - for widget in widgets: - initial = {'widget': widget.is_active()} - prefix = widget.name - if self.request.method == 'POST': - widget.form = SelectWidgetsForm( - self.request.POST, - prefix=prefix, - initial=initial) - else: - widget.form = SelectWidgetsForm(prefix=prefix, initial=initial) - context['widgets'] = widgets - return context - - def post(self, request, *args, **kwargs): - """ - Activates or deactivates the widgets in a post request. - """ - context = self.get_context_data(**kwargs) - session_widgets = self.request.session.get('widgets', {}) - for widget in context['widgets']: - if widget.form.is_valid(): - session_widgets[widget.name] = widget.form.cleaned_data['widget'] - else: - messages.error(request, _('There are errors in the form.')) - break - else: - self.request.session['widgets'] = session_widgets - return redirect(reverse('core_dashboard')) - - -class VersionView(utils_views.TemplateView): - """ - Shows version infos. - """ - template_name = 'core/version.html' - - def get_context_data(self, **kwargs): - """ - Adds version strings to the context. - """ - context = super().get_context_data(**kwargs) - context['modules'] = [{'verbose_name': 'OpenSlides', - 'description': '', - 'version': openslides_version}] - # Versions of plugins. - for plugin in settings.INSTALLED_PLUGINS: - context['modules'].append({'verbose_name': get_plugin_verbose_name(plugin), - 'description': get_plugin_description(plugin), - 'version': get_plugin_version(plugin)}) - return context - - -class SearchView(_SearchView): - """ - Shows search result page. - """ - template = 'core/search.html' - - def __call__(self, request): - if not request.user.is_authenticated() and not config['system_enable_anonymous']: - raise PermissionDenied - return super().__call__(request) - - def extra_context(self): - """ - Adds extra context variables to set navigation and search filter. - - Returns a context dictionary. - """ - context = {} - template_manipulation.send( - sender=self.__class__, request=self.request, context=context) - context['models'] = self.get_indexed_searchmodels() - context['get_values'] = self.request.GET.getlist('models') - return context - - def get_indexed_searchmodels(self): - """ - Iterate over all INSTALLED_APPS and return a list of models which are - indexed by haystack/whoosh for using in customized model search filter - in search template search.html. Each list entry contains a verbose name - of the model and a special form field value for haystack (app_name.model_name), - e.g. ['Agenda', 'agenda.item']. - """ - models = [] - # TODO: cache this query! - for app in settings.INSTALLED_APPS: - try: - module = import_module(app + '.search_indexes') - except ImportError: - pass - else: - models.append([module.Index.modelfilter_name, module.Index.modelfilter_value]) - return models - - -class CustomSlideViewMixin(object): - """ - Mixin for for CustomSlide Views. - """ - fields = ('title', 'text', 'weight',) - required_permission = 'core.can_manage_projector' - template_name = 'core/customslide_update.html' - model = CustomSlide - success_url_name = 'core_dashboard' - url_name_args = [] - - -class CustomSlideCreateView(CustomSlideViewMixin, utils_views.CreateView): - """ - Create a custom slide. - """ - pass - - -class CustomSlideUpdateView(CustomSlideViewMixin, utils_views.UpdateView): - """ - Update a custom slide. - """ - pass - - -class CustomSlideDeleteView(CustomSlideViewMixin, utils_views.DeleteView): - """ - Delete a custom slide. - """ - pass - - -class TagListView(utils_views.AjaxMixin, utils_views.ListView): - """ - View to list and manipulate tags. - - Shows all tags when requested via a GET-request. Manipulates tags with - POST-requests. - """ - - model = Tag - required_permission = 'core.can_manage_tags' - - def post(self, *args, **kwargs): - return self.ajax_get(*args, **kwargs) - - def ajax_get(self, request, *args, **kwargs): - name, value = request.POST['name'], request.POST.get('value', None) - - # Create a new tag - if name == 'new': - try: - tag = Tag.objects.create(name=value) - except IntegrityError: - # The name of the tag is already taken. It must be unique. - self.error = 'Tag name is already taken' - else: - self.pk = tag.pk - self.action = 'created' - - # Update an existing tag - elif name.startswith('edit-tag-'): - try: - self.get_tag_queryset(name, 9).update(name=value) - except TagException as error: - self.error = str(error) - except IntegrityError: - self.error = 'Tag name is already taken' - except Tag.DoesNotExist: - self.error = 'Tag does not exist' - else: - self.action = 'updated' - - # Delete a tag - elif name.startswith('delete-tag-'): - try: - self.get_tag_queryset(name, 11).delete() - except TagException as error: - self.error = str(error) - except Tag.DoesNotExist: - self.error = 'Tag does not exist' - else: - self.action = 'deleted' - return super().ajax_get(request, *args, **kwargs) - - def get_tag_queryset(self, name, place_in_str): - """ - Get a django-tag-queryset from a string. - - 'name' is the string in which the pk is (at the end). - - 'place_in_str' is the place where to look for the pk. It has to be an int. - - Returns a Tag QuerySet or raises TagException. - Also sets self.pk to the pk inside the name. - """ - try: - self.pk = int(name[place_in_str:]) - except ValueError: - raise TagException('Invalid name in request') - return Tag.objects.filter(pk=self.pk) - - def get_ajax_context(self, **context): - return super().get_ajax_context( - pk=getattr(self, 'pk', None), - action=getattr(self, 'action', None), - error=getattr(self, 'error', None), - **context) diff --git a/openslides/core/widgets.py b/openslides/core/widgets.py deleted file mode 100644 index f5a350cdb..000000000 --- a/openslides/core/widgets.py +++ /dev/null @@ -1,46 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.config.api import config -from openslides.projector.api import get_active_slide -from openslides.utils.widgets import Widget - -from .models import CustomSlide - - -class WelcomeWidget(Widget): - """ - Welcome widget with static info for all users. - """ - name = 'welcome' - required_permission = 'core.can_see_dashboard' - default_column = 1 - default_weight = 10 - template_name = 'core/widget_welcome.html' - icon_css_class = 'icon-home' - - def get_verbose_name(self): - return config['welcome_title'] - - -class CustonSlideWidget(Widget): - """ - Widget to control custom slides. - """ - name = 'custom_slide' - verbose_name = ugettext_lazy('Custom Slides') - required_permission = 'core.can_manage_projector' - default_column = 2 - default_weight = 30 - template_name = 'core/widget_customslide.html' - context = None - icon_css_class = 'icon-star' - - def get_context_data(self, **context): - """ - Adds custom slides and a flag whether the welcome page is active to - the context. - """ - context['slides'] = CustomSlide.objects.all().order_by('weight') - context['welcomepage_is_active'] = ( - get_active_slide().get('callback', 'default') == 'default') - return super(CustonSlideWidget, self).get_context_data(**context) diff --git a/openslides/global_settings.py b/openslides/global_settings.py index e28b9b52e..5707b001a 100644 --- a/openslides/global_settings.py +++ b/openslides/global_settings.py @@ -1,5 +1,5 @@ -import os import copy +import os from django.utils.translation import ugettext_lazy @@ -11,9 +11,6 @@ AUTH_USER_MODEL = 'users.User' AUTHENTICATION_BACKENDS = ('openslides.users.auth.CustomizedModelBackend',) -LOGIN_URL = '/users/' -LOGIN_REDIRECT_URL = '/' - SESSION_COOKIE_NAME = 'OpenSlidesSessionID' LANGUAGES = ( @@ -89,9 +86,7 @@ INSTALLED_APPS = ( 'haystack', # full-text-search 'ckeditor', 'rest_framework', - 'openslides.poll', - 'openslides.account', - 'openslides.projector', + 'openslides.poll', # TODO: try to remove this line 'openslides.agenda', 'openslides.motions', 'openslides.assignments', diff --git a/openslides/mediafiles/apps.py b/openslides/mediafiles/apps.py index 52fdaee00..0cdbff423 100644 --- a/openslides/mediafiles/apps.py +++ b/openslides/mediafiles/apps.py @@ -6,24 +6,9 @@ class MediafileAppConfig(AppConfig): verbose_name = 'OpenSlides Mediafiles' def ready(self): - # Load main menu entry and widgets. - # Do this by just importing all from these files. - from . import main_menu, widgets # noqa - # Import all required stuff. - from openslides.projector.api import register_slide from openslides.utils.rest_api import router - from openslides.utils.signals import template_manipulation - from .slides import mediafile_presentation_as_slide - from .template import add_mediafile_stylesheets from .views import MediafileViewSet - # Connect template signal. - template_manipulation.connect(add_mediafile_stylesheets, dispatch_uid='add_mediafile_stylesheets') - - # Register slides. - Mediafile = self.get_model('Mediafile') - register_slide('mediafile', mediafile_presentation_as_slide, Mediafile) - # Register viewsets. router.register('mediafiles/mediafile', MediafileViewSet) diff --git a/openslides/mediafiles/forms.py b/openslides/mediafiles/forms.py deleted file mode 100644 index 90010832b..000000000 --- a/openslides/mediafiles/forms.py +++ /dev/null @@ -1,38 +0,0 @@ -from django.forms import ModelForm - -from openslides.utils.forms import CssClassMixin - -from .models import Mediafile - - -class MediafileFormMixin(object): - """ - Mixin for mediafile forms. It is used to delete old files. - """ - def save(self, *args, **kwargs): - """ - Method to save the form. Here the override is to delete old files. - """ - if self.instance.pk is not None: - old_file = Mediafile.objects.get(pk=self.instance.pk).mediafile - if not old_file == self.instance.mediafile: - old_file.delete() - return super(MediafileFormMixin, self).save(*args, **kwargs) - - -class MediafileNormalUserForm(MediafileFormMixin, CssClassMixin, ModelForm): - """ - This form is only used by normal users, not by managers. - """ - class Meta: - model = Mediafile - fields = ('mediafile', 'title', 'is_presentable') - - -class MediafileManagerForm(MediafileFormMixin, CssClassMixin, ModelForm): - """ - This form is only used be managers, not by normal users. - """ - class Meta: - model = Mediafile - fields = ('mediafile', 'title', 'uploader', 'is_presentable') diff --git a/openslides/mediafiles/main_menu.py b/openslides/mediafiles/main_menu.py deleted file mode 100644 index e7e8cefc2..000000000 --- a/openslides/mediafiles/main_menu.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class MediafileMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the mediafile app. - """ - verbose_name = ugettext_lazy('Files') - default_weight = 60 - pattern_name = '/mediafiles' - icon_css_class = 'glyphicon-paperclip' - - def check_permission(self): - return ( - self.request.user.has_perm('mediafiles.can_see') or - self.request.user.has_perm('mediafiles.can_upload') or - self.request.user.has_perm('mediafiles.can_manage')) diff --git a/openslides/mediafiles/models.py b/openslides/mediafiles/models.py index ea0b3b882..749ee138f 100644 --- a/openslides/mediafiles/models.py +++ b/openslides/mediafiles/models.py @@ -1,17 +1,15 @@ import mimetypes -from django.core.urlresolvers import reverse from django.db import models from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop from openslides.projector.models import SlideMixin -from openslides.utils.models import AbsoluteUrlMixin -from openslides.utils.rest_api import RESTModelMixin from openslides.users.models import User +from openslides.utils.rest_api import RESTModelMixin -class Mediafile(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): +class Mediafile(RESTModelMixin, SlideMixin, models.Model): """ Class for uploaded files which can be delivered under a certain url. """ @@ -68,19 +66,6 @@ class Mediafile(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): self.filetype = ugettext_noop('unknown') return super(Mediafile, self).save(*args, **kwargs) - def get_absolute_url(self, link='update'): - """ - Returns the URL to a mediafile. The link can be 'projector', - 'update' or 'delete'. - """ - if link == 'update': - url = reverse('mediafile_update', kwargs={'pk': str(self.pk)}) - elif link == 'delete': - url = reverse('mediafile_delete', kwargs={'pk': str(self.pk)}) - else: - url = super(Mediafile, self).get_absolute_url(link) - return url - def get_filesize(self): """ Transforms bytes to kilobytes or megabytes. Returns the size as string. diff --git a/openslides/mediafiles/search_indexes.py b/openslides/mediafiles/search_indexes.py index a1de7c4c9..8854f8a35 100644 --- a/openslides/mediafiles/search_indexes.py +++ b/openslides/mediafiles/search_indexes.py @@ -1,4 +1,5 @@ from haystack import indexes + from .models import Mediafile diff --git a/openslides/mediafiles/slides.py b/openslides/mediafiles/slides.py deleted file mode 100644 index 95cb991bb..000000000 --- a/openslides/mediafiles/slides.py +++ /dev/null @@ -1,29 +0,0 @@ -from django.template.loader import render_to_string - -from openslides.config.api import config -from openslides.projector.api import SlideError - -from .models import Mediafile - - -def mediafile_presentation_as_slide(**kwargs): - """ - Return the html code for a presentation of a Mediafile. - - At the moment, only the presentation of pdfs is supported. - - The function is registered during app loading. - """ - file_pk = kwargs.get('pk', None) - page_num = kwargs.get('page_num', 1) - - try: - pdf = Mediafile.objects.get( - pk=file_pk, - filetype__in=Mediafile.PRESENTABLE_FILE_TYPES, - is_presentable=True) - except Mediafile.DoesNotExist: - raise SlideError - context = {'pdf': pdf, 'page_num': page_num, - 'fullscreen': config['pdf_fullscreen']} - return render_to_string('mediafiles/presentation_slide.html', context) diff --git a/openslides/mediafiles/template.py b/openslides/mediafiles/template.py deleted file mode 100644 index 90dc78de1..000000000 --- a/openslides/mediafiles/template.py +++ /dev/null @@ -1,7 +0,0 @@ -def add_mediafile_stylesheets(sender, request, context, **kwargs): - """ - Receiver function to add the mediafile.css to the context. It is - connected to the signal openslides.utils.signals.template_manipulation - during app loading. - """ - context['extra_stylefiles'].append('css/mediafile.css') diff --git a/openslides/mediafiles/templates/mediafiles/mediafile_form.html b/openslides/mediafiles/templates/mediafiles/mediafile_form.html deleted file mode 100644 index 70802f997..000000000 --- a/openslides/mediafiles/templates/mediafiles/mediafile_form.html +++ /dev/null @@ -1,37 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %} - {% if mediafile %} - {% trans "Edit file" %} - {% else %} - {% trans "New file" %} - {% endif %} - – {{ block.super }} -{% endblock %} - -{% block content %} -

- {% if mediafile %} - {% trans "Edit file" %} - {% else %} - {% trans "New file" %} - {% endif %} - - {% trans "Back to overview" %} - -

-
{% csrf_token %} - {% include "form.html" %} -

- {% if perms.mediafile.can_manage %} - {% include "formbuttons_saveapply.html" %} - {% else %} - {% include "formbuttons_save.html" %} - {% endif %} - {% trans 'Cancel' %} -

- * {% trans "required" %} -
-{% endblock %} diff --git a/openslides/mediafiles/templates/mediafiles/mediafile_list.html b/openslides/mediafiles/templates/mediafiles/mediafile_list.html deleted file mode 100644 index b1095701f..000000000 --- a/openslides/mediafiles/templates/mediafiles/mediafile_list.html +++ /dev/null @@ -1,59 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} -{% load tags %} - -{% block title %}{% trans 'Files' %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans 'Files' %} - - {% if perms.mediafiles.can_upload %} - {% trans "New" %} - {% endif %} - -

- - - - - - - - {% if perms.mediafiles.can_manage or perms.mediafiles.can_upload %} - - {% endif %} - - {% for mediafile in mediafile_list %} - - - - - - - {% if perms.mediafiles.can_manage or perms.mediafiles.can_upload %} - - {% endif %} - - {% empty %} - - - - {% endfor %} -
{% trans 'Title' %}{% trans 'Type' %}{% trans 'Size' %}{% trans 'Upload time' %}{% trans 'Uploaded by' %}{% trans "Actions" %}
{{ mediafile }}{% trans mediafile.filetype %}{{ mediafile.get_filesize }}{{ mediafile.timestamp }}{{ mediafile.uploader }} - {% if mediafile.with_action_buttons %} - - - - {% if perms.mediafiles.can_manage and mediafile.is_presentable %}{% if mediafile.filetype in mediafile.PRESENTABLE_FILE_TYPES %} - - - - - {% endif %}{% endif %} - - {% else %} -   - {% endif %} -
{% trans 'No files available.' %}
-{% endblock %} diff --git a/openslides/mediafiles/templates/mediafiles/presentation_slide.html b/openslides/mediafiles/templates/mediafiles/presentation_slide.html deleted file mode 100644 index 9e5d8d2a1..000000000 --- a/openslides/mediafiles/templates/mediafiles/presentation_slide.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load i18n %} -{% load static %} - - - - - - - -
- -
diff --git a/openslides/mediafiles/templates/mediafiles/widget_pdfpresentation.html b/openslides/mediafiles/templates/mediafiles/widget_pdfpresentation.html deleted file mode 100644 index aeaa6627d..000000000 --- a/openslides/mediafiles/templates/mediafiles/widget_pdfpresentation.html +++ /dev/null @@ -1,50 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} -
- -
- - - -
-
- {% trans "Page" %}: - - -
-
- - -{% endblock %} diff --git a/openslides/mediafiles/urls.py b/openslides/mediafiles/urls.py deleted file mode 100644 index 789cb9196..000000000 --- a/openslides/mediafiles/urls.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.conf.urls import patterns, url - -from . import views - -urlpatterns = patterns( - '', - url(r'^pdf/next/$', - views.PdfNextView.as_view(), - name='mediafiles_next_pdf_page'), - url(r'^pdf/prev/$', - views.PdfPreviousView.as_view(), - name='mediafiles_prev_pdf_page'), - url(r'^pdf/target_page/$', - views.PdfGoToPageView.as_view(), - name='mediafiles_target_pdf_page'), - url(r'^pdf/toggle_fullscreen/$', - views.PdfToggleFullscreenView.as_view(), - name='mediafiles_toggle_fullscreen') -) diff --git a/openslides/mediafiles/views.py b/openslides/mediafiles/views.py index 46133f7d6..10a6576ba 100644 --- a/openslides/mediafiles/views.py +++ b/openslides/mediafiles/views.py @@ -1,108 +1,9 @@ -from django.http import HttpResponse - -from openslides.config.api import config -from openslides.projector.api import get_active_slide from openslides.utils.rest_api import ModelViewSet -from openslides.utils.views import (AjaxView, RedirectView) from .models import Mediafile from .serializers import MediafileSerializer -class PdfNavBaseView(AjaxView): - """ - BaseView for the Pdf Ajax Navigation. - """ - - def get_ajax_context(self, *args, **kwargs): - return {'current_page': self.active_slide['page_num']} - - def load_other_page(self, active_slide): - """ - Tell connected clients to load an other pdf page. - """ - config['projector_active_slide'] = active_slide - - -class PdfNextView(PdfNavBaseView): - """ - Activate the next Page of a pdf and return the number of the current page. - """ - - def get(self, request, *args, **kwargs): - """ - Increment the page number by 1. - - If the page number is set in the active slide, we are the value is - incremented by 1. Otherwise, it is the first page and it is set to 2. - """ - self.active_slide = get_active_slide() - if self.active_slide['callback'] == 'mediafile': - if 'page_num' not in self.active_slide: - self.active_slide['page_num'] = 2 - else: - self.active_slide['page_num'] += 1 - self.load_other_page(self.active_slide) - response = super(PdfNextView, self).get(self, request, *args, **kwargs) - else: - # no Mediafile is active and the JavaScript should not do anything. - response = HttpResponse() - return response - - -class PdfPreviousView(PdfNavBaseView): - """ - Activate the previous Page of a pdf and return the number of the current page. - """ - - def get(self, request, *args, **kwargs): - """ - Decrement the page number by 1. - - If the page number is set and it is greater than 1, it is decremented - by 1. Otherwise, it is the first page and nothing happens. - """ - self.active_slide = get_active_slide() - response = None - if self.active_slide['callback'] == 'mediafile': - if 'page_num' in self.active_slide and self.active_slide['page_num'] > 1: - self.active_slide['page_num'] -= 1 - self.load_other_page(self.active_slide) - response = super(PdfPreviousView, self).get(self, request, *args, **kwargs) - if not response: - response = HttpResponse() - return response - - -class PdfGoToPageView(PdfNavBaseView): - """ - Activate the page set in the textfield. - """ - - def get(self, request, *args, **kwargs): - target_page = int(request.GET.get('page_num')) - self.active_slide = get_active_slide() - if target_page: - self.active_slide['page_num'] = target_page - self.load_other_page(self.active_slide) - response = super(PdfGoToPageView, self).get(self, request, *args, **kwargs) - else: - response = HttpResponse() - return response - - -class PdfToggleFullscreenView(RedirectView): - """ - Toggle fullscreen mode for pdf presentations. - """ - allow_ajax = True - url_name = 'core_dashboard' - - def get_ajax_context(self, *args, **kwargs): - config['pdf_fullscreen'] = not config['pdf_fullscreen'] - return {'fullscreen': config['pdf_fullscreen']} - - class MediafileViewSet(ModelViewSet): """ API endpoint to list, retrieve, create, update and destroy mediafile diff --git a/openslides/mediafiles/widgets.py b/openslides/mediafiles/widgets.py deleted file mode 100644 index dd3a90d67..000000000 --- a/openslides/mediafiles/widgets.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget -from openslides.projector.api import get_active_slide - -from .models import Mediafile - - -class PDFPresentationWidget(Widget): - """ - Widget for presentable PDF files. - """ - name = 'presentations' - verbose_name = ugettext_lazy('Presentations') - required_permission = 'core.can_manage_projector' - default_column = 1 - default_weight = 75 - template_name = 'mediafiles/widget_pdfpresentation.html' - icon_css_class = 'icon-align-left' - # javascript_files = None # TODO: Add pdf.js stuff here. - - def get_context_data(self, **context): - pdfs = Mediafile.objects.filter( - filetype__in=Mediafile.PRESENTABLE_FILE_TYPES, - is_presentable=True) - current_page = get_active_slide().get('page_num', 1) - return super(PDFPresentationWidget, self).get_context_data( - pdfs=pdfs, - current_page=current_page, - **context) diff --git a/openslides/motions/apps.py b/openslides/motions/apps.py index 19c8b7807..3a84127e3 100644 --- a/openslides/motions/apps.py +++ b/openslides/motions/apps.py @@ -7,14 +7,9 @@ class MotionAppConfig(AppConfig): verbose_name = 'OpenSlides Motion' def ready(self): - # Load main menu entry, personal info and widgets. - # Do this by just importing all from these files. - from . import main_menu, personal_info, widgets # noqa - # Import all required stuff. from openslides.config.signals import config_signal from openslides.utils.rest_api import router - from openslides.projector.api import register_slide_model from .signals import create_builtin_workflows, setup_motion_config from .views import CategoryViewSet, MotionViewSet, WorkflowViewSet @@ -22,12 +17,6 @@ class MotionAppConfig(AppConfig): config_signal.connect(setup_motion_config, dispatch_uid='setup_motion_config') post_migrate.connect(create_builtin_workflows, dispatch_uid='motion_create_builtin_workflows') - # Register slides. - Motion = self.get_model('Motion') - MotionPoll = self.get_model('MotionPoll') - register_slide_model(Motion, 'motions/slide.html') - register_slide_model(MotionPoll, 'motions/motionpoll_slide.html') - # Register viewsets. router.register('motions/category', CategoryViewSet) router.register('motions/motion', MotionViewSet) diff --git a/openslides/motions/forms.py b/openslides/motions/forms.py deleted file mode 100644 index 048480e79..000000000 --- a/openslides/motions/forms.py +++ /dev/null @@ -1,206 +0,0 @@ -import collections - -from django import forms -from django.utils.translation import ugettext_lazy - -from openslides.mediafiles.models import Mediafile -from openslides.utils.forms import (CleanHtmlFormMixin, CssClassMixin, - CSVImportForm, LocalizedModelChoiceField) -from openslides.users.models import User - -from ckeditor.widgets import CKEditorWidget - -from .models import Category, Motion, Workflow, Tag - - -class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm): - """ - Base FormClass for a Motion. - - For it's own, it append the version data to the fields. - - The class can be mixed with the following mixins to add fields for the - submitter, supporters etc. - """ - clean_html_fields = ('text', 'reason') - - title = forms.CharField(widget=forms.TextInput(), label=ugettext_lazy("Title")) - """ - Title of the motion. Will be saved in a MotionVersion object. - """ - - text = forms.CharField(widget=CKEditorWidget(), label=ugettext_lazy("Text")) - """ - Text of the motion. Will be saved in a MotionVersion object. - """ - - reason = forms.CharField( - widget=CKEditorWidget(), required=False, label=ugettext_lazy("Reason")) - """ - Reason of the motion. will be saved in a MotionVersion object. - """ - - attachments = forms.ModelMultipleChoiceField( - queryset=Mediafile.objects.all(), - required=False, - label=ugettext_lazy('Attachments')) - """ - Attachments of the motion. - """ - - tags = forms.ModelMultipleChoiceField( - queryset=Tag.objects.all(), - required=False, - label=ugettext_lazy('Tags')) - - key_order = ('identifier', - 'title', - 'text', - 'reason', - 'submitter', - 'supporter', - 'category', - 'tags', - 'attachments', - 'workflow', - 'disable_versioning',) - """ - Order of fields, including optional fields from mixins (for example MotionSupporterMixin) - """ - - class Meta: - model = Motion - fields = () - - def __init__(self, *args, **kwargs): - """ - Fill the FormFields related to the version data with initial data. - Fill also the initial data for attachments and tags. - """ - self.motion = kwargs.get('instance', None) - self.initial = kwargs.setdefault('initial', {}) - if self.motion is not None: - last_version = self.motion.get_last_version() - self.initial['title'] = last_version.title - self.initial['text'] = last_version.text - self.initial['reason'] = last_version.reason - self.initial['attachments'] = self.motion.attachments.all() - self.initial['tags'] = self.motion.tags.all() - super(BaseMotionForm, self).__init__(*args, **kwargs) - - keys = self.fields.keys() - keys_order = [key for key in self.key_order if key in keys] - keys_order.extend(set(keys) - set(keys_order)) - self.fields = collections.OrderedDict([(key, self.fields[key]) for key in keys_order]) - - -class MotionSubmitterMixin(forms.ModelForm): - """Mixin to append the submitter field to a MotionForm.""" - - submitter = forms.ModelMultipleChoiceField( - User.objects, label=ugettext_lazy("Submitter"), required=False) - """Submitter of the motion. Can be one or more persons.""" - - def __init__(self, *args, **kwargs): - """Fill in the submitter of the motion as default value.""" - if self.motion is not None: - submitter = self.motion.submitters.all() - self.initial['submitter'] = submitter - super(MotionSubmitterMixin, self).__init__(*args, **kwargs) - - -class MotionSupporterMixin(forms.ModelForm): - """Mixin to append the supporter field to a Motionform.""" - - supporter = forms.ModelMultipleChoiceField( - User.objects, required=False, label=ugettext_lazy("Supporters")) - """Supporter of the motion. Can be one or more persons.""" - - def __init__(self, *args, **kwargs): - """Fill in the supporter of the motions as default value.""" - if self.motion is not None: - supporter = self.motion.supporters.all() - self.initial['supporter'] = supporter - super(MotionSupporterMixin, self).__init__(*args, **kwargs) - - -class MotionDisableVersioningMixin(forms.ModelForm): - """Mixin to add the option to the form to choose to disable versioning.""" - - disable_versioning = forms.BooleanField( - required=False, label=ugettext_lazy("Don't create a new version"), - help_text=ugettext_lazy("Don't create a new version. Useful e.g. for trivial changes.")) - """BooleanField to decide, if a new version will be created, or the - last_version will be used.""" - - -# TODO: Add category and identifier to the form as normal fields (the django way), -# not as 'new' field from 'new' forms. - -class MotionCategoryMixin(forms.ModelForm): - """ - Mixin to let the user choose the category for the motion. - """ - - category = forms.ModelChoiceField(queryset=Category.objects.all(), required=False, label=ugettext_lazy("Category")) - """ - Category of the motion. - """ - - def __init__(self, *args, **kwargs): - """ - Fill in the category of the motion as default value. - """ - if self.motion is not None: - category = self.motion.category - self.initial['category'] = category - super(MotionCategoryMixin, self).__init__(*args, **kwargs) - - -class MotionIdentifierMixin(forms.ModelForm): - """ - Mixin to let the user choose the identifier for the motion. - """ - - identifier = forms.CharField(required=False, label=ugettext_lazy('Identifier')) - - class Meta: - model = Motion - fields = ('identifier',) - - -class MotionWorkflowMixin(forms.ModelForm): - """ - Mixin to let the user change the workflow of the motion. - """ - - workflow = LocalizedModelChoiceField( - queryset=Workflow.objects.all(), - empty_label=None, - label=ugettext_lazy('Workflow'), - help_text=ugettext_lazy('Set a specific workflow to switch to it. ' - 'If you do so, the state of the motion will be reset.')) - - -class MotionCSVImportForm(CSVImportForm): - """ - Form for motion import via csv file. - """ - override = forms.BooleanField( - required=False, - label=ugettext_lazy('Override existing motions with the same identifier'), - help_text=ugettext_lazy('If this is active, every motion with the same identifier as in your csv file will be overridden.')) - """ - Flag to decide whether existing motions (according to the identifier) - should be overridden. - """ - - default_submitter = forms.ModelChoiceField( - User.objects.all(), - required=True, - label=ugettext_lazy('Default submitter'), - help_text=ugettext_lazy('This person is used as submitter for any line of your csv file which does not contain valid submitter data.')) - """ - Person which is used as submitter, if the line of the csv file does - not contain valid submitter data. - """ diff --git a/openslides/motions/main_menu.py b/openslides/motions/main_menu.py deleted file mode 100644 index 7906b4656..000000000 --- a/openslides/motions/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class MotionMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the motion app. - """ - verbose_name = ugettext_lazy('Motions') - required_permission = 'motions.can_see' - default_weight = 30 - pattern_name = '/motions' # TODO: use generic solution, see issue #1469 - icon_css_class = 'glyphicon-file' diff --git a/openslides/motions/models.py b/openslides/motions/models.py index 80b109128..23897b5fa 100644 --- a/openslides/motions/models.py +++ b/openslides/motions/models.py @@ -1,25 +1,28 @@ from django.conf import settings -from django.core.urlresolvers import reverse from django.db import models from django.db.models import Max from django.utils import formats from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop +from jsonfield import JSONField from openslides.config.api import config from openslides.core.models import Tag from openslides.mediafiles.models import Mediafile -from openslides.poll.models import (BaseOption, BasePoll, BaseVote, CollectDefaultVotesMixin) +from openslides.poll.models import ( + BaseOption, + BasePoll, + BaseVote, + CollectDefaultVotesMixin, +) from openslides.projector.models import SlideMixin -from jsonfield import JSONField -from openslides.utils.models import AbsoluteUrlMixin -from openslides.utils.rest_api import RESTModelMixin from openslides.users.models import User +from openslides.utils.rest_api import RESTModelMixin from .exceptions import WorkflowError -class Motion(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): +class Motion(RESTModelMixin, SlideMixin, models.Model): """ The Motion Class. @@ -191,22 +194,6 @@ class Motion(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): self.active_version = use_version self.save(update_fields=['active_version']) - def get_absolute_url(self, link='detail'): - """ - Return an URL for this version. - - The keyword argument 'link' can be 'detail', 'update' or 'delete'. - """ - if link == 'detail': - url = reverse('motion_detail', args=[str(self.pk)]) - elif link == 'update': - url = reverse('motion_update', args=[str(self.pk)]) - elif link == 'delete': - url = reverse('motion_delete', args=[str(self.pk)]) - else: - url = super(Motion, self).get_absolute_url(link) - return url - def version_data_changed(self, version): """ Compare the version with the last version of the motion. @@ -535,7 +522,7 @@ class Motion(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, models.Model): return config['motion_amendments_enabled'] and self.parent is not None -class MotionVersion(RESTModelMixin, AbsoluteUrlMixin, models.Model): +class MotionVersion(RESTModelMixin, models.Model): """ A MotionVersion object saves some date of the motion. """ @@ -572,22 +559,6 @@ class MotionVersion(RESTModelMixin, AbsoluteUrlMixin, models.Model): counter = self.version_number or ugettext_lazy('new') return "Motion %s, Version %s" % (self.motion_id, counter) - def get_absolute_url(self, link='detail'): - """ - Return the URL of this Version. - - The keyargument link can be 'detail' or 'delete'. - """ - if link == 'detail': - url = reverse('motion_version_detail', args=[str(self.motion.pk), - str(self.version_number)]) - elif link == 'delete': - url = reverse('motion_version_delete', args=[str(self.motion.pk), - str(self.version_number)]) - else: - url = super(MotionVersion, self).get_absolute_url(link) - return url - @property def active(self): """Return True, if the version is the active version of a motion. Else: False.""" @@ -600,7 +571,7 @@ class MotionVersion(RESTModelMixin, AbsoluteUrlMixin, models.Model): return self.motion -class Category(RESTModelMixin, AbsoluteUrlMixin, models.Model): +class Category(RESTModelMixin, models.Model): name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name")) """Name of the category.""" @@ -613,15 +584,6 @@ class Category(RESTModelMixin, AbsoluteUrlMixin, models.Model): def __str__(self): return self.name - def get_absolute_url(self, link='update'): - if link == 'update': - url = reverse('motion_category_update', args=[str(self.pk)]) - elif link == 'delete': - url = reverse('motion_category_delete', args=[str(self.pk)]) - else: - url = super(Category, self).get_absolute_url(link) - return url - class Meta: ordering = ['prefix'] @@ -699,7 +661,7 @@ class MotionOption(RESTModelMixin, BaseOption): class MotionPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin, - AbsoluteUrlMixin, BasePoll): + BasePoll): """The Class to saves the vote result for a motion poll.""" slide_callback_name = 'motionpoll' @@ -728,22 +690,6 @@ class MotionPoll(RESTModelMixin, SlideMixin, CollectDefaultVotesMixin, """Return a string, representing the poll.""" return _('Vote %d') % self.poll_number - def get_absolute_url(self, link='update'): - """ - Return an URL for the poll. - - The keyargument 'link' can be 'update' or 'delete'. - """ - if link == 'update': - url = reverse('motionpoll_update', args=[str(self.motion.pk), - str(self.poll_number)]) - elif link == 'delete': - url = reverse('motionpoll_delete', args=[str(self.motion.pk), - str(self.poll_number)]) - else: - url = super(MotionPoll, self).get_absolute_url(link) - return url - def set_options(self): """Create the option class for this poll.""" # TODO: maybe it is possible with .create() to call this without poll=self diff --git a/openslides/motions/pdf.py b/openslides/motions/pdf.py index 7b70bb250..dc3f59dc2 100644 --- a/openslides/motions/pdf.py +++ b/openslides/motions/pdf.py @@ -1,7 +1,6 @@ -from cgi import escape - -from operator import attrgetter import random +from cgi import escape +from operator import attrgetter from bs4 import BeautifulSoup from django.utils.translation import ugettext as _ @@ -16,9 +15,6 @@ from openslides.utils.pdf import stylesheet from .models import Category -# Needed to count the delegates -# TODO: find another way to do this. - def motions_to_pdf(pdf, motions): """ diff --git a/openslides/motions/personal_info.py b/openslides/motions/personal_info.py deleted file mode 100644 index 8d7df7298..000000000 --- a/openslides/motions/personal_info.py +++ /dev/null @@ -1,30 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.config.api import config -from openslides.utils.personal_info import PersonalInfo - - -class MotionSubmitterPersonalInfo(PersonalInfo): - """ - Class for personal info block for motion submitters. - """ - headline = ugettext_lazy('I submitted the following motions') - default_weight = 20 - - def get_queryset(self): - return None # TODO: Fix this after transforming everything using AngularJS. - - -class MotionSupporterPersonalInfo(PersonalInfo): - """ - Class for personal info block for motion supporters. - """ - headline = ugettext_lazy('I support the following motions') - default_weight = 30 - - def get_queryset(self): - if config['motion_min_supporters']: - return_value = None # TODO: Fix this after transforming everything using AngularJS. - else: - return_value = None - return return_value diff --git a/openslides/motions/search_indexes.py b/openslides/motions/search_indexes.py index 9b702f325..8f78acde5 100644 --- a/openslides/motions/search_indexes.py +++ b/openslides/motions/search_indexes.py @@ -1,4 +1,5 @@ from haystack import indexes + from .models import Motion diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 8802d1455..60c98bc5a 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -7,7 +7,8 @@ from openslides.utils.rest_api import ( IntegerField, ModelSerializer, PrimaryKeyRelatedField, - ValidationError,) + ValidationError, +) from .models import ( Category, @@ -18,7 +19,8 @@ from .models import ( MotionVersion, MotionVote, State, - Workflow,) + Workflow, +) def validate_workflow_field(value): diff --git a/openslides/motions/signals.py b/openslides/motions/signals.py index e420470fc..d19fba34e 100644 --- a/openslides/motions/signals.py +++ b/openslides/motions/signals.py @@ -1,8 +1,12 @@ from django import forms from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy, ugettext_noop, pgettext +from django.utils.translation import pgettext, ugettext_lazy, ugettext_noop -from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable +from openslides.config.api import ( + ConfigGroup, + ConfigGroupedCollection, + ConfigVariable, +) from openslides.poll.models import PERCENT_BASE_CHOICES from .models import State, Workflow diff --git a/openslides/motions/templates/motions/category_form.html b/openslides/motions/templates/motions/category_form.html deleted file mode 100644 index 9da1039b6..000000000 --- a/openslides/motions/templates/motions/category_form.html +++ /dev/null @@ -1,32 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block title %} - {% if category %} - {% trans "Edit category" %} - {% else %} - {% trans "New category" %} - {% endif %} - – {{ block.super }} -{% endblock %} - -{% block content %} -

- {% if category %} - {% trans "Edit category" %} - {% else %} - {% trans "New category" %} - {% endif %} -

-
{% csrf_token %} - {% include "form.html" %} -

- {% include "formbuttons_saveapply.html" %} - - {% trans 'Cancel' %} - -

- * {% trans "required" %} -
-{% endblock %} diff --git a/openslides/motions/templates/motions/category_list.html b/openslides/motions/templates/motions/category_list.html deleted file mode 100644 index e34ce20de..000000000 --- a/openslides/motions/templates/motions/category_list.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends "base.html" %} - -{% load tags %} -{% load i18n %} - -{% block title %}{% trans "Motions" %} – {{ block.super }}{% endblock %} - -{% block content %} -

- {% trans "Categories" %} - - {% if perms.motions.can_manage %} - {% trans 'New' %} - {% endif %} - {% trans "Back to overview" %} - -

- - - - - - - - {% for category in category_list %} - - - - - - {% empty %} - - - - {% endfor %} -
{% trans "Prefix" %}{% trans "Category name" %}{% trans "Actions" %}
{{ category.prefix }}{{ category }} - - - - - -
{% trans "No categories available." %}
-{% endblock %} diff --git a/openslides/motions/templates/motions/motion_detail.html b/openslides/motions/templates/motions/motion_detail.html deleted file mode 100644 index d71e01ecb..000000000 --- a/openslides/motions/templates/motions/motion_detail.html +++ /dev/null @@ -1,373 +0,0 @@ -{% extends "base.html" %} - -{% load tags %} -{% load i18n %} -{% load staticfiles %} -{% load humanize %} - -{% block title %}{% trans "Motion" %} {{ motion.identifier|default:'' }} – {{ block.super }}{% endblock %} - -{% block header %} - {{ block.super }} - -{% endblock %} - -{% block content %} -

- {{ title }} -
- - {% trans "Motion" %} {{ motion.identifier|default:'' }} - {% if motion.versions.count > 1 %} - | {% trans "Version" %} {{ version.version_number }} - {% if version == motion.active_version %} - {% trans 'This version is authorized' %} - {% endif %} - {% if version.version_number != motion.active_version.version_number %} - - {% trans "This version is not authorized." %} - - {% endif %} - {% endif %} - {% if motion.is_amendment %} - ({% trans "Amendment of" %} {{ motion.parent.identifier|default:motion.parent }}) - {% endif %} - - - - - {% trans "Back to overview" %} - - - - PDF - - - {% if perms.core.can_manage_projector %} - - - - {% endif %} - {% if perms.motions.can_manage or perms.agenda.can_manage or allowed_actions.edit %} -
- - {% trans 'More actions' %} - - -
- {% endif %} -
-

- -
-
- {# TODO: show only for workflow with versioning #} - {% with last_version=motion.get_last_version %} - {% if version.version_number != motion.active_version.version_number %} -

- {% trans "Go to the authorized version" %} - (# {{ motion.active_version.version_number }}) -

- {% elif version.version_number != last_version.version_number %} -

- {% trans "Go to the newest version" %} - (# {{ last_version.version_number }}) -

- {% endif %} - {% endwith %} - - -

{% trans "Motion text" %}:

- {{ text|safe }} -
- - -

{% trans "Reason" %}:

- {{ reason|safe|default:'–' }} -
- - - {% with attachments=motion.attachments.all %} - {% if attachments %} -

{% trans "Attachments" %}:

- {% for attachment in attachments %} -

{{ attachment }}

- {% endfor %} -
- {% endif %} - {% endwith %} - - - {% with versions=motion.versions.all %} - {% if versions|length > 1 %} - {% for version in versions %} - {% if forloop.first %} -

{% trans "Version history" %}:

-
- - - - - - - - - {% endif %} - - - - - - - - {% if forloop.last %} -
#{% trans "Time" %}{% trans "Actions" %}
- {% if version == motion.active_version %} - - {% else %} - {% if perms.motions.can_manage %} - - {% endif %} - {% endif %} - {{ version.version_number }}{{ version.creation_time }} - - - - - - - {% if perms.motions.can_manage and version != motion.active_version %} - - - - {% endif %} -
-
- {% endif %} - {% endfor %} - {% endif %} - {% endwith %} - - - {% if perms.motions.can_manage %} -

-

- -
-
-
    - {% for message in motion.log_messages.all %} -
  • {{ message }}
  • - {% endfor %} -
-
-

- {% endif %} -
- - -
-
- -
{% trans "Submitter" %}:
- {% for submitter in motion.submitters.all %} - {{ submitter }}{% if not forloop.last %}, {% endif %} - {% endfor %} - - - {% if 'motion_min_supporters'|get_config > 0 %} -
{% trans "Supporters" %}: *
- {% if not motion.supporters %} - - - {% else %} -
    - {% for supporter in motion.supporters.all %} -
  1. {{ supporter }}
  2. - {% endfor %} -
- {% endif %} - {% endif %} - - -
{% trans "Status" %}:
- {% trans motion.state.name %} -
- - -
{% trans "Vote result" %}:
- {% with motion.polls.all as polls %} - {% for poll in polls %} -

- {% if polls.count > 1 %} - {{ poll.poll_number|ordinal|safe }} {% trans "vote" %}: - {% endif %} - {% if perms.motions.can_manage %} - {% if polls.count > 1 %}
{% endif %} - - - - - - - - - {% endif %} - {% if poll.has_votes %} -

- {% with poll.get_options.0 as option %} - {{ option.Yes }}
- {{ option.No }}
- {{ option.Abstain }}
- {% if poll.votesvalid != None or poll.votesinvalid != None %} -
- {% if poll.votesvalid != None %} - {{ poll.print_votesvalid }}
- {% endif %} - {% if poll.votesinvalid != None %} - {{ poll.print_votesinvalid }}
- {% endif %} - {% endif %} - {% if poll.votescast != None %} -
- {{ poll.print_votescast }} - {% endif %} - {% endwith %} -
- {% else %} -

{% trans 'No result' %}

- {% endif %} - {% block meta_box_poll_extras %}{% endblock %} -

- {% empty %} - {% if not allowed_actions.create_poll %} - – - {% endif %} - {% endfor %} - {% if allowed_actions.create_poll %} -

- - - {% trans 'New vote' %} - -

- {% endif %} - {% endwith %} - - - {% if motion.category %} -
{% trans "Category" %}:
- {{ motion.category }} - {% endif %} - - - {% for tag in motion.tags.all %} - {% if forloop.first %} -
{% trans "Tags" %}:
- {% endif %} - {{ tag }} - {% endfor %} - - -
- {% if motion.versions.count > 1 %} - {% trans "Last changes (of this version)" %}: - {% else %} - {% trans "Last changes" %}: - {% endif %} -
- {{ version.creation_time }} - - {% if 'motion_amendments_enabled'|get_config %} -
{% trans 'Amendments' %}:
- {% with amendments=motion.amendments.all %} - {% if amendments %} - - {% endif %} - {% endwith %} - - - {% trans 'New amendment' %} - - {% endif %} - - - {% if perms.motions.can_support and 'motion_min_supporters'|get_config > 0 %} - {% if allowed_actions.unsupport %} -

- - {% trans 'Unsupport' %} - - {% endif %} - {% if allowed_actions.support %} -

- - {% trans 'Support' %} - - {% endif %} - {% endif %} - - - {% if 'motion_min_supporters'|get_config > 0 %} -

- * {% trans "minimum required supporters" %}: {{ 'motion_min_supporters'|get_config }} - {% endif %} -
- - {% if perms.motions.can_manage %} - -
-

{% trans "Manage motion" %}

- {% for state in motion.state.next_states.all %} - {% if forloop.first %} -
- {% endif %} - {% trans state.get_action_word %} - {% if forloop.last %} -
- {% endif %} - {% endfor %} -
{% trans "For administration only:" %}
- - {% trans 'Reset state' %} - -
- {% endif %} -
-
- -{% endblock %} diff --git a/openslides/motions/templates/motions/motion_diff.html b/openslides/motions/templates/motions/motion_diff.html deleted file mode 100644 index 5badd8c34..000000000 --- a/openslides/motions/templates/motions/motion_diff.html +++ /dev/null @@ -1,66 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} -{% load staticfiles %} - -{% block title %}{% trans "Motion" %} {{ motion.identifier }} – {{ block.super }}{% endblock %} - -{% block header %} - -{% endblock %} - - -{% block content %} -

- {{ motion.title }} -
- - {% if motion.identifier != None %} - {% trans "Motion" %} {{ motion.identifier }}, - {% else %} - [{% trans "no number" %}], - {% endif %} - {% trans "Diff view" %} - - - - -

- -{% if version_rev1 and version_rev2 %} - - - - - - - - {% if not version_rev1.text == version_rev2.text %} - - {% else %} - - - {% endif %} - - - - - {% if not version_rev1.reason == version_rev2.reason %} - - {% else %} - - - {% endif %} - -
{% trans "Version" %} {{ version_rev1.version_number }}
- {% trans "Last changes" %}: {{ version_rev1.creation_time }}
-

{{ version_rev1.title }}

-
{% trans "Version" %} {{ version_rev2.version_number }}
- {% trans "Last changes" %}: {{ version_rev1.creation_time }}
-

{{ version_rev2.title }}

-
{{ diff_text|safe }}{{ version_rev1.text }}{{ version_rev2.text }}

{% trans "Reason" %}:

{{ diff_reason|safe }}{{ version_rev1.reason }}{{ version_rev2.reason }}
-{% endif %} - -{% endblock %} diff --git a/openslides/motions/templates/motions/motion_form.html b/openslides/motions/templates/motions/motion_form.html deleted file mode 100644 index d6752225b..000000000 --- a/openslides/motions/templates/motions/motion_form.html +++ /dev/null @@ -1,51 +0,0 @@ -{% extends "base.html" %} - -{% load tags %} -{% load i18n %} -{% load staticfiles %} - -{% block header %} - {{ block.super }} - -{% endblock %} - - -{% block title %} - {% if motion %} - {% trans "Edit motion" %} - {% else %} - {% trans "New motion" %} - {% endif %} - – {{ block.super }} -{% endblock %} - -{% block content %} -

- {% if motion %} - {% trans "Edit motion" %} - {% else %} - {% trans "New motion" %} - {% endif %} - - {% if motion %} - - - {% trans "Back to motion" %} - {% else %} - - - {% trans "Back to overview" %} - {% endif %} - -

-
{% csrf_token %} - {% include "form.html" %} -

- {% include "formbuttons_saveapply.html" %} - - {% trans 'Cancel' %} - -

- * {% trans "required" %} -
-{% endblock %} diff --git a/openslides/motions/templates/motions/motion_form_csv_import.html b/openslides/motions/templates/motions/motion_form_csv_import.html deleted file mode 100644 index af5419345..000000000 --- a/openslides/motions/templates/motions/motion_form_csv_import.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} - -{% block title %}{% trans 'Import motions' %} – {{ block.super }}{% endblock %} - -{% block content %} -

- {% trans 'Import motions' %} - - {% trans 'Back to overview' %} - -

- -

{% trans 'Select a CSV file to import motions' %}.

- -

{% trans 'Please note' %}:

- - -
{% csrf_token %} - {% include 'form.html' %} -

- - - {% trans 'Cancel' %} - -

- * {% trans "required" %} -
-{% endblock %} diff --git a/openslides/motions/templates/motions/motion_list.html b/openslides/motions/templates/motions/motion_list.html deleted file mode 100644 index 4eb6e0060..000000000 --- a/openslides/motions/templates/motions/motion_list.html +++ /dev/null @@ -1,149 +0,0 @@ -{% extends "base.html" %} - -{% load tags %} -{% load i18n %} -{% load staticfiles %} - -{% block title %}{% trans "Motions" %} – {{ block.super }}{% endblock %} - -{% block javascript %} - - -{% endblock %} - -{% block content %} -

- {% trans "Motions" %} - - {% if perms.motions.can_create %} - {% if not 'motion_stop_submitting'|get_config or perms.motions.can_manage %} - - - {% trans 'New' %} - - {% endif %} - {% endif %} - {% if perms.core.can_manage_tags %} - - - {% trans 'Tags' %} - - {% endif %} - {% if perms.motions.can_manage %} - - - {% trans 'Categories' %} - - - - {% trans 'Import' %} - {% endif %} - - - PDF - -

- - - - - - - - - - {% if 'motion_min_supporters'|get_config > 0 %} - - {% endif %} - - {% if perms.motions.can_manage or perms.core.can_manage_projector %} - - {% endif %} - - - {% for motion in motion_list %} - - - - - - - {% if 'motion_min_supporters'|get_config > 0 %} - {% with supporters=motion.supporters.all|length %} - - {% endwith %} - {% endif %} - - {% if perms.motions.can_manage or perms.core.can_manage_projector %} - - {% endif %} - - {% endfor %} -
{% trans "#" %}{% trans "Motion title" %}{% trans 'Category' %}{% trans "Status" %}{% trans "Submitter" %}{% trans "Supporters" %}{% trans "Last changes" %}{% trans "Actions" %}
{{ motion.identifier|default:'' }} - {% if motion.is_amendment %} - - {{ 'motion_amendments_prefix'|get_config }} - - {% endif %} - {{ motion.title }} - {% for tag in motion.tags.all %} - {{ tag }} - {% endfor %} - - {{ motion.title }} - {% for tag in motion.tags.all %} - {{ tag }} - {% endfor %} - {% if motion.category %}{{ motion.category }}{% else %}–{% endif %}{% trans motion.state.name %} - {% for submitter in motion.submitters.all %} - {{ submitter.person }}{% if not forloop.last %}, {% endif %} - {% endfor %} - - {% if supporters >= 'motion_min_supporters'|get_config %} - {{ supporters }} - {% endif %} - {% if supporters < 'motion_min_supporters'|get_config %} - {{ supporters }} - {% endif %} - {{ motion.get_last_version.creation_time }} - {% if motion.get_last_version.version_number != motion.active_version.version_number %} - - - - {% endif %} - - {% if perms.core.can_manage_projector %} - - - - {% endif %} - {% if perms.motions.can_manage %} - - - - - - - {% endif %} - -
-{% endblock %} diff --git a/openslides/motions/templates/motions/motionpoll_form.html b/openslides/motions/templates/motions/motionpoll_form.html deleted file mode 100644 index 4a0e02c67..000000000 --- a/openslides/motions/templates/motions/motionpoll_form.html +++ /dev/null @@ -1,88 +0,0 @@ -{% extends 'base.html' %} - -{% load i18n %} -{% load tags %} - -{% block title %} - {% trans "Motion" %} {{ motion.identifier }}, {{ poll }} – {{ block.super }} -{% endblock %} - -{% block content %} -

- {{ motion }} -
- - {% trans "Motion" %} {{ motion.identifier }}, {{ poll }} - - - {% trans "Back to motion" %} - - {% if perms.core.can_manage_projector %} - - - - - {% trans "Vote result" %} - {% endif %} - {% if perms.motions.can_manage %} - - {% endif %} - -

- -

-{% trans "Special values" %}: -1 = {% trans 'majority' %} | -2 = {% trans 'undocumented' %} -

-
{% csrf_token %} - {{ pre_form }} - - - - - - - {% for value in forms.0 %} - - - - - {% endfor %} - - - - - - - - - - - - -
{% trans "Option" %}{% trans "Votes" %}
{{ value.label }}{{ value.errors }}{{ value }}
{% trans "Valid votes" %}{{ pollform.votesvalid.errors }}{{ pollform.votesvalid }}
{% trans "Invalid votes" %}{{ pollform.votesinvalid.errors }}{{ pollform.votesinvalid }}
{% trans "Votes cast" %}{{ pollform.votescast.errors }}{{ pollform.votescast }}
- - {{ post_form }} - - -

- - {% trans 'Ballot paper as PDF' %} - -

- -
- - - - {% trans 'Cancel' %} - -
-
-{% endblock %} diff --git a/openslides/motions/templates/motions/motionpoll_slide.html b/openslides/motions/templates/motions/motionpoll_slide.html deleted file mode 100644 index 6b68d9c81..000000000 --- a/openslides/motions/templates/motions/motionpoll_slide.html +++ /dev/null @@ -1,54 +0,0 @@ -{% load i18n %} -{% load humanize %} -{% load staticfiles %} - -

- {{ poll.motion.active_version.title }} -
- - {% trans "Motion" %} {{ poll.motion.identifier|default:'' }} - {% if poll.motion.get_active_version.version_number > 1 %} | {% trans 'Version' %} {{ poll.motion.active_version.version_number }}{% endif %} - {% if poll.poll_number > 1 %}| {{ poll.poll_number|ordinal|safe }} {% trans "vote" %}{% endif %} - -

- -

- {% if poll.has_votes %} - {% with poll.get_options.0 as option %} - - - - - - - - - - - - - - {% if poll.votesvalid != None %} - - - - - {% endif %} - {% if poll.votesinvalid != None %} - - - - - {% endif %} - {% if poll.votescast != None %} - - - - - {% endif %} -
{% trans 'Yes' %}:{{ option.Yes }}
{% trans 'No' %}:{{ option.No }}
{% trans 'Abstention' %}:{{ option.Abstain }}
{% trans 'Valid votes' %}:{{ poll.print_votesvalid }}
{% trans 'Invalid votes' %}:{{ poll.print_votesinvalid }}
{% trans 'Votes cast' %}:{{ poll.print_votescast }}
- {% endwith %} - {% else %} - {% trans "No result available." %} - {% endif %} -

diff --git a/openslides/motions/templates/motions/slide.html b/openslides/motions/templates/motions/slide.html deleted file mode 100644 index ea3f9fe94..000000000 --- a/openslides/motions/templates/motions/slide.html +++ /dev/null @@ -1,97 +0,0 @@ -{% load i18n %} -{% load humanize %} -{% load staticfiles %} - - - -

- {{ motion.active_version.title }} - - {% trans "Motion" %} {{ motion.identifier|default:'' }} - {% if motion.is_amendment %} - ({% trans "Amendment of" %} {{ motion.parent.identifier|default:motion.parent }}) - {% endif %} - {% if motion.get_active_version.version_number > 1 %} | {% trans 'Version' %} {{ motion.active_version.version_number }}{% endif %} - -

- -
- {{ motion.active_version.text|safe }} -
- -{% if motion.active_version.reason %} -
-
-

{% trans "Reason" %}:

- {{ motion.active_version.reason|safe }} -
-{% endif %} diff --git a/openslides/motions/templates/motions/widget_motion.html b/openslides/motions/templates/motions/widget_motion.html deleted file mode 100644 index e69de29bb..000000000 diff --git a/openslides/motions/urls.py b/openslides/motions/urls.py index 812c3957d..1fbd5a01c 100644 --- a/openslides/motions/urls.py +++ b/openslides/motions/urls.py @@ -4,7 +4,6 @@ from . import views urlpatterns = patterns( '', - url(r'^pdf/$', views.MotionPDFView.as_view(print_all_motions=True), name='motions_pdf'), diff --git a/openslides/motions/views.py b/openslides/motions/views.py index 915b92f96..e54481ebf 100644 --- a/openslides/motions/views.py +++ b/openslides/motions/views.py @@ -1,18 +1,27 @@ from django.http import Http404 +from django.shortcuts import get_object_or_404 from django.utils.text import slugify from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_noop -from django.shortcuts import get_object_or_404 from reportlab.platypus import SimpleDocTemplate from rest_framework import status from openslides.config.api import config -from openslides.utils.rest_api import ModelViewSet, Response, ValidationError, detail_route -from openslides.utils.views import (PDFView, SingleObjectMixin) +from openslides.utils.rest_api import ( + ModelViewSet, + Response, + ValidationError, + detail_route, +) +from openslides.utils.views import PDFView, SingleObjectMixin -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 .serializers import CategorySerializer, MotionSerializer, WorkflowSerializer +from .serializers import ( + CategorySerializer, + MotionSerializer, + WorkflowSerializer, +) class MotionViewSet(ModelViewSet): @@ -236,13 +245,13 @@ class MotionViewSet(ModelViewSet): return Response({'detail': message}) -class PollMixin(object): +class PollPDFView(PDFView): """ - Mixin for the PollUpdateView and the PollDeleteView. + Generates a ballotpaper. """ required_permission = 'motions.can_manage' - success_url_name = 'motion_detail' + top_space = 0 def get_object(self): """ @@ -261,21 +270,6 @@ class PollMixin(object): self._object = obj return obj - def get_url_name_args(self): - """ - Return the arguments to create the url to the success_url. - """ - return [self.get_object().motion.pk] - - -class PollPDFView(PollMixin, PDFView): - """ - Generates a ballotpaper. - """ - - required_permission = 'motions.can_manage' - top_space = 0 - def get_filename(self): """ Return the filename for the PDF. diff --git a/openslides/motions/widgets.py b/openslides/motions/widgets.py deleted file mode 100644 index bae2b6586..000000000 --- a/openslides/motions/widgets.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget - -from .models import Motion - - -class MotionWidget(Widget): - """ - Motion widget. - """ - name = 'motion' - verbose_name = ugettext_lazy('Motions') - required_permission = 'core.can_manage_projector' - default_column = 1 - default_weight = 40 - icon_css_class = 'icon-file' - template_name = 'motions/widget_motion.html' - more_link_pattern_name = 'motion_list' - - def get_context_data(self, **context): - return super(MotionWidget, self).get_context_data( - motions=Motion.objects.all(), - **context) diff --git a/openslides/poll/forms.py b/openslides/poll/forms.py deleted file mode 100644 index 031a1ef44..000000000 --- a/openslides/poll/forms.py +++ /dev/null @@ -1,21 +0,0 @@ -from django import forms - -from openslides.utils.forms import CssClassMixin - - -class OptionForm(CssClassMixin, forms.Form): - def __init__(self, *args, **kwargs): - extra = kwargs.pop('extra') - formid = kwargs.pop('formid') - kwargs['prefix'] = "option-%s" % formid - super(OptionForm, self).__init__(*args, **kwargs) - - for vote in extra: - key = vote.value - value = vote.get_value() - weight = vote.print_weight(raw=True) - self.fields[key] = forms.IntegerField( - label=value, - initial=weight, - min_value=-2, - required=False) diff --git a/openslides/poll/templates/poll/poll.html b/openslides/poll/templates/poll/poll.html deleted file mode 100644 index 129d8d859..000000000 --- a/openslides/poll/templates/poll/poll.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends 'base.html' %} - -{% block content %} -
{% csrf_token %} - - - - {% for value in poll.get_vote_values %} - - {% endfor %} - - {% for form in forms %} - - - {% for value in form %} - - {% endfor %} - - {% endfor %} -
Option{{ value }}
{{ form.option }} - {{ value.errors }} - {{ value }} -
- -

-
-{% endblock %} diff --git a/openslides/poll/views.py b/openslides/poll/views.py deleted file mode 100644 index 9ef1f4ea2..000000000 --- a/openslides/poll/views.py +++ /dev/null @@ -1,82 +0,0 @@ -from django.forms.models import modelform_factory -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404 - -from openslides.utils.views import TemplateView, FormMixin - - -class PollFormView(FormMixin, TemplateView): - poll_class = None - - def get(self, request, *args, **kwargs): - self.poll = self.get_object() - return super(PollFormView, self).get(request, *args, **kwargs) - - def post(self, request, *args, **kwargs): - self.poll = self.get_object() - option_forms = self.poll.get_vote_forms(data=self.request.POST) - - FormClass = self.get_modelform_class() - pollform = FormClass(data=self.request.POST, instance=self.poll, - prefix='pollform') - - error = False - for form in option_forms: - if not form.is_valid(): - error = True - - if not pollform.is_valid(): - error = True - - if error: - response = self.render_to_response(self.get_context_data( - forms=option_forms, - pollform=pollform)) - else: - for form in option_forms: - data = {} - for value in self.poll.get_vote_values(): - data[value] = form.cleaned_data[value] - self.poll.set_vote_objects_with_values(form.option, data) - - pollform.save() - response = HttpResponseRedirect(self.get_success_url()) - return response - - def get_poll_class(self): - if self.poll_class is not None: - return self.poll_class - else: - raise NotImplementedError( - 'No poll class defined. Either provide a poll_class or define ' - 'a get_poll_class method.') - - def get_object(self): - """ - Returns the poll object. Raises Http404 if the poll does not exist. - """ - try: - obj = self._object - except AttributeError: - queryset = self.get_poll_class().objects.filter(pk=self.kwargs['poll_id']) - obj = get_object_or_404(queryset) - self._object = obj - return obj - - def get_context_data(self, **kwargs): - context = super(PollFormView, self).get_context_data(**kwargs) - context['poll'] = self.poll - if 'forms' in kwargs: - context['forms'] = kwargs['forms'] - context['pollform'] = kwargs['pollform'] - else: - context['forms'] = self.poll.get_vote_forms() - FormClass = self.get_modelform_class() - context['pollform'] = FormClass(instance=self.poll, - prefix='pollform') - return context - - def get_modelform_class(self): - fields = [] - self.poll.append_pollform_fields(fields) - return modelform_factory(type(self.poll), fields=fields) diff --git a/openslides/projector/api.py b/openslides/projector/api.py deleted file mode 100644 index 5b98cf92b..000000000 --- a/openslides/projector/api.py +++ /dev/null @@ -1,210 +0,0 @@ -from json import dumps -from time import time - -from django.template.loader import render_to_string - -from openslides.config.api import config -from openslides.utils.exceptions import OpenSlidesError - -from .signals import projector_overlays - -slide_callback = {} -""" -A dictonary where the key is the name of a slide, and the value is a -callable object which returns the html code for a slide. -""" - -slide_model = {} -""" -A dictonary for SlideMixin models to reference from the slide_callback_name to -the Model -""" -# TODO: Find a bether way to do this. Maybe by reimplementing slides with -# metaclasses - - -class SlideError(OpenSlidesError): - pass - - -def call_on_projector(calls): - """ - Sends data to the projector. - - The argument call has to be a dictionary with the javascript function name - as key and the argument for it as value. - """ - # TODO: remove this function - projector_js_cache = config['projector_js_cache'] - projector_js_cache.update(calls) - config['projector_js_cache'] = projector_js_cache - - -def get_projector_content(slide_dict=None): - """ - Returns the HTML-Content block for the projector. - - Slide_dict has to be an dictonary with the key 'callback'. - - If slide_dict is None, use the active slide from the database. - """ - if slide_dict is None: - slide_dict = config['projector_active_slide'].copy() - callback = slide_dict.pop('callback', None) - - try: - slide_content = slide_callback[callback](**slide_dict) - except (KeyError, SlideError): - slide_content = default_slide() - return slide_content - - -def default_slide(): - """ - Returns the HTML Code for the default slide. - """ - return render_to_string('projector/default_slide.html') - - -def get_overlays(only_active=False): - """ - Returns all overlay objects. - - If only_active is True, returns only active overlays. - - The returned value is a dictonary with the name of the overlay as key, and - the overlay object as value. - """ - overlays = {} - for receiver, overlay in projector_overlays.send(sender='get_overlays'): - if not only_active or overlay.is_active(): - overlays[overlay.name] = overlay - return overlays - - -def get_projector_overlays_js(as_json=False): - """ - Returns JS-Code for the active overlays. - - The retuned value is a list of json objects. - """ - javascript = [] - for overlay in get_overlays().values(): - if overlay.is_active(): - overlay_js = overlay.get_javascript() - if overlay_js: - if as_json: - overlay_js = dumps(overlay_js) - javascript.append(overlay_js) - return javascript - - -def register_slide(name, callback, model=None): - """ - Registers a function as slide callback. - - The optional argument 'model' is used to register a SlideModelClass. - """ - slide_callback[name] = callback - if model is not None: - slide_model[name] = model - - -def register_slide_model(SlideModel, template): - """ - Shortcut for register_slide for a Model with the SlideMixin. - - The Argument 'SlideModel' has to be a Django-Model-Class, which is a subclass - of SlideMixin. Template has to be a string to the path of a template. - """ - - def model_slide(**kwargs): - """ - Return the html code for the model slide. - """ - slide_pk = kwargs.get('pk', None) - - try: - slide = SlideModel.objects.get(pk=slide_pk) - except SlideModel.DoesNotExist: - raise SlideError - else: - context = slide.get_slide_context() - - return render_to_string(template, context) - - register_slide(SlideModel.slide_callback_name, model_slide, SlideModel) - - -def set_active_slide(callback, **kwargs): - """ - Set the active Slide. - - callback: The name of the slide callback. - kwargs: Keyword arguments for the slide callback. - """ - kwargs.update(callback=callback) - config['projector_active_slide'] = kwargs - - -def get_active_slide(): - """ - Returns the dictonary, which defines the active slide. - """ - return config['projector_active_slide'] - - -def get_active_object(): - """ - Returns an object if the active slide is an instance of SlideMixin. - In other case, returns None - """ - active_slide_dict = get_active_slide() - callback_name = active_slide_dict.get('callback', None) - object_pk = active_slide_dict.get('pk', None) - try: - Model = slide_model[callback_name] - except KeyError: - value = None - else: - try: - value = Model.objects.get(pk=object_pk) - except Model.DoesNotExist: - value = None - return value - - -def start_countdown(): - """ - Starts the countdown - """ - # if we had stopped the countdown resume were we left of - if config['countdown_state'] == 'paused': - start_stamp = config['countdown_start_stamp'] - pause_stamp = config['countdown_pause_stamp'] - now = time() - config['countdown_start_stamp'] = now - \ - (pause_stamp - start_stamp) - else: - config['countdown_start_stamp'] = time() - - config['countdown_state'] = 'active' - config['countdown_pause_stamp'] = 0 - - -def stop_countdown(): - """ - Stops the countdown - """ - if config['countdown_state'] == 'active': - config['countdown_state'] = 'paused' - config['countdown_pause_stamp'] = time() - - -def reset_countdown(): - """ - Resets the countdown - """ - config['countdown_start_stamp'] = time() - config['countdown_pause_stamp'] = 0 - config['countdown_state'] = 'inactive' diff --git a/openslides/projector/apps.py b/openslides/projector/apps.py deleted file mode 100644 index 5c02769c1..000000000 --- a/openslides/projector/apps.py +++ /dev/null @@ -1,23 +0,0 @@ -from django.apps import AppConfig - - -class ProjectorAppConfig(AppConfig): - name = 'openslides.projector' - verbose_name = 'OpenSlides Projector' - - def ready(self): - # Load widgets. - # Do this by just importing all from this file. - from . import widgets # noqa - - # Import all required stuff. - from openslides.config.signals import config_signal - from .signals import ( - countdown, projector_clock, projector_overlays, - projector_overlay_message, setup_projector_config) - - # Connect signals. - config_signal.connect(setup_projector_config, dispatch_uid='setup_projector_config') - projector_overlays.connect(countdown, dispatch_uid="projector_countdown") - projector_overlays.connect(projector_overlay_message, dispatch_uid="projector_overlay_message") - projector_overlays.connect(projector_clock, dispatch_uid="projector_clock") diff --git a/openslides/projector/exceptions.py b/openslides/projector/exceptions.py deleted file mode 100644 index 4ce9d1caf..000000000 --- a/openslides/projector/exceptions.py +++ /dev/null @@ -1,2 +0,0 @@ -class ProjectorExceptionWarning(RuntimeWarning): - pass diff --git a/openslides/projector/models.py b/openslides/projector/models.py index 9aa1b2b1d..1066f7f8c 100644 --- a/openslides/projector/models.py +++ b/openslides/projector/models.py @@ -1,48 +1,8 @@ -from django.core.urlresolvers import reverse - -from openslides.utils.utils import int_or_none - - class SlideMixin(object): """ - A Mixin for a Django-Model, for making the model a slide. + Deprecated. + + Will be reused or removed when more slides are implemented for the + OpenSlides 2.0 projector API """ - - slide_callback_name = None - """ - Name of the callback to render the model as slide. - """ - - def get_absolute_url(self, link='projector'): - """ - Return the url to activate the slide, if link == 'projector'. - """ - if link in ('projector', 'projector_preview'): - url_name = {'projector': 'projector_activate_slide', - 'projector_preview': 'projector_preview'}[link] - value = '%s?pk=%d' % ( - reverse(url_name, - args=[self.slide_callback_name]), - self.pk) - else: - value = super(SlideMixin, self).get_absolute_url(link) - return value - - def is_active_slide(self): - """ - Return True, if the the slide is the active slide. - """ - from openslides.projector.api import get_active_slide - active_slide = get_active_slide() - slide_pk = int_or_none(active_slide.get('pk', None)) - return (active_slide['callback'] == self.slide_callback_name and - self.pk == slide_pk) - - def get_slide_context(self, **context): - """ - Returns the context for the template which renders the slide. - """ - slide_name = self._meta.object_name.lower() - context.update({'slide': self, - slide_name: self}) - return context + pass diff --git a/openslides/projector/projector.py b/openslides/projector/projector.py deleted file mode 100644 index 0d14b29de..000000000 --- a/openslides/projector/projector.py +++ /dev/null @@ -1,93 +0,0 @@ -import warnings - -from openslides.config.api import config - -from .exceptions import ProjectorExceptionWarning - - -class Overlay(object): - """ - Represents an overlay which can be seen on the projector. - """ - - def __init__(self, name, get_widget_html, get_projector_html, - get_javascript=None, allways_active=False): - self.name = name - self.widget_html_callback = get_widget_html - self.projector_html_callback = get_projector_html - self.javascript_callback = get_javascript - self.allways_active = allways_active - - def __repr__(self): - return self.name - - def get_widget_html(self): - """ - Returns the html code for the overlay widget. - - Can return None, if the widget does not want to be in the widget. - """ - value = None - if self.widget_html_callback is not None: - value = self.widget_html_callback() - return value - - def get_projector_html(self): - """ - Returns the html code for the projector. - """ - try: - value = self.get_html_wrapper(self.projector_html_callback()) - except Exception as exception: - warnings.warn('%s in overlay "%s": %s' - % (type(exception).__name__, self, exception), - ProjectorExceptionWarning) - value = '' - return value - - def get_javascript(self): - """ - Returns the java-script code for the projector. - """ - if self.javascript_callback is None: - value = {} - else: - value = self.javascript_callback() - return value - - def get_html_wrapper(self, inner_html): - """ - Returns the inner_html wrapped in a div. - - The html-id of the div is "overlay_OVERLAYNAME" - """ - full_html = '' - if inner_html is not None: - full_html = '
%s
' % (self.name, inner_html) - return full_html - - def is_active(self): - """ - Returns True if the overlay is activated. False in other case. - """ - return self.allways_active or self.name in config['projector_active_overlays'] - - def set_active(self, active): - """ - Publish or depublish the overlay on the projector. - - publish, if active is true, - depublish, if active is false. - """ - active_overlays = set(config['projector_active_overlays']) - if active: - active_overlays.add(self.name) - else: - active_overlays.discard(self.name) - config['projector_active_overlays'] = list(active_overlays) - - def show_on_projector(self): - """ - Retruns True if the overlay should be shoun on the projector. - """ - return self.is_active() and self.get_projector_html() is not None diff --git a/openslides/projector/signals.py b/openslides/projector/signals.py deleted file mode 100644 index b75710e29..000000000 --- a/openslides/projector/signals.py +++ /dev/null @@ -1,167 +0,0 @@ -from time import time - -from django.contrib.staticfiles.templatetags.staticfiles import static -from django.core.context_processors import csrf -from django.dispatch import Signal -from django.template.loader import render_to_string -from django.utils.datastructures import SortedDict - -from openslides.config.api import config, ConfigCollection, ConfigVariable - -from .projector import Overlay - -projector_overlays = Signal(providing_args=['request']) - - -def setup_projector_config(sender, **kwargs): - """ - Receiver function to setup all projector config variables. They are not - shown on a config view. The function is connected to the signal - openslides.config.signals.config_signal during app loading. - """ - # The active slide. The config-value is a dictonary with at least the entry - # 'callback'. - projector = ConfigVariable( - name='projector_active_slide', - default_value={'callback': None}) - - projector_message = ConfigVariable( - name='projector_message', - default_value='') - - countdown_time = ConfigVariable( - name='countdown_time', - default_value=60) - - countdown_start_stamp = ConfigVariable( - name='countdown_start_stamp', - default_value=0) - - countdown_pause_stamp = ConfigVariable( - name='countdown_pause_stamp', - default_value=0) - - countdown_state = ConfigVariable( - name='countdown_state', - default_value='inactive') - - projector_scale = ConfigVariable( - name='projector_scale', - default_value=0) - - projector_scroll = ConfigVariable( - name='projector_scroll', - default_value=0) - - projector_js_cache = ConfigVariable( - name='projector_js_cache', - default_value={}) - - projector_active_overlays = ConfigVariable( - name='projector_active_overlays', - default_value=[]) - - projector_pdf_fullscreen = ConfigVariable( - name='pdf_fullscreen', - default_value=False) - - return ConfigCollection( - variables=( - projector, projector_message, - countdown_time, countdown_start_stamp, countdown_pause_stamp, - countdown_state, projector_scale, projector_scroll, - projector_active_overlays, projector_js_cache, - projector_pdf_fullscreen)) - - -def countdown(sender, **kwargs): - """ - Receiver function for the projector countdown. The function is - connected to the signal projector_overlays during app loading. - """ - name = 'projector_countdown' - request = kwargs.get('request', None) - - def get_widget_html(): - """ - Returns the the html-code to show in the overly-widget. - """ - context = { - 'countdown_time': config['countdown_time'], - 'countdown_state': config['countdown_state']} - context.update(csrf(request)) - return render_to_string('projector/overlay_countdown_widget.html', - context) - - def get_projector_js(): - """ - Returns JavaScript for the projector - """ - value = SortedDict() - value['load_file'] = static('js/countdown.js') - value['projector_countdown_start'] = int(config['countdown_start_stamp']) - value['projector_countdown_duration'] = int(config['countdown_time']) - value['projector_countdown_pause'] = int(config['countdown_pause_stamp']) - value['projector_countdown_state'] = config['countdown_state'] - value['call'] = 'update_countdown();' - return value - - def get_projector_html(): - """ - Returns an html-code to show on the projector. - """ - return render_to_string('projector/overlay_countdown_projector.html') - - return Overlay(name, get_widget_html, get_projector_html, get_projector_js) - - -def projector_overlay_message(sender, **kwargs): - """ - Receiver function to show the overlay_message on the projector or the - form in the overlay-widget on the dashboard. The function is connected - to the signal projector_overlays during app loading. - """ - name = 'projector_message' - request = kwargs.get('request', None) - - def get_widget_html(): - """ - Returns the the html-code to show in the overly-widget. - """ - return render_to_string('projector/overlay_message_widget.html', csrf(request)) - - def get_projector_html(): - """ - Returns an html-code to show on the projector. - """ - if config['projector_message']: - return render_to_string('projector/overlay_message_projector.html', - {'message': config['projector_message']}) - return None - - return Overlay(name, get_widget_html, get_projector_html) - - -def projector_clock(sender, **kwargs): - """ - Receiver function to show the clock on the projector. The function is - connected to the signal projector_overlays during app loading. - """ - name = 'projector_clock' - - def get_projector_html(): - """ - Returns the html-code for the clock. - """ - return render_to_string('projector/overlay_clock_projector.html') - - def get_projector_js(): - """ - Returns JavaScript for the projector - """ - javascript = 'projector.set_server_time(%d);update_clock();' % int(time()) - return {'load_file': static('js/clock.js'), - 'call': javascript} - - return Overlay(name, None, get_projector_html, get_projector_js, - allways_active=True) diff --git a/openslides/projector/static/css/projector.css b/openslides/projector/static/css/projector.css deleted file mode 100644 index e1a29d990..000000000 --- a/openslides/projector/static/css/projector.css +++ /dev/null @@ -1,202 +0,0 @@ -/* - * OpenSlides default projector styles - * - */ - -body{ - font-size: 20px !important; - line-height: 24px !important; - overflow: hidden; -} - -/*** HEADER ***/ -#header { - box-shadow: 0 0 7px rgba(0,0,0,0.6); - height: 70px; - margin-bottom: 20px; -} -#logo { - position: relative; - left: 75px; - top: 4px; - height: 60px; - margin-right: 35px; - float: left; -} -#eventdata { - position: relative; - left: 75px; - top: 12px; - height: 50px; - overflow: hidden; -} -#eventdata .title { - font-size: 26px; - font-weight: bold; - } -#eventdata .title.titleonly { - position: relative; - top: 12px; -} -#eventdata .description { - font-size: 18px; - opacity: 0.5; -} -#currentTime { - border:0 solid #000000; - font-size:24px; - position:absolute; - text-align:right; - top:110px; - right:40px; - padding-left:30px; - background: url(../img/glyphicons_054_clock_big.png) no-repeat scroll 0px 0px; -} - -/*** CONTENT with base style elements ***/ -#content { - position: absolute; - left: 75px; - top: 150px; - right: 40px; - z-index: -1; - line-height: normal; - transition-property: margin, font-size; - transition-duration: 1s; -} -h1 { - font-size: 2.25em; - margin-bottom: 40px; - line-height: 1.1em; - padding-bottom: 10px; - border-bottom: 1px solid #e6e6e6; -} -h1.title_only { - text-align: center; - font-size: 2.75em; - line-height: 1.3em; - border: 0px; - padding: 40px; - margin-left: -35px; /* see #content position 'left' - 'right' for real centering */ -} -h1 small { - font-size: 0.55em; - margin-top: 15px; - display: block; -} -h3 { - font-size: 1.2em; -} -#sidebar { - width: 255px; - float: right; - margin: 0 0 20px 10px; -} -ul, ol { - margin: 0 0 10px 2em; -} -li { - line-height: normal; -} -.well h4 { - margin: 20px 0 2px 0; -} -.well h4.first { - margin-top: 0; -} -.well .result { - line-height: 30px; -} -.well .result hr { - border-top: 1px solid; - margin: 5px 0; - width: 10em; -} -.result.big { - font-size: 120%; - line-height: 40px; -} -.result .bold { - font-weight: bold; -} -hr { - margin: 10px 0; -} -.nobr { - white-space: nowrap; -} - - -/*** Overlay ***/ -#overlay_transparent { - background-color: #777777; - opacity: 0.6; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} -#overlay_countdown_inner { - position: fixed; - right: 40px; - height: 30px; - width: 215px; - margin: 0; - top: 0; - font-size: 4em; - font-weight: bold; - text-align: center; - border-radius: 0 0 0.2em 0.2em; -} -#overlay_countdown_inner.negative { - color: #CC0000; -} -#overlay_message_inner { - position: fixed; - top: 35%; - left: 10%; - width: 80%; - text-align: center; - border-radius: 0.5em; - background: #FFFFFF; - font-size: 2.75em; - padding: 0.2em 0; - line-height: normal !important; -} - - -/*** Table style ***/ -table { - border-collapse:collapse; - border-color:#CCCCCC -moz-use-text-color #CCCCCC #CCCCCC; - border-style:solid none solid solid; - border-width:1px medium 1px 1px; - margin:0; - border-spacing:0px; -} -table th { - border-right:1px solid #CCCCCC; - color:#333333; - font-weight:normal; - padding:10px 10px 10px 10px; - text-align:left; - text-transform:uppercase; -} -table tr.odd td { - background:none repeat scroll 0 0 #F1F1F1; -} -table td { - background:none repeat scroll 0 0 #F7F7F7; - border-right:1px solid #CCCCCC; - line-height:120%; - padding: 10px 10px; - vertical-align:middle; -} -tr.total td { - border-top: 1px solid #333333; - background-color: #e3e3e3; -} -tr.elected td { - background-color: #BED4DE !important; -} diff --git a/openslides/projector/static/img/glyphicons_054_clock_big.png b/openslides/projector/static/img/glyphicons_054_clock_big.png deleted file mode 100644 index 46a86c1a9..000000000 Binary files a/openslides/projector/static/img/glyphicons_054_clock_big.png and /dev/null differ diff --git a/openslides/projector/static/img/logo-projector.png b/openslides/projector/static/img/logo-projector.png deleted file mode 100644 index 9b81eb762..000000000 Binary files a/openslides/projector/static/img/logo-projector.png and /dev/null differ diff --git a/openslides/projector/static/js/clock.js b/openslides/projector/static/js/clock.js deleted file mode 100644 index b0471a634..000000000 --- a/openslides/projector/static/js/clock.js +++ /dev/null @@ -1,22 +0,0 @@ -/* - * JavaScript functions for clock. - * - */ - -function update_clock() { - var currentTime = projector.get_server_time() - var currentHours = currentTime.getHours(); - var currentMinutes = currentTime.getMinutes(); - currentHours = normalise(currentHours); - currentMinutes = normalise(currentMinutes); - $('#currentTime').html(currentHours + ':' + currentMinutes); -} - -setInterval('update_clock()', 200); - -function normalise(i) { - if (i < 10) { - i = "0" + i; - } - return i; -} diff --git a/openslides/projector/static/js/countdown.js b/openslides/projector/static/js/countdown.js deleted file mode 100644 index 06d6eb72e..000000000 --- a/openslides/projector/static/js/countdown.js +++ /dev/null @@ -1,50 +0,0 @@ -/* - * JavaScript functions for countdown. - * - */ - - -function update_countdown() { - var time = projector.get_server_time().getTime() / 1000; - var totalseconds; - var min; - var sec; - var negative; - var start = projector.projector_countdown_start; - var duration = projector.projector_countdown_duration; - var pause = projector.projector_countdown_pause; - - switch (projector.projector_countdown_state) { - case 'active': - totalseconds = start + duration - time; - break; - case 'paused': - totalseconds = start + duration - pause; - break; - case 'inactive': - totalseconds = duration; - break; - } - totalseconds = Math.floor(totalseconds); - if (totalseconds < 0 ) { - totalseconds = -totalseconds; - negative = true; - } - min = Math.floor(totalseconds / 60); - sec = Math.floor(totalseconds - (min * 60)); - if (sec < 10) { - sec = "0" + sec; - } - if (negative) { - min = "-" + min; - $('#overlay_countdown_inner').addClass('negative'); - } - else { - $('#overlay_countdown_inner').removeClass('negative'); - } - if(totalseconds !== undefined) { - $('#overlay_countdown_inner').html(min + ":" + sec); - - } -} -setInterval('update_countdown()', 200); diff --git a/openslides/projector/static/js/projector.js b/openslides/projector/static/js/projector.js deleted file mode 100644 index 4ad097062..000000000 --- a/openslides/projector/static/js/projector.js +++ /dev/null @@ -1,99 +0,0 @@ -/* - * JavaScript functions for projector. - * - */ - -$(document).ready(function() { - if ($('#content.reload').length > 0) { - updater.start(); - } -}); - -var projector = { - _loaded_files: {}, - - load_file: function(src) { - if (projector._loaded_files[src] === undefined) { - projector._loaded_files[src] = document.createElement('script'); - projector._loaded_files[src].setAttribute("type","text/javascript"); - projector._loaded_files[src].setAttribute("src", src); - $('head').append(projector._loaded_files[src]); - } - }, - - scroll: function(value) { - $('#content').css('margin-top', -10 * value + 'em'); - }, - - scale: function(value) { - $('#content').css('font-size', 100 + 20 * value + '%'); - }, - - get_server_time: function () { - var date = new Date(); - date.setTime(date.getTime() - projector.server_time_offset); - return date; - }, - - set_server_time: function(value) { - var local_time = Date.parse(new Date().toUTCString()); - projector.server_time_offset = local_time - value * 1000; - }, - - update_data: function(data) { - $.each(data, function (key, value) { - if (key === 'load_file') - projector.load_file(value); - else if (key === 'call') { - try { - eval(value); - } catch (e) {} - } else - projector[key] = value; - }); - } -}; - -var updater = { - socket: null, - - start: function() { - var url = "http://" + location.host + "/projector/socket"; - updater.socket = new SockJS(url); - updater.socket.onmessage = function(event) { - updater.updateProjector(event.data); - } - updater.socket.onclose = function() { - setTimeout('updater.start()', 5000); - } - }, - - updateProjector: function(data) { - if (data.content) { - $('#content').removeClass('fullscreen'); - $('#footer').removeClass('black'); - $('body').removeClass('black'); - $('#content').html(data.content); - } - if (data.overlays) { - $.each(data.overlays, function (key, value) { - var overlay = $('#overlays #overlay_' + key) - if (!value) - overlay.remove(); - else { - if (overlay.length) { - overlay.html(value.html) - } else { - $('#overlays').append(value.html); - } - projector.update_data(value.javascript); - } - }); - } - if (data.calls) { - $.each(data.calls, function (call, argument) { - projector[call](argument); - }); - } - } -}; diff --git a/openslides/projector/static/js/sockjs-0.3.min.js b/openslides/projector/static/js/sockjs-0.3.min.js deleted file mode 100644 index baae37641..000000000 --- a/openslides/projector/static/js/sockjs-0.3.min.js +++ /dev/null @@ -1,27 +0,0 @@ -/* SockJS client, version 0.3.4, http://sockjs.org, MIT License - -Copyright (c) 2011-2012 VMware, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. -*/ - -// JSON2 by Douglas Crockford (minified). -var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c1?this._listeners[a]=d.slice(0,e).concat(d.slice(e+1)):delete this._listeners[a];return}return},d.prototype.dispatchEvent=function(a){var b=a.type,c=Array.prototype.slice.call(arguments,0);this["on"+b]&&this["on"+b].apply(this,c);if(this._listeners&&b in this._listeners)for(var d=0;d=3e3&&a<=4999},c.countRTO=function(a){var b;return a>100?b=3*a:b=a+200,b},c.log=function(){b.console&&console.log&&console.log.apply&&console.log.apply(console,arguments)},c.bind=function(a,b){return a.bind?a.bind(b):function(){return a.apply(b,arguments)}},c.flatUrl=function(a){return a.indexOf("?")===-1&&a.indexOf("#")===-1},c.amendUrl=function(b){var d=a.location;if(!b)throw new Error("Wrong url for SockJS");if(!c.flatUrl(b))throw new Error("Only basic urls are supported in SockJS");return b.indexOf("//")===0&&(b=d.protocol+b),b.indexOf("/")===0&&(b=d.protocol+"//"+d.host+b),b=b.replace(/[/]+$/,""),b},c.arrIndexOf=function(a,b){for(var c=0;c=0},c.delay=function(a,b){return typeof a=="function"&&(b=a,a=0),setTimeout(b,a)};var i=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,j={"\0":"\\u0000","\x01":"\\u0001","\x02":"\\u0002","\x03":"\\u0003","\x04":"\\u0004","\x05":"\\u0005","\x06":"\\u0006","\x07":"\\u0007","\b":"\\b","\t":"\\t","\n":"\\n","\x0b":"\\u000b","\f":"\\f","\r":"\\r","\x0e":"\\u000e","\x0f":"\\u000f","\x10":"\\u0010","\x11":"\\u0011","\x12":"\\u0012","\x13":"\\u0013","\x14":"\\u0014","\x15":"\\u0015","\x16":"\\u0016","\x17":"\\u0017","\x18":"\\u0018","\x19":"\\u0019","\x1a":"\\u001a","\x1b":"\\u001b","\x1c":"\\u001c","\x1d":"\\u001d","\x1e":"\\u001e","\x1f":"\\u001f",'"':'\\"',"\\":"\\\\","\x7f":"\\u007f","\x80":"\\u0080","\x81":"\\u0081","\x82":"\\u0082","\x83":"\\u0083","\x84":"\\u0084","\x85":"\\u0085","\x86":"\\u0086","\x87":"\\u0087","\x88":"\\u0088","\x89":"\\u0089","\x8a":"\\u008a","\x8b":"\\u008b","\x8c":"\\u008c","\x8d":"\\u008d","\x8e":"\\u008e","\x8f":"\\u008f","\x90":"\\u0090","\x91":"\\u0091","\x92":"\\u0092","\x93":"\\u0093","\x94":"\\u0094","\x95":"\\u0095","\x96":"\\u0096","\x97":"\\u0097","\x98":"\\u0098","\x99":"\\u0099","\x9a":"\\u009a","\x9b":"\\u009b","\x9c":"\\u009c","\x9d":"\\u009d","\x9e":"\\u009e","\x9f":"\\u009f","\xad":"\\u00ad","\u0600":"\\u0600","\u0601":"\\u0601","\u0602":"\\u0602","\u0603":"\\u0603","\u0604":"\\u0604","\u070f":"\\u070f","\u17b4":"\\u17b4","\u17b5":"\\u17b5","\u200c":"\\u200c","\u200d":"\\u200d","\u200e":"\\u200e","\u200f":"\\u200f","\u2028":"\\u2028","\u2029":"\\u2029","\u202a":"\\u202a","\u202b":"\\u202b","\u202c":"\\u202c","\u202d":"\\u202d","\u202e":"\\u202e","\u202f":"\\u202f","\u2060":"\\u2060","\u2061":"\\u2061","\u2062":"\\u2062","\u2063":"\\u2063","\u2064":"\\u2064","\u2065":"\\u2065","\u2066":"\\u2066","\u2067":"\\u2067","\u2068":"\\u2068","\u2069":"\\u2069","\u206a":"\\u206a","\u206b":"\\u206b","\u206c":"\\u206c","\u206d":"\\u206d","\u206e":"\\u206e","\u206f":"\\u206f","\ufeff":"\\ufeff","\ufff0":"\\ufff0","\ufff1":"\\ufff1","\ufff2":"\\ufff2","\ufff3":"\\ufff3","\ufff4":"\\ufff4","\ufff5":"\\ufff5","\ufff6":"\\ufff6","\ufff7":"\\ufff7","\ufff8":"\\ufff8","\ufff9":"\\ufff9","\ufffa":"\\ufffa","\ufffb":"\\ufffb","\ufffc":"\\ufffc","\ufffd":"\\ufffd","\ufffe":"\\ufffe","\uffff":"\\uffff"},k=/[\x00-\x1f\ud800-\udfff\ufffe\uffff\u0300-\u0333\u033d-\u0346\u034a-\u034c\u0350-\u0352\u0357-\u0358\u035c-\u0362\u0374\u037e\u0387\u0591-\u05af\u05c4\u0610-\u0617\u0653-\u0654\u0657-\u065b\u065d-\u065e\u06df-\u06e2\u06eb-\u06ec\u0730\u0732-\u0733\u0735-\u0736\u073a\u073d\u073f-\u0741\u0743\u0745\u0747\u07eb-\u07f1\u0951\u0958-\u095f\u09dc-\u09dd\u09df\u0a33\u0a36\u0a59-\u0a5b\u0a5e\u0b5c-\u0b5d\u0e38-\u0e39\u0f43\u0f4d\u0f52\u0f57\u0f5c\u0f69\u0f72-\u0f76\u0f78\u0f80-\u0f83\u0f93\u0f9d\u0fa2\u0fa7\u0fac\u0fb9\u1939-\u193a\u1a17\u1b6b\u1cda-\u1cdb\u1dc0-\u1dcf\u1dfc\u1dfe\u1f71\u1f73\u1f75\u1f77\u1f79\u1f7b\u1f7d\u1fbb\u1fbe\u1fc9\u1fcb\u1fd3\u1fdb\u1fe3\u1feb\u1fee-\u1fef\u1ff9\u1ffb\u1ffd\u2000-\u2001\u20d0-\u20d1\u20d4-\u20d7\u20e7-\u20e9\u2126\u212a-\u212b\u2329-\u232a\u2adc\u302b-\u302c\uaab2-\uaab3\uf900-\ufa0d\ufa10\ufa12\ufa15-\ufa1e\ufa20\ufa22\ufa25-\ufa26\ufa2a-\ufa2d\ufa30-\ufa6d\ufa70-\ufad9\ufb1d\ufb1f\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufb4e\ufff0-\uffff]/g,l,m=JSON&&JSON.stringify||function(a){return i.lastIndex=0,i.test(a)&&(a=a.replace(i,function(a){return j[a]})),'"'+a+'"'},n=function(a){var b,c={},d=[];for(b=0;b<65536;b++)d.push(String.fromCharCode(b));return a.lastIndex=0,d.join("").replace(a,function(a){return c[a]="\\u"+("0000"+a.charCodeAt(0).toString(16)).slice(-4),""}),a.lastIndex=0,c};c.quote=function(a){var b=m(a);return k.lastIndex=0,k.test(b)?(l||(l=n(k)),b.replace(k,function(a){return l[a]})):b};var o=["websocket","xdr-streaming","xhr-streaming","iframe-eventsource","iframe-htmlfile","xdr-polling","xhr-polling","iframe-xhr-polling","jsonp-polling"];c.probeProtocols=function(){var a={};for(var b=0;b0&&h(a)};return c.websocket!==!1&&h(["websocket"]),d["xhr-streaming"]&&!c.null_origin?e.push("xhr-streaming"):d["xdr-streaming"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-streaming"):h(["iframe-eventsource","iframe-htmlfile"]),d["xhr-polling"]&&!c.null_origin?e.push("xhr-polling"):d["xdr-polling"]&&!c.cookie_needed&&!c.null_origin?e.push("xdr-polling"):h(["iframe-xhr-polling","jsonp-polling"]),e};var p="_sockjs_global";c.createHook=function(){var a="a"+c.random_string(8);if(!(p in b)){var d={};b[p]=function(a){return a in d||(d[a]={id:a,del:function(){delete d[a]}}),d[a]}}return b[p](a)},c.attachMessage=function(a){c.attachEvent("message",a)},c.attachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.addEventListener(c,d,!1):(a.attachEvent("on"+c,d),b.attachEvent("on"+c,d))},c.detachMessage=function(a){c.detachEvent("message",a)},c.detachEvent=function(c,d){typeof b.addEventListener!="undefined"?b.removeEventListener(c,d,!1):(a.detachEvent("on"+c,d),b.detachEvent("on"+c,d))};var q={},r=!1,s=function(){for(var a in q)q[a](),delete q[a]},t=function(){if(r)return;r=!0,s()};c.attachEvent("unload",t),c.unload_add=function(a){var b=c.random_string(8);return q[b]=a,r&&c.delay(s),b},c.unload_del=function(a){a in q&&delete q[a]},c.createIframe=function(b,d){var e=a.createElement("iframe"),f,g,h=function(){clearTimeout(f);try{e.onload=null}catch(a){}e.onerror=null},i=function(){e&&(h(),setTimeout(function(){e&&e.parentNode.removeChild(e),e=null},0),c.unload_del(g))},j=function(a){e&&(i(),d(a))},k=function(a,b){try{e&&e.contentWindow&&e.contentWindow.postMessage(a,b)}catch(c){}};return e.src=b,e.style.display="none",e.style.position="absolute",e.onerror=function(){j("onerror")},e.onload=function(){clearTimeout(f),f=setTimeout(function(){j("onload timeout")},2e3)},a.body.appendChild(e),f=setTimeout(function(){j("timeout")},15e3),g=c.unload_add(i),{post:k,cleanup:i,loaded:h}},c.createHtmlfile=function(a,d){var e=new ActiveXObject("htmlfile"),f,g,i,j=function(){clearTimeout(f)},k=function(){e&&(j(),c.unload_del(g),i.parentNode.removeChild(i),i=e=null,CollectGarbage())},l=function(a){e&&(k(),d(a))},m=function(a,b){try{i&&i.contentWindow&&i.contentWindow.postMessage(a,b)}catch(c){}};e.open(),e.write(' - - - - - - - - -
- {% for overlay in overlays.values %} - {{ overlay.get_projector_html|safe }} - {% endfor %} -
- -
- {{ content }} -
- - diff --git a/openslides/projector/templates/projector/default_slide.html b/openslides/projector/templates/projector/default_slide.html deleted file mode 100644 index 1c15da27d..000000000 --- a/openslides/projector/templates/projector/default_slide.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load tags %} - -

- {{ 'welcome_title'|get_config }} -

diff --git a/openslides/projector/templates/projector/overlay_clock_projector.html b/openslides/projector/templates/projector/overlay_clock_projector.html deleted file mode 100644 index f25ab7182..000000000 --- a/openslides/projector/templates/projector/overlay_clock_projector.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/openslides/projector/templates/projector/overlay_countdown_projector.html b/openslides/projector/templates/projector/overlay_countdown_projector.html deleted file mode 100644 index 770286808..000000000 --- a/openslides/projector/templates/projector/overlay_countdown_projector.html +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/openslides/projector/templates/projector/overlay_countdown_widget.html b/openslides/projector/templates/projector/overlay_countdown_widget.html deleted file mode 100644 index 869d68755..000000000 --- a/openslides/projector/templates/projector/overlay_countdown_widget.html +++ /dev/null @@ -1,38 +0,0 @@ -{% load i18n %} - - - {% trans "Countdown for speaking time" %}: -
-
- - {% trans "s" context "seconds" %} - -
- - - - - - - - - - - - - -
-
-
diff --git a/openslides/projector/templates/projector/overlay_message_projector.html b/openslides/projector/templates/projector/overlay_message_projector.html deleted file mode 100644 index 4a2da609d..000000000 --- a/openslides/projector/templates/projector/overlay_message_projector.html +++ /dev/null @@ -1,5 +0,0 @@ - - -
- {{ message|safe }} -
diff --git a/openslides/projector/templates/projector/overlay_message_widget.html b/openslides/projector/templates/projector/overlay_message_widget.html deleted file mode 100644 index ce658a77d..000000000 --- a/openslides/projector/templates/projector/overlay_message_widget.html +++ /dev/null @@ -1,19 +0,0 @@ -{% load i18n %} -{% load tags %} - - - {% trans "Message" %}: -
{% csrf_token %} -
- - - -
-
-
diff --git a/openslides/projector/templates/projector/welcome_widget.html b/openslides/projector/templates/projector/welcome_widget.html deleted file mode 100644 index 95917be00..000000000 --- a/openslides/projector/templates/projector/welcome_widget.html +++ /dev/null @@ -1,5 +0,0 @@ -{% load i18n %} - -{% if welcometext %} -

{{ welcometext|safe|linebreaks }}

-{% endif %} diff --git a/openslides/projector/templates/projector/widget_live_view.html b/openslides/projector/templates/projector/widget_live_view.html deleted file mode 100644 index 6692f5dc1..000000000 --- a/openslides/projector/templates/projector/widget_live_view.html +++ /dev/null @@ -1,47 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} - - - -
- -
-
-
- - -{% if perms.core.can_manage_projector %} -

- - - - - - - - - - {% trans "Zoom level" %}: - {{ 'projector_scale'|get_config }} -

-

- - - - - - - - - - {% trans "Scroll level" %}: - {{ 'projector_scroll'|get_config }} -

-{% endif %} - -{% endblock %} diff --git a/openslides/projector/templates/projector/widget_overlay.html b/openslides/projector/templates/projector/widget_overlay.html deleted file mode 100644 index 55981c73e..000000000 --- a/openslides/projector/templates/projector/widget_overlay.html +++ /dev/null @@ -1,25 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} - -{% block content %} -
    - {% for overlay in overlays %} -
  • - - - - - - - {{ overlay.get_widget_html|safe }} -
  • - {% endfor %} -
-{% endblock %} diff --git a/openslides/projector/urls.py b/openslides/projector/urls.py deleted file mode 100644 index ec37c0937..000000000 --- a/openslides/projector/urls.py +++ /dev/null @@ -1,82 +0,0 @@ -from django.conf.urls import patterns, url - -from . import views - -urlpatterns = patterns( - '', - url(r'^$', - views.ProjectorView.as_view(), - name='projector_show'), - - url(r'^preview/(?P[^/]*)/$', - views.ProjectorView.as_view(), - name='projector_preview'), - - url(r'^activate/(?P[^/]*)/$', - views.ActivateView.as_view(), - name='projector_activate_slide'), - - url(r'^overlay_message/$', - views.OverlayMessageView.as_view(), - name='projector_overlay_message'), - - url(r'^bigger/$', - views.ProjectorControllView.as_view(), - {'direction': 'bigger'}, - name='projector_bigger'), - - url(r'^smaller/$', - views.ProjectorControllView.as_view(), - {'direction': 'smaller'}, - name='projector_smaller'), - - url(r'^up/$', - views.ProjectorControllView.as_view(), - {'direction': 'up'}, - name='projector_up'), - - url(r'^down/$', - views.ProjectorControllView.as_view(), - {'direction': 'down'}, - name='projector_down'), - - url(r'^clean/scale/$', - views.ProjectorControllView.as_view(), - {'direction': 'clean_scale'}, - name='projector_clean_scale'), - - url(r'^clean/scroll/$', - views.ProjectorControllView.as_view(), - {'direction': 'clean_scroll'}, - name='projector_clean_scroll'), - - url(r'^countdown/reset/$', - views.CountdownControllView.as_view(), - {'command': 'reset'}, - name='countdown_reset'), - - url(r'^countdown/start/$', - views.CountdownControllView.as_view(), - {'command': 'start'}, - name='countdown_start'), - - url(r'^countdown/stop/$', - views.CountdownControllView.as_view(), - {'command': 'stop'}, - name='countdown_stop'), - - url(r'^countdown/set-default/$', - views.CountdownControllView.as_view(), - {'command': 'set-default'}, - name='countdown_set_default'), - - url('^overlay/(?P[^/]*)/activate/$', - views.ActivateOverlay.as_view(), - {'activate': True}, - name='projector_overlay_activate'), - - url('^overlay/(?P[^/]*)/deactivate/$', - views.ActivateOverlay.as_view(), - {'activate': False}, - name='projector_overlay_deactivate'), -) diff --git a/openslides/projector/views.py b/openslides/projector/views.py deleted file mode 100644 index 8ef546f23..000000000 --- a/openslides/projector/views.py +++ /dev/null @@ -1,174 +0,0 @@ -from openslides.config.api import config -from openslides.utils.views import RedirectView, TemplateView - -from .api import (call_on_projector, get_active_slide, - get_overlays, get_projector_content, - get_projector_overlays_js, reset_countdown, set_active_slide, - start_countdown, stop_countdown) - - -class ProjectorView(TemplateView): - """ - The Projector-Page. - """ - required_permission = 'core.can_see_projector' - template_name = 'projector.html' - - def get_context_data(self, **kwargs): - callback = self.kwargs.get('callback', None) - - if callback is None: - kwargs.update({ - 'content': get_projector_content(), - 'overlays': get_overlays(only_active=True), - 'overlay_js': get_projector_overlays_js(as_json=True), - 'reload': True, - 'calls': config['projector_js_cache']}) - # For the Preview - else: - slide_dict = dict(self.request.GET.items()) - slide_dict['callback'] = callback - kwargs.update({ - 'content': get_projector_content(slide_dict), - 'reload': False}) - - return super(ProjectorView, self).get_context_data(**kwargs) - - -class ActivateView(RedirectView): - """ - Activate a Slide. - """ - required_permission = 'core.can_manage_projector' - url_name = 'core_dashboard' - allow_ajax = True - - def pre_redirect(self, request, *args, **kwargs): - if (kwargs['callback'] == 'mediafile' and - get_active_slide()['callback'] == 'mediafile'): - # TODO: find a way to do this in the mediafile-app - # If the current slide is a pdf and the new page is also a slide, - # we dont have to use set_active_slide, because is causes a content - # reload. - kwargs.update({'page_num': 1, 'pk': request.GET.get('pk')}) - # TODO: fix me - # url = Mediafile.objects.get(pk=kwargs['pk'], is_presentable=True).mediafile.url - config['projector_active_slide'] = kwargs - # ProjectorSocketHandler.send_updates( - # {'calls': {'load_pdf': {'url': url, 'page_num': kwargs['page_num']}}}) - else: - set_active_slide(kwargs['callback'], **dict(request.GET.items())) - - -class ProjectorControllView(RedirectView): - """ - Scale or scroll the projector. - """ - required_permission = 'core.can_manage_projector' - url_name = 'core_dashboard' - allow_ajax = True - - def pre_redirect(self, request, *args, **kwargs): - direction = kwargs['direction'] - if direction == 'bigger': - config['projector_scale'] = int(config['projector_scale']) + 1 - elif direction == 'smaller': - config['projector_scale'] = int(config['projector_scale']) - 1 - elif direction == 'down': - config['projector_scroll'] = int(config['projector_scroll']) + 1 - elif direction == 'up': - if config['projector_scroll'] > 0: - config['projector_scroll'] = int(config['projector_scroll']) - 1 - elif direction == 'clean_scale': - config['projector_scale'] = config.get_default('projector_scale') - elif direction == 'clean_scroll': - config['projector_scroll'] = config.get_default('projector_scroll') - - call_on_projector({'scroll': config['projector_scroll'], - 'scale': config['projector_scale']}) - - def get_ajax_context(self, **kwargs): - return { - 'scale_level': config['projector_scale'], - 'scroll_level': config['projector_scroll'], - } - - -class CountdownControllView(RedirectView): - """ - Start, stop or reset the countdown. - """ - required_permission = 'core.can_manage_projector' - url_name = 'core_dashboard' - allow_ajax = True - - def pre_redirect(self, request, *args, **kwargs): - command = kwargs['command'] - if command == 'reset': - reset_countdown() - elif command == 'start': - start_countdown() - elif command == 'stop': - stop_countdown() - elif command == 'set-default': - try: - config['countdown_time'] = \ - int(self.request.GET['countdown_time']) - except (ValueError, AttributeError): - pass - else: - reset_countdown() - # TODO: send signal to update data - - def get_ajax_context(self, **kwargs): - return { - 'state': config['countdown_state'], - 'countdown_time': config['countdown_time'], - } - - -class OverlayMessageView(RedirectView): - """ - Sets or clears the overlay message - """ - url_name = 'core_dashboard' - allow_ajax = True - required_permission = 'core.can_manage_projector' - - def pre_post_redirect(self, request, *args, **kwargs): - if 'message' in request.POST: - config['projector_message'] = request.POST['message_text'] - elif 'message-clean' in request.POST: - config['projector_message'] = '' - # TODO: update data - - def get_ajax_context(self, **kwargs): - return { - 'overlay_message': config['projector_message'], - } - - -class ActivateOverlay(RedirectView): - """ - Activate or deactivate an overlay. - """ - url_name = 'core_dashboard' - allow_ajax = True - required_permission = 'core.can_manage_projector' - - def pre_redirect(self, request, *args, **kwargs): - overlay = get_overlays()[kwargs['name']] - self.name = overlay.name - if kwargs['activate']: - if not overlay.is_active(): - overlay.set_active(True) - # Push new overlay to projector, somehow... - self.active = True - else: - if overlay.is_active(): - overlay.set_active(False) - # Push new overlay to projector, somehow... - self.active = False - - def get_ajax_context(self, **kwargs): - return {'active': self.active, 'name': self.name} diff --git a/openslides/projector/widgets.py b/openslides/projector/widgets.py deleted file mode 100644 index da02d94b5..000000000 --- a/openslides/projector/widgets.py +++ /dev/null @@ -1,44 +0,0 @@ -from django.core.context_processors import csrf -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget - -from .signals import projector_overlays - - -class ProjectorLiveWidget(Widget): - """ - Widget with a live view of the projector. - """ - name = 'live_view' - verbose_name = ugettext_lazy('Projector live view') - required_permission = 'core.can_see_projector' - default_column = 2 - default_weight = 10 - template_name = 'projector/widget_live_view.html' - icon_css_class = 'icon-facetime-video' - - -class OverlayWidget(Widget): - """ - Widget to control all overlays. - """ - name = 'overlays' # TODO: Use singular here - verbose_name = ugettext_lazy('Overlays') - required_permission = 'core.can_manage_projector' - default_column = 2 - default_weight = 20 - template_name = 'projector/widget_overlay.html' - icon_css_class = 'icon-star' - - def get_context_data(self, **context): - """ - Inserts all overlays into the context. The overlays are collected by - the projector_overlays signal. - """ - overlays = [overlay for __, overlay in projector_overlays.send(sender='overlay_widget', request=self.request) - if overlay.widget_html_callback is not None] - context.update(csrf(self.request)) - return super(OverlayWidget, self).get_context_data( - overlays=overlays, - **context) diff --git a/openslides/urls.py b/openslides/urls.py index a133af703..13b630306 100644 --- a/openslides/urls.py +++ b/openslides/urls.py @@ -1,52 +1,17 @@ from django.conf.urls import include, patterns, url +from django.views.generic import RedirectView -from openslides.core.views import IndexView, ErrorView -from openslides.users.views import UserSettingsView, UserPasswordSettingsView +from openslides.core.views import IndexView from openslides.utils.rest_api import router -handler403 = ErrorView.as_view(status_code=403) -handler404 = ErrorView.as_view(status_code=404) -handler500 = ErrorView.as_view(status_code=500) - urlpatterns = patterns( '', - url(r'^', include('openslides.core.urls')), - url(r'^core.*', IndexView.as_view()), + url(r'^(?P.*[^/])$', RedirectView.as_view(url='/%(url)s/')), url(r'^rest/', include(router.urls)), - url(r'^users/', include('openslides.users.urls')), - url(r'^users.*', IndexView.as_view()), - url(r'^assignments/', include('openslides.assignments.urls')), - url(r'^assignments.*', IndexView.as_view()), + url(r'^', include('openslides.core.urls')), url(r'^agenda/', include('openslides.agenda.urls')), - url(r'^agenda.*', IndexView.as_view()), + url(r'^assignments/', include('openslides.assignments.urls')), url(r'^motions/', include('openslides.motions.urls')), - url(r'^motions.*', IndexView.as_view()), - url(r'^mediafiles/', include('openslides.mediafiles.urls')), - url(r'^mediafiles.*', IndexView.as_view()), - - # TODO: all patterns end with ".*" can be removed after we add the global - # url-pattern "/" to the indexView - - # Activate next lines to get more AngularJS views - # url(r'^$', IndexView.as_view()), -) - -# Deprecated urls. Move them up when the apps are refactored. -urlpatterns += patterns( - '', - (r'^config/', include('openslides.config.urls')), - (r'^projector/', include('openslides.projector.urls')), - (r'^i18n/', include('django.conf.urls.i18n')), - (r'^ckeditor/', include('ckeditor.urls')), -) - -# TODO: Move these patterns into core or the users app. -urlpatterns += patterns( - '', - url(r'^myusersettings/$', - UserSettingsView.as_view(), - name='user_settings'), - url(r'^myusersettings/changepassword/$', - UserPasswordSettingsView.as_view(), - name='password_change'), + url(r'^users/', include('openslides.users.urls')), + url(r'^.*$', IndexView.as_view()), ) diff --git a/openslides/users/apps.py b/openslides/users/apps.py index c04af0627..8d81339b6 100644 --- a/openslides/users/apps.py +++ b/openslides/users/apps.py @@ -6,9 +6,9 @@ class UsersAppConfig(AppConfig): verbose_name = 'OpenSlides Users' def ready(self): - # Load main menu entry and widgets. + # Load projector elements. # Do this by just importing all from these files. - from . import main_menu, projector, widgets # noqa + from . import projector # noqa # Import all required stuff. from openslides.config.signals import config_signal diff --git a/openslides/users/csv_import.py b/openslides/users/csv_import.py deleted file mode 100644 index e9055fbdb..000000000 --- a/openslides/users/csv_import.py +++ /dev/null @@ -1,84 +0,0 @@ -import csv - -from django.db import transaction -from django.utils.translation import ugettext as _ - -from openslides.utils import csv_ext -from openslides.utils.utils import html_strong - -from .models import Group, User - - -def import_users(csvfile): - error_messages = [] - count_success = 0 - try: - # check for valid encoding (will raise UnicodeDecodeError if not) - csvfile.read().decode('utf-8') - csvfile.seek(0) - - with transaction.atomic(): - dialect = csv.Sniffer().sniff(csvfile.readline().decode('utf-8')) - dialect = csv_ext.patchup(dialect) - csvfile.seek(0) - - for (line_no, line) in enumerate(csv.reader( - (line.decode('utf8') for line in csvfile.readlines()), dialect=dialect)): - if line_no: - try: - (title, first_name, last_name, gender, email, groups, - structure_level, committee, about_me, comment, is_active) = line[:11] - except ValueError: - error_messages.append(_('Ignoring malformed line %d in import file.') % (line_no + 1)) - continue - if not first_name and not last_name: - error_messages.append(_("In line %d you have to provide either 'first_name' or 'last_name'.") % (line_no + 1)) - continue - user = User() - user.title = title - user.last_name = last_name - user.first_name = first_name - user.username = User.objects.generate_username(first_name, last_name) - user.gender = gender - user.email = email - user.structure_level = structure_level - user.committee = committee - user.about_me = about_me - user.comment = comment - if is_active == '1': - user.is_active = True - else: - user.is_active = False - user.default_password = User.objects.generate_password() - user.save() - for groupid in groups.split(','): - try: - if groupid and int(groupid): - Group.objects.get(pk=groupid).user_set.add(user) - except (Group.DoesNotExist, ValueError): - error_messages.append(_('Ignoring group id "%(id)s" in line %(line)d which does not exist.') % - {'id': groupid, 'line': line_no + 1}) - continue - user.reset_password() - user.save() - count_success += 1 - except csv.Error: - error_messages.append(_('Import aborted because of severe errors in the input file.')) - except UnicodeDecodeError: - error_messages.append(_('Import file has wrong character encoding, only UTF-8 is supported!')) - - # Build final success message - if count_success: - success_message = _('%d new users were successfully imported.') % count_success - else: - success_message = '' - - # Build final error message with all error items (one bullet point for each csv line) - full_error_message = '' - if error_messages: - full_error_message = "%s
    " % html_strong(_("Errors")) - for error in error_messages: - full_error_message += "
  • %s
  • " % error - full_error_message += "
" - - return success_message, '', full_error_message diff --git a/openslides/users/forms.py b/openslides/users/forms.py deleted file mode 100644 index 53e0ef106..000000000 --- a/openslides/users/forms.py +++ /dev/null @@ -1,16 +0,0 @@ -from django import forms -from django.conf import settings -from django.utils.translation import ugettext_lazy - -from openslides.utils.forms import CssClassMixin - -from .models import User - - -class UsersettingsForm(CssClassMixin, forms.ModelForm): - class Meta: - model = User - fields = ('username', 'title', 'first_name', 'last_name', 'about_me') - - language = forms.ChoiceField( - choices=settings.LANGUAGES, label=ugettext_lazy('Language')) diff --git a/openslides/users/main_menu.py b/openslides/users/main_menu.py deleted file mode 100644 index eb4d28ba6..000000000 --- a/openslides/users/main_menu.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.main_menu import MainMenuEntry - - -class UserMainMenuEntry(MainMenuEntry): - """ - Main menu entry for the participant app. - """ - verbose_name = ugettext_lazy('Users') - required_permission = 'users.can_see_extra_data' - default_weight = 50 - pattern_name = '/users' # TODO: use generic solution, see issue #1469 - icon_css_class = 'glyphicon-user' diff --git a/openslides/users/models.py b/openslides/users/models.py index 563c70a27..5c68d8970 100644 --- a/openslides/users/models.py +++ b/openslides/users/models.py @@ -8,14 +8,13 @@ from django.contrib.auth.models import ( # noqa BaseUserManager, Group, Permission, - PermissionsMixin + PermissionsMixin, ) from django.db import models from django.utils.translation import ugettext_lazy, ugettext_noop from openslides.config.api import config from openslides.projector.models import SlideMixin -from openslides.utils.models import AbsoluteUrlMixin from openslides.utils.rest_api import RESTModelMixin from .exceptions import UserError @@ -88,7 +87,7 @@ class UserManager(BaseUserManager): return ''.join([choice(chars) for i in range(size)]) -class User(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, PermissionsMixin, AbstractBaseUser): +class User(RESTModelMixin, SlideMixin, PermissionsMixin, AbstractBaseUser): """ Model for users in OpenSlides. A client can login as a user with credentials. A user can also just be used as representation for a person @@ -154,18 +153,6 @@ class User(RESTModelMixin, SlideMixin, AbsoluteUrlMixin, PermissionsMixin, Abstr def __str__(self): return self.get_full_name() - def get_absolute_url(self, link='detail'): - """ - Returns the URL to the user. - """ - if link == 'detail': - url = "/users/%s/" % self.pk - elif link == 'update': - url = "/users/%s/edit/" % self.pk - else: - url = super().get_absolute_url(link) - return url - def get_slide_context(self, **context): """ Returns the context for the user slide. diff --git a/openslides/users/search_indexes.py b/openslides/users/search_indexes.py index 380eda9e2..6c7ce3702 100644 --- a/openslides/users/search_indexes.py +++ b/openslides/users/search_indexes.py @@ -1,4 +1,5 @@ from haystack import indexes + from .models import User diff --git a/openslides/users/serializers.py b/openslides/users/serializers.py index dd3f7245f..dce7f156e 100644 --- a/openslides/users/serializers.py +++ b/openslides/users/serializers.py @@ -1,7 +1,13 @@ from django.contrib.auth.hashers import make_password -from django.utils.translation import ugettext as _, ugettext_lazy +from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext_lazy -from openslides.utils.rest_api import ModelSerializer, PrimaryKeyRelatedField, RelatedField, ValidationError +from openslides.utils.rest_api import ( + ModelSerializer, + PrimaryKeyRelatedField, + RelatedField, + ValidationError, +) from .models import Group, Permission, User diff --git a/openslides/users/signals.py b/openslides/users/signals.py index 2a432becd..05231043a 100644 --- a/openslides/users/signals.py +++ b/openslides/users/signals.py @@ -3,7 +3,11 @@ from django.db.models import Q from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy, ugettext_noop -from openslides.config.api import ConfigGroup, ConfigGroupedCollection, ConfigVariable +from openslides.config.api import ( + ConfigGroup, + ConfigGroupedCollection, + ConfigVariable, +) from .models import Group, Permission, User diff --git a/openslides/users/templates/users/password_change.html b/openslides/users/templates/users/password_change.html deleted file mode 100644 index cef9a54d5..000000000 --- a/openslides/users/templates/users/password_change.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block title %}{% trans "Password settings" %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans "Password settings" %}

- -
{% csrf_token %} - {% include "form.html" %} - {% include "formbuttons_save.html" %} -
-{% endblock %} diff --git a/openslides/users/templates/users/settings.html b/openslides/users/templates/users/settings.html deleted file mode 100644 index b667a93d7..000000000 --- a/openslides/users/templates/users/settings.html +++ /dev/null @@ -1,14 +0,0 @@ -{% extends "base.html" %} - -{% load i18n %} - -{% block title %}{% trans "Edit profile" %} – {{ block.super }}{% endblock %} - -{% block content %} -

{% trans "Edit profile" %}

- -
{% csrf_token %} - {% include "form.html" %} - {% include "formbuttons_save.html" %} -
-{% endblock %} diff --git a/openslides/users/templates/users/user_slide.html b/openslides/users/templates/users/user_slide.html deleted file mode 100644 index 09f1c9335..000000000 --- a/openslides/users/templates/users/user_slide.html +++ /dev/null @@ -1,18 +0,0 @@ -{% load i18n %} - -

- {{ shown_user.clean_name }}
- {{ shown_user.structure_level }} -

- -{% if shown_user.committee %} -

{{ shown_user.committee }}

-{% endif %} - -

- {% if shown_user.groups.all %} - {% for group in shown_user.groups.all %} - {% trans group.name %}{% if not forloop.last %}
{% endif %} - {% endfor %} - {% endif %} -

diff --git a/openslides/users/templates/users/widget_user.html b/openslides/users/templates/users/widget_user.html deleted file mode 100644 index 3e33f0795..000000000 --- a/openslides/users/templates/users/widget_user.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends 'core/widget.html' %} - -{% load i18n %} -{% load tags %} - -{% block content %} -
    -{% for user in users %} -
  • - - -   - {% if perms.users.can_manage %} - - - - {% endif %} - - - - {{ user }} -
  • -{% empty %} -
  • {% trans 'No users available.' %}
  • -{% endfor %} -
-{% endblock %} diff --git a/openslides/users/views.py b/openslides/users/views.py index 5d3ff641a..bbda24593 100644 --- a/openslides/users/views.py +++ b/openslides/users/views.py @@ -1,23 +1,12 @@ -from django.contrib import messages from django.contrib.auth import login as auth_login from django.contrib.auth import logout as auth_logout -from django.contrib.auth.forms import AuthenticationForm, PasswordChangeForm -from django.utils.translation import ugettext as _ -from django.utils.translation import activate, ugettext_lazy +from django.contrib.auth.forms import AuthenticationForm +from django.utils.translation import ugettext_lazy from rest_framework import status from openslides.utils.rest_api import ModelViewSet, Response -from openslides.utils.views import ( - APIView, - CSVImportView, - FormView, - LoginMixin, - PDFView, - UpdateView, -) +from openslides.utils.views import APIView, PDFView -from .csv_import import import_users -from .forms import UsersettingsForm from .models import Group, User from .pdf import users_passwords_to_pdf, users_to_pdf from .serializers import ( @@ -179,54 +168,3 @@ class WhoAmIView(APIView): return super().get_context_data( user_id=self.request.user.pk, **context) - - -# Deprecated views. Will be removed after the implementation in angularjs - -class UserCSVImportView(CSVImportView): - """ - Import users via CSV. - """ - required_permission = 'users.can_manage' - success_url_name = 'user_list' - template_name = 'users/user_form_csv_import.html' - import_function = staticmethod(import_users) - - -class UserSettingsView(LoginMixin, UpdateView): - required_permission = None - template_name = 'users/settings.html' - success_url_name = 'user_settings' - model = User - form_class = UsersettingsForm - url_name_args = [] - - def get_initial(self): - initial = super().get_initial() - initial['language'] = self.request.session.get('django_language', self.request.LANGUAGE_CODE) - return initial - - def form_valid(self, form): - self.request.LANGUAGE_CODE = self.request.session['django_language'] = form.cleaned_data['language'] - activate(self.request.LANGUAGE_CODE) - return super().form_valid(form) - - def get_object(self): - return self.request.user - - -class UserPasswordSettingsView(LoginMixin, FormView): - required_permission = None - template_name = 'users/password_change.html' - success_url_name = 'core_dashboard' - form_class = PasswordChangeForm - - def form_valid(self, form): - form.save() - messages.success(self.request, _('Password successfully changed.')) - return super().form_valid(form) - - def get_form_kwargs(self): - kwargs = super().get_form_kwargs() - kwargs['user'] = self.request.user - return kwargs diff --git a/openslides/users/widgets.py b/openslides/users/widgets.py deleted file mode 100644 index 4965bebe9..000000000 --- a/openslides/users/widgets.py +++ /dev/null @@ -1,24 +0,0 @@ -from django.utils.translation import ugettext_lazy - -from openslides.utils.widgets import Widget - -from .models import User - - -class UserWidget(Widget): - """ - Provides a widget with all users. This is for short activation of - user slides. - """ - name = 'user' - verbose_name = ugettext_lazy('Users') - required_permission = 'core.can_manage_projector' - default_column = 1 - default_weight = 60 - default_active = False - template_name = 'users/widget_user.html' - - def get_context_data(self, **context): - return super(UserWidget, self).get_context_data( - users=User.objects.all(), - **context) diff --git a/openslides/utils/autoupdate.py b/openslides/utils/autoupdate.py index 841bce4a0..f9c093fb2 100644 --- a/openslides/utils/autoupdate.py +++ b/openslides/utils/autoupdate.py @@ -5,17 +5,17 @@ from urllib.parse import unquote from django.conf import settings from django.core.wsgi import get_wsgi_application -from sockjs.tornado import SockJSRouter, SockJSConnection -from tornado.httpserver import HTTPServer +from sockjs.tornado import SockJSConnection, SockJSRouter from tornado.httpclient import AsyncHTTPClient, HTTPRequest +from tornado.httpserver import HTTPServer from tornado.httputil import HTTPHeaders from tornado.ioloop import IOLoop from tornado.options import parse_command_line from tornado.web import ( Application, FallbackHandler, + HTTPError, StaticFileHandler, - HTTPError ) from tornado.wsgi import WSGIContainer diff --git a/openslides/utils/csv_ext.py b/openslides/utils/csv_ext.py deleted file mode 100644 index 7d988369b..000000000 --- a/openslides/utils/csv_ext.py +++ /dev/null @@ -1,21 +0,0 @@ -from csv import Dialect, excel, register_dialect - - -class excel_semikolon(Dialect): - delimiter = ';' - doublequote = True - lineterminator = '\r\n' - quotechar = '"' - quoting = 0 - skipinitialspace = False - - -def patchup(dialect): - if dialect: - if dialect.delimiter in [excel_semikolon.delimiter, excel.delimiter] and \ - dialect.quotechar == excel_semikolon.quotechar: - # walks like a duck and talks like a duck.. must be one - dialect.doublequote = True - return dialect - -register_dialect("excel_semikolon", excel_semikolon) diff --git a/openslides/utils/forms.py b/openslides/utils/forms.py deleted file mode 100644 index 11cda07d0..000000000 --- a/openslides/utils/forms.py +++ /dev/null @@ -1,117 +0,0 @@ -import bleach -from django import forms -from django.utils.translation import ugettext as _ -from django.utils.translation import ugettext_lazy - -# Allowed tags, attributes and styles allowed in textareas edited with a JS -# editor. Everything not in these whitelists is stripped. -HTML_TAG_WHITELIST = ('a', - 'i', - 'em', - 'b', - 'strong', - 'ul', - 'ol', - 'li', - 'p', - 'br', - 'span', - 'strike', - 'u', - 'pre', - 'h1', - 'h2', - 'h3', - 'img') - -HTML_ATTRIBUTES_WHITELIST = { - 'a': ['href'], - '*': ['style'], - 'pre': ['class'], - 'img': ['src', 'width', 'height'], -} - -HTML_STYLES_WHITELIST = ('color', 'background-color', 'list-style', 'width', 'height') - - -class CssClassMixin(object): - error_css_class = 'error' - required_css_class = 'required' - - -class LocalizedModelChoiceField(forms.ModelChoiceField): - """ - Subclass of Django's ModelChoiceField to translate the labels of the - model's objects. - """ - def label_from_instance(self, *args, **kwargs): - """ - Translates the output from Django's label_from_instance method. - """ - return _(super(LocalizedModelChoiceField, self).label_from_instance(*args, **kwargs)) - - -class LocalizedModelMultipleChoiceField(forms.ModelMultipleChoiceField): - """ - FormField to translate models in many to many fields in forms. - """ - - def __init__(self, *args, **kwargs): - self.to_field_name = kwargs.get('to_field_name', None) - super(LocalizedModelMultipleChoiceField, self).__init__(*args, **kwargs) - - def _localized_get_choices(self): - """ - Generator which calles super() and yields the values. - """ - if hasattr(self, '_choices'): - return self._choices - - for (id, text) in super()._get_choices(): - # This line is only required to translate permission objects - text = text.split(' | ')[-1] - yield (id, ugettext_lazy(text)) - - choices = property(_localized_get_choices, forms.ChoiceField._set_choices) - - -class CleanHtmlFormMixin(object): - """ - A form mixin that pre-processes the form, cleaning up the HTML code found - in the fields in clean_html. All HTML tags, attributes and styles not in the - whitelists are stripped from the output, leaving only the text content: - -
foo
simply becomes 'foo' - """ - clean_html_fields = () - - def get_clean_html_fields(self): - """ - The list of elements to strip of potential malicious HTML. - """ - return self.clean_html_fields - - def clean(self): - cleaned_data = super(CleanHtmlFormMixin, self).clean() - for field in self.get_clean_html_fields(): - try: - cleaned_data[field] = bleach.clean( - cleaned_data[field], - tags=HTML_TAG_WHITELIST, - attributes=HTML_ATTRIBUTES_WHITELIST, - styles=HTML_STYLES_WHITELIST, - strip=True) - except KeyError: - # The field 'field' is not pressent. Do not change cleaned_data - pass - return cleaned_data - - -class CSVImportForm(CssClassMixin, forms.Form): - """ - Form for the CSVImportView. - """ - csvfile = forms.FileField( - widget=forms.FileInput(attrs={'size': '50'}), - label=ugettext_lazy('CSV File'), - help_text=ugettext_lazy('The file has to be encoded in UTF-8.')) diff --git a/openslides/utils/main.py b/openslides/utils/main.py index 280c9a031..6eb418c00 100644 --- a/openslides/utils/main.py +++ b/openslides/utils/main.py @@ -1,3 +1,4 @@ +import argparse import ctypes import os import sys @@ -5,13 +6,12 @@ import tempfile import threading import time import webbrowser -import argparse -from django.core.exceptions import ImproperlyConfigured from django.conf import ENVIRONMENT_VARIABLE +from django.core.exceptions import ImproperlyConfigured from django.utils.crypto import get_random_string -from django.utils.translation import activate, check_for_language, get_language from django.utils.translation import ugettext as _ +from django.utils.translation import activate, check_for_language, get_language DEVELOPMENT_VERSION = 'Development Version' UNIX_VERSION = 'Unix Version' diff --git a/openslides/utils/main_menu.py b/openslides/utils/main_menu.py deleted file mode 100644 index 400d9339b..000000000 --- a/openslides/utils/main_menu.py +++ /dev/null @@ -1,119 +0,0 @@ -from django.core.urlresolvers import reverse -from django.dispatch import Signal, receiver - -from .dispatch import SignalConnectMetaClass -from .signals import template_manipulation - - -class MainMenuEntry(object, metaclass=SignalConnectMetaClass): - """ - Base class for a main menu entry. - - Every app which wants to add entries has to create a class subclassing - from this base class. For the appearance the verbose_name, the - pattern_name and the icon-css-class attribute have to be set. The - metaclass (SignalConnectMetaClass) does the rest of the - magic. - - For the appearance there are some optional attributes and methods like - required_permission, default_weight, stylesheets, javascript_files, - check_permission, get_url, get_default_weight, get_icon_css_class, - get_stylesheets and get_javascript_files. - """ - signal = Signal(providing_args=['request']) - verbose_name = None - required_permission = None - default_weight = 0 - pattern_name = None - icon_css_class = 'icon-home' - stylesheets = None - javascript_files = None - - def __init__(self, sender, request, **kwargs): - """ - Initializes the main menu entry instance. This is done when the signal - is sent. - - Only the required request argument is used. Because of Django's signal - API, we have to take also a sender argument and wildcard keyword - arguments. But they are not used here. - """ - self.request = request - - def __str__(self): - if self.verbose_name is None: - raise NotImplementedError( - 'The main menu entry class %s must provide a verbose_name ' - 'attribute or override the __str__ method.' % type(self).__name__) - return str(self.verbose_name) - - @classmethod - def get_dispatch_uid(cls): - """ - Returns the classname as a unique string for each class. Returns None - for the base class so it will not be connected to the signal. - """ - if not cls.__name__ == 'MainMenuEntry': - return cls.__name__ - - def check_permission(self): - """ - Returns True if the request user is allowed to see the entry. - """ - return self.required_permission is None or self.request.user.has_perm(self.required_permission) - - def get_icon_css_class(self): - """ - Returns the css class name of the icon. Default is 'icon-home'. - """ - return self.icon_css_class - - def get_url(self): - """ - Returns the url of the entry. - """ - if self.pattern_name is None: - raise NotImplementedError( - 'The main menu entry class %s must provide a pattern_name ' - 'attribute or override the get_url method.' % type(self).__name__) - return reverse(self.pattern_name) - - def is_active(self): - """ - Returns True if the entry is selected at the moment. - """ - try: - return_value = isinstance(self, self.request.active_main_menu_class) - except AttributeError: - return_value = self.request.path.startswith(self.get_url()) - return return_value - - def get_stylesheets(self): - """ - Returns an interable of stylesheets to be loaded. - """ - return iter(self.stylesheets or []) - - def get_javascript_files(self): - """ - Returns an interable of javascript files to be loaded. - """ - return iter(self.javascript_files or []) - - -def main_menu_entries(request): - """ - Adds all main menu entries to the request context as template context - processor. - """ - return {'main_menu_entries': MainMenuEntry.get_all(request)} - - -@receiver(template_manipulation, dispatch_uid="add_main_menu_context") -def add_main_menu_context(sender, request, context, **kwargs): - """ - Adds all stylefiles from all main menu entries to the context. - """ - for main_menu_entry in MainMenuEntry.get_all(request): - context['extra_stylefiles'].extend(main_menu_entry.get_stylesheets()) - context['extra_javascript'].extend(main_menu_entry.get_javascript_files()) diff --git a/openslides/utils/models.py b/openslides/utils/models.py index f430440fe..9744332b1 100644 --- a/openslides/utils/models.py +++ b/openslides/utils/models.py @@ -14,19 +14,3 @@ class MinMaxIntegerField(models.IntegerField): defaults = {'min_value': self.min_value, 'max_value': self.max_value} defaults.update(kwargs) return super(MinMaxIntegerField, self).formfield(**defaults) - - -class AbsoluteUrlMixin(object): - """ - Mixin that raises a ValueError if the name of an url was not found with - get_absolute_url. - - The Mixin has to be placed as last OpenSlides-Mixin before the django - model class. - """ - - def get_absolute_url(self, link=None): - """ - Raises a ValueError. - """ - raise ValueError('Unknown Link "%s" for model "%s"' % (link, type(self))) diff --git a/openslides/utils/personal_info.py b/openslides/utils/personal_info.py deleted file mode 100644 index 63d727249..000000000 --- a/openslides/utils/personal_info.py +++ /dev/null @@ -1,51 +0,0 @@ -from django.dispatch import Signal - -from .dispatch import SignalConnectMetaClass - - -class PersonalInfo(object, metaclass=SignalConnectMetaClass): - """ - Base class for a personal info collection for the personal info widget - on the dashboard. - - Every app which wants to add info has to create a class subclassing - from this base class. For the content the headline attribute, the - default_weight attribute and the get_queryset method have to be set. - The metaclass (SignalConnectMetaClass) does the rest of - the magic. - """ - signal = Signal(providing_args=['request']) - headline = None - default_weight = 0 - - def __init__(self, sender, request, **kwargs): - """ - Initializes the personal info instance. This is done when the - signal is sent. - - Only the required request argument is used. Because of Django's signal - API, we have to take also a sender argument and wildcard keyword - arguments. But they are not used here. - """ - self.request = request - - @classmethod - def get_dispatch_uid(cls): - """ - Returns the classname as a unique string for each class. Returns - None for the base class so it will not be connected to the signal. - """ - if not cls.__name__ == 'PersonalInfo': - return cls.__name__ - - def get_queryset(self): - """ - Returns a queryset of objects for the personal info widget. - """ - raise NotImplementedError('Your class %s has to define a get_queryset method.' % self.__class__) - - def is_active(self): - """ - Returns True if the infoblock is shown in the widget. - """ - return self.get_queryset() is not None diff --git a/openslides/utils/plugins.py b/openslides/utils/plugins.py index fd470dcc8..e49b179c1 100644 --- a/openslides/utils/plugins.py +++ b/openslides/utils/plugins.py @@ -6,8 +6,8 @@ from django.utils.importlib import import_module from pkg_resources import iter_entry_points from openslides.utils.main import ( - detect_openslides_type, WINDOWS_PORTABLE_VERSION, + detect_openslides_type, get_win32_portable_user_data_path, ) diff --git a/openslides/utils/rest_api.py b/openslides/utils/rest_api.py index 0840b988a..c57f18724 100644 --- a/openslides/utils/rest_api.py +++ b/openslides/utils/rest_api.py @@ -1,9 +1,12 @@ import re - from urllib.parse import urlparse from django.core.urlresolvers import reverse from rest_framework.decorators import detail_route # noqa +from rest_framework.decorators import list_route # noqa +from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa +from rest_framework.response import Response # noqa +from rest_framework.routers import DefaultRouter from rest_framework.serializers import ( # noqa CharField, DictField, @@ -15,13 +18,14 @@ from rest_framework.serializers import ( # noqa PrimaryKeyRelatedField, RelatedField, SerializerMethodField, - ValidationError) -from rest_framework.mixins import DestroyModelMixin, UpdateModelMixin # noqa -from rest_framework.response import Response # noqa -from rest_framework.routers import DefaultRouter -from rest_framework.viewsets import ModelViewSet as _ModelViewSet -from rest_framework.viewsets import GenericViewSet, ReadOnlyModelViewSet, ViewSet # noqa -from rest_framework.decorators import list_route # noqa + ValidationError, +) +from rest_framework.viewsets import ModelViewSet as _ModelViewSet # noqa +from rest_framework.viewsets import ( # noqa + GenericViewSet, + ReadOnlyModelViewSet, + ViewSet, +) from .exceptions import OpenSlidesError diff --git a/openslides/utils/signals.py b/openslides/utils/signals.py deleted file mode 100644 index cbe71c1f6..000000000 --- a/openslides/utils/signals.py +++ /dev/null @@ -1,15 +0,0 @@ -from django.dispatch import Signal - - -class TemplateManipulationSignal(Signal): - """ - Derived class to ensure that the key extra_stylefiles and extra_javascript - exist in the context dictionary. - """ - def send(self, **kwargs): - kwargs['context'].setdefault('extra_stylefiles', []) - kwargs['context'].setdefault('extra_javascript', []) - return super(TemplateManipulationSignal, self).send(**kwargs) - - -template_manipulation = TemplateManipulationSignal(providing_args=['request', 'context']) diff --git a/openslides/utils/utils.py b/openslides/utils/utils.py index aa54a07ff..05368581f 100644 --- a/openslides/utils/utils.py +++ b/openslides/utils/utils.py @@ -1,6 +1,4 @@ -import difflib import roman - from django.contrib.auth.models import Permission @@ -18,31 +16,6 @@ def delete_default_permissions(**kwargs): p.delete() -def html_strong(string): - """ - Returns the text wrapped in an HTML-Strong element. - """ - return "%s" % string - - -def htmldiff(text1, text2): - """ - Return string of html diff between two strings (text1 and text2) - """ - diff = difflib.HtmlDiff(wrapcolumn=60) - return diff.make_table(text1.splitlines(), text2.splitlines()) - - -def int_or_none(var): - """ - Trys to convert 'var' into an integer. Returns None if an TypeError occures. - """ - try: - return int(var) - except (TypeError, ValueError): - return None - - def to_roman(number): """ Converts an arabic number within range from 1 to 4999 to the corresponding roman number. diff --git a/openslides/utils/views.py b/openslides/utils/views.py index 6b0d2cb61..090fb6826 100644 --- a/openslides/utils/views.py +++ b/openslides/utils/views.py @@ -1,15 +1,8 @@ -import json from io import BytesIO from django.conf import settings -from django.contrib import messages -from django.contrib.auth.decorators import login_required -from django.core.context_processors import csrf -from django.core.exceptions import ImproperlyConfigured, PermissionDenied -from django.core.urlresolvers import reverse +from django.core.exceptions import PermissionDenied from django.http import HttpResponse, HttpResponseRedirect -from django.utils.decorators import method_decorator -from django.utils.translation import ugettext as _ from django.utils.translation import ugettext_lazy from django.views import generic as django_views from django.views.decorators.csrf import ensure_csrf_cookie @@ -18,28 +11,11 @@ from reportlab.platypus import SimpleDocTemplate, Spacer from rest_framework.response import Response from rest_framework.views import APIView as _APIView -from .exceptions import OpenSlidesError -from .forms import CSVImportForm from .pdf import firstPage, laterPages -from .signals import template_manipulation -from .utils import html_strong View = django_views.View -class LoginMixin: - """ - Mixin for Views, that only can be viseted from users how are logedin. - """ - - @method_decorator(login_required) - def dispatch(self, request, *args, **kwargs): - """ - Check if the user is loged in. - """ - return super().dispatch(request, *args, **kwargs) - - class PermissionMixin: """ Mixin for views, that only can be visited from users with special @@ -75,102 +51,6 @@ class PermissionMixin: return super().dispatch(request, *args, **kwargs) -class AjaxMixin: - """ - Mixin to response to an ajax request with an json object. - """ - - def get_ajax_context(self, **context): - """ - Returns a dictonary with the context for the ajax response. - """ - return context - - def ajax_get(self, request, *args, **kwargs): - """ - Deprecated. Use ajax_response instead. - """ - return self.ajax_response() - - def ajax_response(self): - """ - Returns the HttpResponse. - """ - return HttpResponse(json.dumps(self.get_ajax_context())) - - -class ExtraContextMixin: - """ - Mixin to send the signal 'template_manipulation' to add extra content to the - context of the view. - - For example this is used to add the main menu of openslides. - """ - - def get_context_data(self, **kwargs): - """ - Sends the signal. - """ - context = super(ExtraContextMixin, self).get_context_data(**kwargs) - template_manipulation.send( - sender=type(self), request=self.request, context=context) - return context - - -class UrlMixin: - url_name_args = None - - def get_url(self, url_name=None, url=None, args=None, use_absolute_url_link=None): - """ - Returns an url. - - Tries - 1. to use the reverse for the attribute 'url_name', - 2. to use the attribute 'url' or - 3. to use self.object.get_absolute_url(). - - Uses the attribute 'use_absolute_url_link' as argument for - get_absolute_url in the third step. If the attribute is 'None' then - the default value of get_absolute_url is used. - - Raises ImproperlyConfigured if no url can be found. - """ - if url_name: - value = reverse(url_name, args=args or []) - elif url: - value = url - else: - if use_absolute_url_link is None: - get_absolute_url_args = [] - else: - get_absolute_url_args = [use_absolute_url_link] - - try: - value = self.object.get_absolute_url(*get_absolute_url_args) - except AttributeError: - raise ImproperlyConfigured( - 'No url to redirect to. See openslides.utils.views.UrlMixin ' - 'for more details.') - return value - - def get_url_name_args(self): - """ - Returns the arguments for the url name. - - Default is an empty list or [self.object.pk] if this exist. - """ - if self.url_name_args is not None: - value = self.url_name_args - else: - try: - pk = self.object.pk - except AttributeError: - value = [] - else: - value = [pk] if pk else [] - return value - - class SingleObjectMixin(django_views.detail.SingleObjectMixin): """ Mixin for single objects from the database. @@ -197,91 +77,6 @@ class SingleObjectMixin(django_views.detail.SingleObjectMixin): return obj -class FormMixin(UrlMixin): - """ - Mixin for views with forms. - """ - - use_apply = True - success_url_name = None - success_url = None - success_message = None - apply_url_name = None - apply_url = None - error_message = ugettext_lazy('Please check the form for errors.') - - def get_apply_url(self): - """ - Returns the url when the user clicks on 'apply'. - """ - return self.get_url(self.apply_url_name, self.apply_url, - args=self.get_url_name_args(), - use_absolute_url_link='update') - - def get_success_url(self): - """ - Returns the url when the user submits a form. - - Redirects to get_apply_url if self.use_apply is True - """ - if self.use_apply and 'apply' in self.request.POST: - value = self.get_apply_url() - else: - value = self.get_url(self.success_url_name, self.success_url, - args=self.get_url_name_args()) - return value - - def form_valid(self, form): - value = super(FormMixin, self).form_valid(form) - messages.success(self.request, self.get_success_message()) - return value - - def form_invalid(self, form): - value = super(FormMixin, self).form_invalid(form) - messages.error(self.request, self.get_error_message()) - return value - - def get_success_message(self): - return self.success_message - - def get_error_message(self): - return self.error_message - - -class ModelFormMixin(FormMixin): - """ - Mixin for FormViews. - """ - - def form_valid(self, form): - """ - Called if the form is valid. - - 1. saves the form into the model, - 2. calls 'self.manipulate_object, - 3. saves the object in the database, - 4. calls self.post_save. - """ - self.object = form.save(commit=False) - self.manipulate_object(form) - self.object.save() - self.post_save(form) - messages.success(self.request, self.get_success_message()) - return HttpResponseRedirect(self.get_success_url()) - - def manipulate_object(self, form): - """ - Called before the object is saved into the database. - """ - pass - - def post_save(self, form): - """ - Called after the object is saved into the database. - """ - form.save_m2m() - - class CSRFMixin: """ Adds the csrf cookie to the response. @@ -293,292 +88,6 @@ class CSRFMixin: return ensure_csrf_cookie(view) -class TemplateView(PermissionMixin, ExtraContextMixin, django_views.TemplateView): - """ - View to return with an template. - """ - pass - - -class ListView(PermissionMixin, ExtraContextMixin, django_views.ListView): - """ - View to show a list of model objects. - """ - pass - - -class AjaxView(PermissionMixin, AjaxMixin, View): - """ - View for ajax requests. - - Deprecated. Use APIView instead. - """ - def get(self, request, *args, **kwargs): - # TODO: Raise an error, if the request is not an ajax-request - return self.ajax_response() - - def post(self, *args, **kwargs): - return self.ajax_response() - - -class RedirectView(PermissionMixin, AjaxMixin, UrlMixin, django_views.RedirectView): - """ - View to redirect to another url. - - The initial value of url_name_args is None, but the default given by - the used get_url_name_args method is [self.object.pk] if it exist, else - an empty list. Set url_name_args to an empty list, if you use an url - name which does not need any arguments. - """ - permanent = False - allow_ajax = False - url_name = None - - def pre_redirect(self, request, *args, **kwargs): - """ - Called before the redirect. - """ - # TODO: Also call this method on post-request. - # Add pre_get_redirect for get requests. - pass - - def pre_post_redirect(self, request, *args, **kwargs): - """ - Called before the redirect, if it is a post request. - """ - pass - - def get(self, request, *args, **kwargs): - if request.method == 'GET': - self.pre_redirect(request, *args, **kwargs) - elif request.method == 'POST': - self.pre_post_redirect(request, *args, **kwargs) - - if request.is_ajax() and self.allow_ajax: - return self.ajax_get(request, *args, **kwargs) - return super(RedirectView, self).get(request, *args, **kwargs) - - def get_redirect_url(self, **kwargs): - """ - Returns the url to which the redirect should go. - """ - return self.get_url(self.url_name, self.url, - args=self.get_url_name_args()) - - -class QuestionView(RedirectView): - """ - Mixin for questions to the requesting user. - - The BaseView has to be a RedirectView. - """ - - question_message = ugettext_lazy('Are you sure?') - final_message = ugettext_lazy('Thank you for your answer.') - answer_options = [('yes', ugettext_lazy("Yes")), ('no', ugettext_lazy("No"))] - question_url_name = None - question_url = None - - def get_redirect_url(self, **kwargs): - """ - Returns the url to which the view should redirect. - """ - if self.request.method == 'GET': - url = self.get_url(self.question_url_name, self.question_url, - args=self.get_url_name_args()) - else: - url = super(QuestionView, self).get_redirect_url() - return url - - def pre_redirect(self, request, *args, **kwargs): - """ - Prints the question in a GET request. - """ - self.confirm_form() - - def get_question_message(self): - """ - Returns the question. - """ - return str(self.question_message) - - def get_answer_options(self): - """ - Returns the possible answers. - - This is a list of tubles. The first element of the tuble is the key, - the second element is shown to the user. - """ - return self.answer_options - - def confirm_form(self): - """ - Returns the form to show in the message. - """ - option_fields = "\n".join([ - '' - % (option[0], str(option[1])) - for option in self.get_answer_options()]) - messages.warning( - self.request, - '%(message)s
' - '' - '%(option_fields)s
' % { - 'message': self.get_question_message(), - 'url': self.request.path, - 'csrf': csrf(self.request)['csrf_token'], - 'option_fields': option_fields}) - - def pre_post_redirect(self, request, *args, **kwargs): - """ - Calls the method for the answer the user clicked. - - The method name is on_clicked_ANSWER where ANSWER is the key from - the clicked answer. See get_answer_options. Prints an error - message, if no valid answer was given. If this method is not - defined, nothing happens, else it is called and the success message - is printed to the user. - """ - try: - answer = self.get_answer() - except OpenSlidesError as error: - messages.error(self.request, error) - else: - method_name = 'on_clicked_%s' % answer - method = getattr(self, method_name, None) - if method is None: - pass - else: - method() - self.create_final_message() - - def get_answer(self): - """ - Returns the key of the clicked answer. - - Raises OpenSlidesError if the answer is not one of get_answer_options. - """ - for option_key, option_name in self.get_answer_options(): - if option_key in self.request.POST: - answer = option_key - break - else: - raise OpenSlidesError(ugettext_lazy('You did not send a valid answer.')) - return answer - - def get_final_message(self): - """ - Returns the message to show after the action. - - Uses the attribute 'final_messsage' as default - """ - return self.final_message - - def create_final_message(self): - """ - Creates a message. - """ - messages.success(self.request, self.get_final_message()) - - -class FormView(PermissionMixin, ExtraContextMixin, FormMixin, - django_views.FormView): - """ - View for forms. - """ - pass - - -class UpdateView(PermissionMixin, ExtraContextMixin, - ModelFormMixin, SingleObjectMixin, django_views.UpdateView): - """ - View to update an model object. - """ - - def get_success_message(self): - if self.success_message is None: - message = _('%s was successfully modified.') % html_strong(self.get_object()) - else: - message = self.success_message - return message - - -class CreateView(PermissionMixin, ExtraContextMixin, - ModelFormMixin, django_views.CreateView): - """ - View to create a model object. - - Note: This class has a django method get_object() which is different form - the method in openslides.utils.views.SingleObjectMixin. The result - is not cached. - """ - - def get_success_message(self): - if self.success_message is None: - message = _('%s was successfully created.') % html_strong(self.object) - else: - message = self.success_message - return message - - -class DeleteView(SingleObjectMixin, QuestionView): - """ - View to delete an model object. - """ - success_url = None - success_url_name = None - - def get_redirect_url(self, **kwargs): - """ - Returns the url on which the delete dialog is shown and the url after - the deleting. - - On GET-requests and on aborted or failed POST-requests, redirects to the detail - view as default. The attributes question_url_name or question_url can - define other urls. - """ - if self.request.method == 'POST': - try: - answer = self.get_answer() - except OpenSlidesError: - answer = 'no' - if answer == 'no': - url = self.get_url(self.question_url_name, self.question_url, - args=self.get_url_name_args()) - else: - url = self.get_url(self.success_url_name, self.success_url, - args=self.get_url_name_args()) - else: - url = self.get_url(self.question_url_name, self.question_url, - args=self.get_url_name_args()) - return url - - def get_question_message(self): - """ - Returns the question for the delete dialog. - """ - return _('Do you really want to delete %s?') % html_strong(self.get_object()) - - def on_clicked_yes(self): - """ - Deletes the object. - """ - self.get_object().delete() - - def get_final_message(self): - """ - Prints the success message to the user. - """ - return _('%s was successfully deleted.') % html_strong(self.get_object()) - - -class DetailView(PermissionMixin, ExtraContextMixin, SingleObjectMixin, django_views.DetailView): - """ - View to show an model object. - """ - pass - - class PDFView(PermissionMixin, View): """ View to generate an PDF. @@ -630,41 +139,6 @@ class PDFView(PermissionMixin, View): return self.render_to_response(self.get_filename()) -class CSVImportView(FormView): - """ - View for a csv import of some data. - - The attribute import_function might to be a staticmethod. - """ - form_class = CSVImportForm - import_function = None - - def get_import_function(self): - """ - Override this to return a specific function to import data from - a given csv file using some extra kwargs. This function has to - return a three-tuple of strings which are the messages for the - user. - - Example function: - - def my_import(csvfile, **kwargs): - # Parse file and import data - return success_message, warning_message, error_message - """ - if self.import_function is None: - raise NotImplementedError('A CSVImportView must provide an import_function ' - 'attribute or override a get_import_function method.') - return self.import_function - - def form_valid(self, form): - success, warning, error = self.get_import_function()(**form.cleaned_data) - messages.success(self.request, success) - messages.warning(self.request, warning) - messages.error(self.request, error) - return super(CSVImportView, self).form_valid(form) - - class APIView(_APIView): """ The Django Rest framework APIView with improvements for OpenSlides. diff --git a/openslides/utils/widgets.py b/openslides/utils/widgets.py deleted file mode 100644 index 61c662341..000000000 --- a/openslides/utils/widgets.py +++ /dev/null @@ -1,136 +0,0 @@ -from django.core.urlresolvers import reverse -from django.dispatch import Signal -from django.template import RequestContext -from django.template.loader import render_to_string - -from .dispatch import SignalConnectMetaClass - - -class Widget(object, metaclass=SignalConnectMetaClass): - """ - Base class for a widget for the dashboard. - - Every app which wants to add widgets to the dashboard has to create a - widget class subclassing from this base class. The name attribute has to be - set. It has to be unique. The metaclass (SignalConnectMetaClass) does the - rest of the magic. - - For the appearance of the widget there are some attributes and methods - like verbose_name, required_permission, default_column, default_weight, - default_active, template_name, context, icon_css_class, - more_link_pattern_name, stylesheets, javascript_files, - get_verbose_name, check_permission, get_html, get_context_data, - get_icon_css_class, get_url_for_more, get_stylesheets and - get_javascript_files. Most of them are optional. - """ - signal = Signal(providing_args=['request']) - name = None - verbose_name = None - required_permission = None - default_column = 1 - default_weight = 0 - default_active = True - template_name = None - context = None - icon_css_class = None - more_link_pattern_name = None - stylesheets = None - javascript_files = None - - def __init__(self, sender, request, **kwargs): - """ - Initializes the widget instance. This is done when the signal is sent. - - Only the required request argument is used. Because of Django's signal - API, we have to take also a sender argument and wildcard keyword - arguments. But they are not used here. - """ - self.request = request - - def __repr__(self): - return repr(self.get_verbose_name()) - - def __str__(self): - return str(self.get_verbose_name()) - - @classmethod - def get_dispatch_uid(cls): - """ - Returns the name as a unique string for each class. Returns None for - the base class so it will not be connected to the signal. - - This does not follow the example implementation of - SignalConnectMetaClass, so take care here. - """ - return cls.name - - def get_verbose_name(self): - """ - Returns a human readable name of the widget. - """ - return self.verbose_name or self.name.capitalize() - - def check_permission(self): - """ - Returns True if the request user is allowed to see the widget. - """ - return self.required_permission is None or self.request.user.has_perm(self.required_permission) - - def is_active(self): - """ - Returns True if the widget is active to be displayed. - """ - session_widgets = self.request.session.get('widgets', {}) - return session_widgets.get(self.name, self.default_active) - - def get_html(self): - """ - Returns the html code of the widget. - - This method also adds the widget itself to the context. - """ - if self.template_name is not None: - html = render_to_string( - template_name=self.template_name, - dictionary=self.get_context_data(widget=self), - context_instance=RequestContext(self.request)) - else: - raise NotImplementedError('A widget class must define either a get_html ' - 'method or have template_name argument.') - return html - - def get_context_data(self, **context): - """ - Returns the context data for the widget template. - """ - return_context = self.context or {} - return_context.update(context) - return return_context - - def get_icon_css_class(self): - """ - Returns the css class name of the icon. - """ - return self.icon_css_class or 'icon-%s' % self.name - - def get_url_for_more(self): - """ - Returns the url for the link 'More ...' in the base template. - """ - if self.more_link_pattern_name is not None: - url = reverse(self.more_link_pattern_name) - else: - url = None - return url - - def get_stylesheets(self): - """ - Returns an interable of stylesheets to be loaded. - """ - return iter(self.stylesheets or []) - - def get_javascript_files(self): - """ - Returns an interable of javascript files to be loaded. - """ - return iter(self.javascript_files or []) diff --git a/requirements_production.txt b/requirements_production.txt index eb0eccc06..198397f0d 100644 --- a/requirements_production.txt +++ b/requirements_production.txt @@ -4,7 +4,6 @@ beautifulsoup4>=4.1,<4.4 bleach>=1.4,<1.5 django-ckeditor-updated>=4.2.3,<4.4 django-haystack>=2.1,<2.4 -django-mptt>=0.6,<0.7 djangorestframework>=3.0.5,<3.2.0 jsonfield>=0.9.19,<1.1 natsort>=3.2,<3.6 diff --git a/setup.cfg b/setup.cfg index cee566b4a..421791833 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,4 +10,3 @@ max_line_length = 150 [isort] include_trailing_comma = true multi_line_output = 3 -skip = main.py,signals.py,csv_import.py,global_settings.py,test_personal_info.py,tests.py,search_indexes.py,test_list_of_speakers.py,chatbox.py,test_main_menu.py,test_config.py,runserver.py,rest_api.py,settings.py,test_pdf.py,__main__.py,autoupdate.py,pdf.py,widgets.py,models.py,test_csv_import.py,plugins.py,serializers.py,test_csv.py,test_overlays.py,backupdb.py,views.py,test_views.py,urls.py,forms.py,utils.py diff --git a/tests/integration/agenda/test_views.py b/tests/integration/agenda/test_views.py index 7916cb819..79e71eb9d 100644 --- a/tests/integration/agenda/test_views.py +++ b/tests/integration/agenda/test_views.py @@ -1,8 +1,9 @@ import json + from rest_framework.test import APIClient -from openslides.utils.test import TestCase from openslides.agenda.models import Item +from openslides.utils.test import TestCase class AgendaTreeTest(TestCase): diff --git a/tests/integration/config/test_views.py b/tests/integration/config/test_views.py index 39cfc163d..f72bad8a8 100644 --- a/tests/integration/config/test_views.py +++ b/tests/integration/config/test_views.py @@ -1,10 +1,10 @@ from django import forms -from django.dispatch import receiver from django.core.urlresolvers import reverse +from django.dispatch import receiver from rest_framework import status from rest_framework.test import APIClient -from openslides.config.api import config, ConfigCollection, ConfigVariable +from openslides.config.api import ConfigCollection, ConfigVariable, config from openslides.config.signals import config_signal from openslides.utils.test import TestCase diff --git a/tests/integration/core/test_views.py b/tests/integration/core/test_views.py index 671627d0f..4b09e8638 100644 --- a/tests/integration/core/test_views.py +++ b/tests/integration/core/test_views.py @@ -3,8 +3,8 @@ import json from django.core.urlresolvers import reverse from rest_framework import status -from openslides.utils.test import TestCase from openslides.core.models import CustomSlide, Projector +from openslides.utils.test import TestCase class ProjectorAPI(TestCase): diff --git a/tests/integration/motions/test_views.py b/tests/integration/motions/test_views.py index 55998fc78..45891db03 100644 --- a/tests/integration/motions/test_views.py +++ b/tests/integration/motions/test_views.py @@ -1,8 +1,8 @@ from django.test.client import Client -from openslides.utils.test import TestCase -from openslides.motions.models import Motion from openslides.config.api import config +from openslides.motions.models import Motion +from openslides.utils.test import TestCase class AnonymousRequests(TestCase): diff --git a/tests/old/account/__init__.py b/tests/old/account/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/account/test_widgets.py b/tests/old/account/test_widgets.py deleted file mode 100644 index d0b4c53db..000000000 --- a/tests/old/account/test_widgets.py +++ /dev/null @@ -1,101 +0,0 @@ -from unittest import skip - -from django.test.client import Client - -from openslides.config.api import config -from openslides.users.models import User -from openslides.utils.test import TestCase - - -class PersonalInfoWidget(TestCase): - """ - Tests the content of the personal info widget. - """ - def import_agenda(self): - """ - Helper function to make the module agenda optional. - """ - try: - from openslides import agenda - except ImportError: - return False - else: - return agenda - - def import_motion(self): - """ - Helper function to make the module motion optional. - """ - try: - from openslides import motion - except ImportError: - return False - else: - return motion - - def import_assignment(self): - """ - Helper function to make the module assignment optional. - """ - try: - from openslides import assignment - except ImportError: - return False - else: - return assignment - - def setUp(self): - self.user = User.objects.create_user('HansMeiser', 'default') - self.client = Client() - self.client.login(username='HansMeiser', password='default') - - def test_widget_appearance(self): - response = self.client.get('/dashboard/') - self.assertContains(response, 'My personal info', status_code=200) - - @skip - def test_item_list(self): - agenda = self.import_agenda() - if agenda: - item_1 = agenda.models.Item.objects.create(title='My Item Title iw5ohNgee4eiYahb5Eiv') - speaker = agenda.models.Speaker.objects.add(item=item_1, user=self.user) - response = self.client.get('/dashboard/') - self.assertContains(response, 'I am on the list of speakers of the following items:', status_code=200) - self.assertContains(response, 'My Item Title iw5ohNgee4eiYahb5Eiv', status_code=200) - speaker.begin_speach() - response = self.client.get('/dashboard/') - self.assertNotContains(response, 'My Item Title iw5ohNgee4eiYahb5Eiv', status_code=200) - - def test_submitter_list(self): - motion = self.import_motion() - if motion: - motion_1 = motion.models.Motion.objects.create(title='My Motion Title pa8aeNohYai0ahge', text='My Motion Text') - motion_2 = motion.models.Motion.objects.create(title='My Motion Title quielohL7vah1weochai', text='My Motion Text') - motion.models.MotionSubmitter.objects.create(motion=motion_1, person=self.user) - motion.models.MotionSubmitter.objects.create(motion=motion_2, person=self.user) - response = self.client.get('/dashboard/') - self.assertContains(response, 'I submitted the following motions:', status_code=200) - self.assertContains(response, 'My Motion Title pa8aeNohYai0ahge', status_code=200) - self.assertContains(response, 'My Motion Title quielohL7vah1weochai', status_code=200) - - def test_supporter_list(self): - motion = self.import_motion() - if motion: - motion_1 = motion.models.Motion.objects.create(title='My Motion Title jahN9phaiThae5ooKubu', text='My Motion Text') - motion_2 = motion.models.Motion.objects.create(title='My Motion Title vech9ash8aeh9eej2Ga2', text='My Motion Text') - motion.models.MotionSupporter.objects.create(motion=motion_1, person=self.user) - motion.models.MotionSupporter.objects.create(motion=motion_2, person=self.user) - config['motion_min_supporters'] = 1 - response = self.client.get('/dashboard/') - self.assertContains(response, 'I support the following motions:', status_code=200) - self.assertContains(response, 'My Motion Title jahN9phaiThae5ooKubu', status_code=200) - self.assertContains(response, 'My Motion Title vech9ash8aeh9eej2Ga2', status_code=200) - - def test_candidate_list(self): - assignment = self.import_assignment() - if assignment: - assignment_1 = assignment.models.Assignment.objects.create(title='Hausmeister ooKoh7roApoo3phe', open_posts=1) - assignment_1.set_candidate(self.user) - response = self.client.get('/dashboard/') - self.assertContains(response, 'I am candidate for the following elections:', status_code=200) - self.assertContains(response, 'Hausmeister ooKoh7roApoo3phe', status_code=200) diff --git a/tests/old/agenda/test_list_of_speakers.py b/tests/old/agenda/test_list_of_speakers.py index 9d7668a1d..c6dac3de0 100644 --- a/tests/old/agenda/test_list_of_speakers.py +++ b/tests/old/agenda/test_list_of_speakers.py @@ -1,19 +1,8 @@ -from unittest import skip -from unittest.mock import patch, MagicMock - -from django.contrib.auth.models import Permission -from django.test.client import Client - from openslides.agenda.models import Item, Speaker -from openslides.agenda.signals import agenda_list_of_speakers -from openslides.config.api import config from openslides.users.models import User -from openslides.projector.api import set_active_slide, register_slide_model from openslides.utils.exceptions import OpenSlidesError from openslides.utils.test import TestCase -from .models import RelatedItem - class ListOfSpeakerModelTests(TestCase): def setUp(self): @@ -72,334 +61,3 @@ class ListOfSpeakerModelTests(TestCase): speaker2_item1.begin_speach() self.assertIsNotNone(Speaker.objects.get(user=self.speaker1, item=self.item1).end_time) self.assertIsNotNone(speaker2_item1.begin_time) - - def test_speach_coupled_with_countdown(self): - config['agenda_couple_countdown_and_speakers'] = True - speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1) - self.item1.is_active_slide = MagicMock(return_value=True) - - speaker1_item1.begin_speach() - self.assertEqual(config['countdown_state'], 'active') - - speaker1_item1.end_speach() - self.assertEqual(config['countdown_state'], 'paused') - - def test_begin_speach_not_coupled_with_countdown(self): - config['agenda_couple_countdown_and_speakers'] = False - speaker1_item1 = Speaker.objects.add(self.speaker1, self.item1) - - speaker1_item1.begin_speach() - self.assertEqual(config['countdown_state'], 'inactive') - - config['countdown_state'] = 'active' - speaker1_item1.end_speach() - self.assertEqual(config['countdown_state'], 'active') - - -class SpeakerViewTestCase(TestCase): - def setUp(self): - # Admin - self.admin = User.objects.get(pk=1) - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - # Speaker1 - self.speaker1 = User.objects.create_user('speaker1', 'speaker') - self.speaker1_client = Client() - self.speaker1_client.login(username='speaker1', password='speaker') - - # Speaker2 - self.speaker2 = User.objects.create_user('speaker2', 'speaker') - self.speaker2_client = Client() - self.speaker2_client.login(username='speaker2', password='speaker') - - # Items - self.item1 = Item.objects.create(title='item1') - self.item2 = Item.objects.create(title='item2') - - def check_url(self, url, test_client, response_code): - response = test_client.get(url) - self.assertEqual(response.status_code, response_code) - return response - - def assertMessage(self, response, message): - self.assertTrue(message in response.cookies['messages'].value, - '"%s" is not a message of the response. (But: %s)' - % (message, response.cookies['messages'].value)) - - -class TestSpeakerAppendView(SpeakerViewTestCase): - @skip - def test_get(self): - self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists()) - self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 0) - - # Set speaker1 to item1 - response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302) - self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists()) - self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1) - self.assertMessage(response, 'You were successfully added to the list of speakers.') - - # Try to set speaker 1 to item 1 again - response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302) - self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 1) - self.assertMessage(response, 'speaker1 is already on the list of speakers of item 1.') - - @skip - def test_closed_list(self): - self.item1.speaker_list_closed = True - self.item1.save() - - response = self.check_url('/agenda/1/speaker/', self.speaker1_client, 302) - self.assertEqual(Speaker.objects.filter(item=self.item1).count(), 0) - self.assertMessage(response, 'The list of speakers is closed.') - - -class TestAgendaItemView(SpeakerViewTestCase): - @skip - def test_post(self): - # Set speaker1 to item1 - response = self.admin_client.post( - '/agenda/1/', {'speaker': self.speaker1.id}) - self.assertTrue(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists()) - - # Try it again - response = self.admin_client.post( - '/agenda/1/', {'speaker': self.speaker1.id}) - self.assertFormError(response, 'form', 'speaker', 'speaker1 is already on the list of speakers.') - - -class TestSpeakerDeleteView(SpeakerViewTestCase): - @skip - def test_get(self): - self.check_url('/agenda/1/speaker/del/', self.speaker1_client, 302) - - @skip - def test_post_as_admin(self): - speaker = Speaker.objects.add(self.speaker1, self.item1) - - response = self.admin_client.post( - '/agenda/1/speaker/%d/del/' % speaker.pk, {'yes': 'yes'}) - self.assertEqual(response.status_code, 302) - self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists()) - - @skip - def test_post_as_user(self): - Speaker.objects.add(self.speaker1, self.item1) - - response = self.speaker1_client.post( - '/agenda/1/speaker/del/', {'yes': 'yes'}) - self.assertEqual(response.status_code, 302) - self.assertFalse(Speaker.objects.filter(user=self.speaker1, item=self.item1).exists()) - - -class TestSpeakerSpeakView(SpeakerViewTestCase): - @skip - def test_get(self): - url = '/agenda/1/speaker/%s/speak/' % self.speaker1.pk - response = self.check_url(url, self.admin_client, 302) - self.assertMessage(response, '2 is not on the list of item1.') - - speaker = Speaker.objects.add(self.speaker1, self.item1) - response = self.check_url(url, self.admin_client, 302) - speaker = Speaker.objects.get(pk=speaker.pk) - self.assertIsNotNone(speaker.begin_time) - self.assertIsNone(speaker.weight) - - -class TestSpeakerEndSpeachView(SpeakerViewTestCase): - @skip - def test_get(self): - url = '/agenda/1/speaker/end_speach/' - response = self.check_url(url, self.admin_client, 302) - self.assertMessage(response, 'There is no one speaking at the moment according to item1.') - speaker = Speaker.objects.add(self.speaker1, self.item1) - speaker.begin_speach() - response = self.check_url(url, self.admin_client, 302) - speaker = Speaker.objects.get(pk=speaker.pk) - self.assertIsNotNone(speaker.begin_time) - self.assertIsNotNone(speaker.end_time) - self.assertIsNone(speaker.weight) - - -class SpeakerListOpenView(SpeakerViewTestCase): - @skip - def test_get(self): - self.check_url('/agenda/1/speaker/close/', self.admin_client, 302) - item = Item.objects.get(pk=self.item1.pk) - self.assertTrue(item.speaker_list_closed) - - self.check_url('/agenda/1/speaker/reopen/', self.admin_client, 302) - item = Item.objects.get(pk=self.item1.pk) - self.assertFalse(item.speaker_list_closed) - - -class GlobalListOfSpeakersLinks(SpeakerViewTestCase): - @skip - def test_global_redirect_url(self): - response = self.speaker1_client.get('/agenda/list_of_speakers/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') - - set_active_slide('agenda', pk=1) - response = self.speaker1_client.get('/agenda/list_of_speakers/') - self.assertRedirects(response, '/agenda/1/') - - @skip - def test_global_add_url(self): - response = self.speaker1_client.get('/agenda/list_of_speakers/add/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') - - set_active_slide('agenda', pk=1) - response = self.speaker1_client.get('/agenda/list_of_speakers/add/') - self.assertRedirects(response, '/agenda/1/') - self.assertEqual(Speaker.objects.get(item__pk='1').user, self.speaker1) - self.assertMessage(response, 'You were successfully added to the list of speakers.') - - perm = Permission.objects.filter(name='Can see agenda').get() - self.speaker2.groups.model.objects.get(name='Registered').permissions.remove(perm) - response = self.speaker2_client.get('/agenda/list_of_speakers/add/') - self.assertMessage(response, 'You were successfully added to the list of speakers.') - - @patch('openslides.projector.api.slide_callback', {}) - @patch('openslides.projector.api.slide_model', {}) - @skip - def test_next_speaker_on_related_item(self): - """ - Test to add a speaker on a related item. - - The patching of slide_callback and slide_model is needed to cleanup the - call of register_slide_model after the test is run. - """ - register_slide_model(RelatedItem, 'some/template.html') - related_item = RelatedItem.objects.create() - agenda_item = Item.objects.create(content_object=related_item) - config['projector_active_slide'] = {'callback': 'test_related_item', 'pk': 1} - response = self.speaker1_client.get('/agenda/list_of_speakers/add/') - - self.assertRedirects(response, '/agenda/%d/' % agenda_item.pk) - self.assertEqual(Speaker.objects.get(item__pk=agenda_item.pk).user, self.speaker1) - self.assertMessage(response, 'You were successfully added to the list of speakers.') - - @skip - def test_global_next_speaker_url(self): - response = self.admin_client.get('/agenda/list_of_speakers/next/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') - - set_active_slide('agenda', pk=1) - response = self.admin_client.get('/agenda/list_of_speakers/next/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'The list of speakers is empty.') - - response = self.speaker1_client.get('/agenda/list_of_speakers/add/') - self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is None) - response = self.admin_client.get('/agenda/list_of_speakers/next/') - self.assertRedirects(response, '/dashboard/') - self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is not None) - - @skip - def test_global_end_speach_url(self): - response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no list of speakers for the current slide. Please choose the agenda item manually from the agenda.') - - set_active_slide('agenda', pk=1) - response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no one speaking at the moment.') - - response = self.speaker1_client.get('/agenda/list_of_speakers/add/') - self.assertTrue(Speaker.objects.get(item__pk='1').begin_time is None) - response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') - self.assertRedirects(response, '/dashboard/') - self.assertMessage(response, 'There is no one speaking at the moment.') - - response = self.admin_client.get('/agenda/list_of_speakers/next/') - self.assertTrue(Speaker.objects.get(item__pk='1').end_time is None) - response = self.admin_client.get('/agenda/list_of_speakers/end_speach/') - self.assertRedirects(response, '/dashboard/') - self.assertTrue(Speaker.objects.get(item__pk='1').end_time is not None) - - -class TestOverlay(TestCase): - def test_overlay_with_no_model_slide(self): - """ - When a slide is active, that is not a model (for example the agenda) - an Attribute Error was raised. - """ - config['projector_active_slide'] = {'callback': None} - - value = agenda_list_of_speakers(sender='test').get_projector_html() - - self.assertEqual(value, '') - - -class TestCurrentListOfSpeakersOnProjectorView(SpeakerViewTestCase): - """ - Test the view with the current list of speakers depending on the actual - slide. - """ - def test_get_none(self): - response = self.admin_client.get('/agenda/list_of_speakers/projector/') - self.assertContains(response, 'List of speakersNot available') - - def test_get_normal(self): - self.item1.title = 'title_gupooDee8ahahnaxoo2a' - self.item1.save() - Speaker.objects.add(self.speaker1, self.item1) - config['projector_active_slide'] = {'callback': 'agenda', 'pk': self.item1.pk} - response = self.admin_client.get('/agenda/list_of_speakers/projector/') - self.assertContains(response, 'List of speakers') - self.assertContains(response, 'title_gupooDee8ahahnaxoo2a') - self.assertContains(response, 'speaker1') - - -class TestSpeakerChangeOrderView(SpeakerViewTestCase): - def setUp(self): - super().setUp() - Speaker.objects.add(self.speaker1, self.item1) - Speaker.objects.add(self.speaker2, self.item1) - - @skip - def test_post(self): - """ - Tests to change the order of two speakers. - """ - data = {'sort_order': 'speaker_2,speaker_1'} - self.admin_client.post('/agenda/1/speaker/change_order/', - data) - - self.assertEqual(Speaker.objects.get(pk=1).weight, 2) - self.assertEqual(Speaker.objects.get(pk=2).weight, 1) - - @skip - def test_invalid_data1(self): - """ - Tests to send invalid data. - - The order should not change. - """ - data = {'sort_order': 'speaker_2,speaker:1'} - response = self.admin_client.post('/agenda/1/speaker/change_order/', - data) - - self.assertEqual(Speaker.objects.get(pk=1).weight, 1) - self.assertEqual(Speaker.objects.get(pk=2).weight, 2) - self.assertMessage(response, 'Could not change order. Invalid data.') - - @skip - def test_invalid_data2(self): - """ - Tests to send a speaker that does not exist. - - The order should not change. - """ - data = {'sort_order': 'speaker_2,speaker_10'} - response = self.admin_client.post('/agenda/1/speaker/change_order/', - data) - - self.assertEqual(Speaker.objects.get(pk=1).weight, 1) - self.assertEqual(Speaker.objects.get(pk=2).weight, 2) - self.assertMessage(response, 'Could not change order. Invalid data.') diff --git a/tests/old/agenda/tests.py b/tests/old/agenda/tests.py deleted file mode 100644 index 36f123a88..000000000 --- a/tests/old/agenda/tests.py +++ /dev/null @@ -1,329 +0,0 @@ -from unittest.mock import patch -from unittest import skip - -from django.contrib.auth.models import Permission -from django.contrib.contenttypes.models import ContentType -from django.core.files.uploadedfile import SimpleUploadedFile -from django.test.client import Client - -from openslides.agenda.models import Item -from openslides.agenda.slides import agenda_slide -from openslides.users.models import User -from openslides.utils.test import TestCase - - -class ViewTest(TestCase): - def setUp(self): - self.item1 = Item.objects.create(title='item1') - self.item2 = Item.objects.create(title='item2') - self.refreshItems() - - self.admin = User.objects.get(pk=1) - self.anonym = User.objects.create_user('testanonym', 'default') - - def refreshItems(self): - self.item1 = Item.objects.get(pk=self.item1.id) - self.item2 = Item.objects.get(pk=self.item2.id) - - @property - def adminClient(self): - c = Client() - c.login(username='admin', password='admin') - return c - - @property - def anonymClient(self): - return Client() - - @skip - def testOverview(self): - c = self.adminClient - - response = c.get('/agenda/') - self.assertEqual(response.status_code, 200) - self.assertEqual(len(response.context['items']), len(Item.objects.all())) - - @skip - def testClose(self): - c = self.adminClient - - response = c.get('/agenda/%d/close/' % self.item1.id) - self.refreshItems() - self.assertEqual(response.status_code, 302) - self.assertTrue(Item.objects.get(pk=self.item1.id).closed) - - response = c.get('/agenda/%d/open/' % self.item1.id) - self.refreshItems() - self.assertEqual(response.status_code, 302) - self.assertFalse(self.item1.closed) - - response = c.get('/agenda/%d/open/' % 1000) - self.refreshItems() - self.assertEqual(response.status_code, 404) - - # Test ajax - response = c.get('/agenda/%d/close/' % self.item1.id, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(response.status_code, 200) - response = c.get('/agenda/%d/open/' % self.item1.id, - HTTP_X_REQUESTED_WITH='XMLHttpRequest') - self.assertEqual(response.status_code, 200) - - @skip - def testEdit(self): - c = self.adminClient - - response = c.get('/agenda/%d/edit/' % self.item1.id) - self.assertEqual(response.status_code, 200) - - response = c.get('/agenda/%d/edit/' % 1000) - self.assertEqual(response.status_code, 404) - - data = {'title': 'newitem1', 'text': 'item1-text', 'weight': '0', - 'type': 1} - response = c.post('/agenda/%d/edit/' % self.item1.id, data) - self.assertEqual(response.status_code, 302) - self.refreshItems() - self.assertEqual(self.item1.title, 'newitem1') - self.assertEqual(self.item1.text, 'item1-text') - - data = {'title': '', 'text': 'item1-text', 'weight': '0'} - response = c.post('/agenda/%d/edit/' % self.item1.id, data) - self.assertEqual(response.status_code, 200) - self.refreshItems() - self.assertEqual(self.item1.title, 'newitem1') - - @skip - def test_view(self): - item = Item.objects.create(title='quai5OTeephaequ0xei0') - c = self.adminClient - response = c.get('/agenda/%s/' % item.id) - self.assertContains(response, 'quai5OTeephaequ0xei0') - self.assertTemplateUsed(response, 'agenda/view.html') - # Test it twice for former error in the template - response = c.get('/agenda/%s/' % item.id) - self.assertContains(response, 'quai5OTeephaequ0xei0') - - @skip - def test_change_item_order(self): - data = { - 'i1-self': 1, - 'i1-weight': 50, - 'i1-parent': 0, - 'i2-self': 2, - 'i2-weight': 50, - 'i2-parent': 1} - response = self.adminClient.post('/agenda/', data) - - # Test values in response. - items = response.context['items'] - self.assertIsNone(items[0].parent) - self.assertEqual(items[1].parent_id, 1) - - # Test values in DB - self.assertIsNone(Item.objects.get(pk=1).parent) - self.assertEqual(Item.objects.get(pk=2).parent_id, 1) - - @skip - def test_change_item_order_with_orga_item(self): - self.item1.type = 2 - self.item1.save() - - data = { - 'i1-self': 1, - 'i1-weight': 50, - 'i1-parent': 0, - 'i2-self': 2, - 'i2-weight': 50, - 'i2-parent': 1} - response = self.adminClient.post('/agenda/', data) - - self.assertNotEqual(Item.objects.get(pk=2).parent_id, 1) - self.assertContains(response, 'Agenda items can not be child elements of an organizational item.') - - def test_change_item_order_with_form_error(self): - """ - Sends invalid data to the view. The expected behavior is to change - nothing. - """ - data = { - 'i1-self': 1, - 'i1-weight': 50, - 'i1-parent': 2, - 'i2-self': 2, - 'i2-weight': "invalid", - 'i2-parent': "invalid"} - - self.adminClient.post('/agenda/', data) - - self.assertIsNone(Item.objects.get(pk=1).parent_id, 0) - self.assertIsNone(Item.objects.get(pk=2).parent_id, 0) - - @skip('Check the tree for integrety in the openslides code') - def test_change_item_order_with_tree_error(self): - """ - Sends invalid data to the view. The expected behavior is to change - nothing. - """ - data = { - 'i1-self': 1, - 'i1-weight': 50, - 'i1-parent': 2, - 'i2-self': 2, - 'i2-weight': 50, - 'i2-parent': 1} - - self.adminClient.post('/agenda/', data) - - self.assertEqual(Item.objects.get(pk=1).parent_id, 0) - self.assertEqual(Item.objects.get(pk=2).parent_id, 0) - - @skip - def test_delete(self): - response = self.adminClient.get('/agenda/%s/del/' % self.item1.pk) - self.assertRedirects(response, '/agenda/') - response = self.adminClient.post('/agenda/%s/del/' % self.item1.pk, {'yes': 1}) - self.assertRedirects(response, '/agenda/') - self.assertFalse(Item.objects.filter(pk=1).exists()) - - @skip - def test_delete_item_with_children(self): - item1 = Item.objects.create(title='item1') - item2 = Item.objects.create(title='item2', parent=item1) - - self.adminClient.post('/agenda/%d/del/' % item1.pk, {'all': 'all'}) - query = Item.objects.filter(pk__in=[item1.pk, item2.pk]) - self.assertFalse(query) - - @skip - def test_delete_item_with_wrong_answer(self): - response = self.adminClient.post( - '/agenda/%s/del/' % self.item1.pk, - {'unknown_answer_aicipohc1Eeph2chaeng': 1}) - self.assertRedirects(response, '/agenda/') - self.assertTrue(Item.objects.filter(pk=self.item1.pk).exists()) - - @skip - def test_orga_item_permission(self): - # Prepare - self.item1.type = Item.ORGANIZATIONAL_ITEM - self.item1.save() - user = User.objects.create_user('testuser_EeBoPh5uyookoowoodii', 'default') - client = Client() - client.login(username='testuser_EeBoPh5uyookoowoodii', password='default') - # Test view with permission - self.assertTrue(user.has_perm('agenda.can_see_orga_items')) - self.assertContains(client.get('/agenda/1/'), 'item1') - # Remove permission - orga_perm = Permission.objects.get( - content_type=ContentType.objects.get_for_model(Item), - codename='can_see_orga_items') - user.groups.model.objects.get(name='Registered').permissions.remove(orga_perm) - # Reload user - user = User.objects.get(username=user.username) - # Test view without permission - self.assertFalse(user.has_perm('agenda.can_see_orga_items')) - response = client.get('/agenda/1/') - self.assertEqual(response.status_code, 403) - response = client.get('/agenda/2/') - self.assertEqual(response.status_code, 200) - - @skip - def test_orga_item_with_orga_parent_one(self): - item1 = Item.objects.create(title='item1_Taeboog1de1sahSeiM8y', type=2) - response = self.adminClient.post( - '/agenda/new/', - {'title': 'item2_faelohD2uK7ohNgeepi2', - 'type': '1', - 'parent': item1.pk}) - self.assertFormError( - response, - 'form', - None, - 'Agenda items can not be child elements of an organizational item.') - - @skip - def test_orga_item_with_orga_parent_two(self): - item1 = Item.objects.create(title='item1_aeNg4Heibee8ULooneep') - Item.objects.create(title='item2_fooshaeroo7Ohvoow0hoo', parent=item1) - response = self.adminClient.post( - '/agenda/%s/edit/' % item1.pk, - {'title': 'item1_aeNg4Heibee8ULooneep_changed', - 'type': '2'}) - self.assertFormError( - response, - 'form', - None, - 'Organizational items can not have agenda items as child elements.') - - @skip - def test_csv_import(self): - """ - Test to upload a csv file. - """ - new_csv_file = SimpleUploadedFile( - name='new_csv_file.csv', - content=bytes('Title,text,duration\nTitle thei5KieK6ohphuilahs,Text Chai1ioWae3ASh0Eloh1,42\n,Bad line\n', 'UTF-8')) - - self.adminClient.post('/agenda/csv_import/', {'csvfile': new_csv_file}) - - self.assertEqual(Item.objects.all().count(), 3) - item = Item.objects.get(pk=3) - self.assertEqual(item.title, 'Title thei5KieK6ohphuilahs') - self.assertEqual(item.text, 'Text Chai1ioWae3ASh0Eloh1') - self.assertEqual(item.duration, '42') - - -class ConfigTest(TestCase): - def setUp(self): - self.client = Client() - self.client.login(username='admin', password='admin') - - def test_config_collection_css_javascript(self): - response = self.client.get('/config/agenda/') - self.assertContains(response, 'timepicker.css', status_code=200) - self.assertContains(response, 'jquery-ui-timepicker-addon.min.js', status_code=200) - - def test_wrong_input(self): - response = self.client.post( - '/config/agenda/', - {'agenda_start_event_date_time': 'wrong_format', - 'agenda_show_last_speakers': '3'}) - self.assertFormError(response, form='form', - field='agenda_start_event_date_time', - errors='Invalid input.') - - -@patch('openslides.agenda.slides.render_to_string') -class SlideTest(TestCase): - """ - Test the agenda slide. - """ - - def setUp(self): - self.item1 = Item.objects.create(title='first slide') - Item.objects.create(title='second slide') - Item.objects.create(title='first child', parent=self.item1) - Item.objects.create(title='second child', parent=self.item1) - - def test_full_agenda_summary(self, mock_render_to_string): - agenda_slide() - self.assertTrue(mock_render_to_string.called) - self.assertEqual(mock_render_to_string.call_args[0][0], 'agenda/item_slide_summary.html') - query = mock_render_to_string.call_args[0][1]['items'] - self.assertEqual(repr(query), repr(Item.objects.filter(pk__in=[1, 2]))) - - def test_item_summary(self, mock_render_to_string): - agenda_slide(type='summary', pk=1) - self.assertTrue(mock_render_to_string.called) - self.assertEqual(mock_render_to_string.call_args[0][0], 'agenda/item_slide_summary.html') - self.assertEqual(mock_render_to_string.call_args[0][1]['title'], self.item1.get_title()) - query = mock_render_to_string.call_args[0][1]['items'] - self.assertEqual(repr(query), repr(Item.objects.filter(pk__in=[3, 4]))) - - def test_normal_slide(self, mock_render_to_string): - agenda_slide(pk=1) - self.assertTrue(mock_render_to_string.called) - self.assertEqual(mock_render_to_string.call_args[0][0], 'agenda/item_slide.html') - item = mock_render_to_string.call_args[0][1]['item'] - self.assertEqual(item, Item.objects.get(pk=1)) diff --git a/tests/old/assignments/test_views.py b/tests/old/assignments/test_views.py deleted file mode 100644 index 8878dc95b..000000000 --- a/tests/old/assignments/test_views.py +++ /dev/null @@ -1,154 +0,0 @@ -from unittest import skip - -from django.test.client import Client - -from openslides.assignments.models import Assignment, AssignmentPoll -from openslides.config.api import config -from openslides.users.models import Group, User -from openslides.utils.test import TestCase - - -class AssignmentViewTestCase(TestCase): - def setUp(self): - # Admin - self.admin = User.objects.get(pk=1) - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - # Staff - self.staff = User.objects.create_user('staff', 'staff') - staff_group = Group.objects.get(name='Staff') - self.staff.groups.add(staff_group) - self.staff.save() - self.staff_client = Client() - self.staff_client.login(username='staff', password='staff') - - # Delegate - self.delegate = User.objects.create_user(username='delegate', password='delegate') - delegate_group = Group.objects.get(name='Delegates') - self.delegate.groups.add(delegate_group) - self.delegate.save() - self.delegate_client = Client() - self.delegate_client.login(username='delegate', password='delegate') - - # Registered - self.registered = User.objects.create_user(username='registered', password='registered') - self.registered_client = Client() - self.registered_client.login(username='registered', password='registered') - - self.assignment1 = Assignment.objects.create(title='test', open_posts=2) - - def check_url(self, url, test_client, response_cose): - response = test_client.get(url) - self.assertEqual(response.status_code, response_cose) - return response - - -class TestAssignmentPollDelete(AssignmentViewTestCase): - def setUp(self): - super(TestAssignmentPollDelete, self).setUp() - self.assignment1.create_poll() - - @skip - def test_get(self): - response = self.check_url('/assignments/poll/1/del/', self.admin_client, 302) - self.assertRedirects(response, 'assignments/1/') - - @skip - def test_post(self): - response = self.admin_client.post('/assignments/poll/1/del/', {'yes': 1}) - self.assertRedirects(response, '/assignments/1/') - - -class TestAssignmentDetailView(AssignmentViewTestCase): - @skip - def test_blocked_candidates_view(self): - """ - Tests that a delegate runs for a vote and then withdraws himself. - """ - response = self.staff_client.get('/assignments/1/') - self.assertContains(response, 'No candidates available.') - self.assertNotContains(response, 'Blocked Candidates') - - response = self.delegate_client.get('/assignments/1/candidate/') - self.assertTrue(self.assignment1.is_candidate(self.delegate)) - self.assertFalse(self.assignment1.is_blocked(self.delegate)) - - response = self.staff_client.get('/assignments/1/') - self.assertNotContains(response, 'No candidates available.') - self.assertNotContains(response, 'Blocked Candidates') - - response = self.delegate_client.get('/assignments/1/delete_candidate/') - self.assertFalse(self.assignment1.is_candidate(self.delegate)) - self.assertTrue(self.assignment1.is_blocked(self.delegate)) - - response = self.staff_client.get('/assignments/1/') - self.assertContains(response, 'No candidates available.') - self.assertContains(response, 'Blocked Candidates') - - -class TestAssignmentPollCreateView(TestCase): - """ - Tests the creation of assignment polls. - """ - def test_assignment_add_candidate(self): - admin = User.objects.get(pk=1) - self.assignment = Assignment.objects.create( - title='test_assignment_oiL2heerookiegeirai0', - open_posts=1) - self.assignment.set_candidate(admin) - self.assertEqual(len(Assignment.objects.get(pk=self.assignment.pk).candidates), 1) - - @skip - def test_assignment_poll_creation(self): - self.test_assignment_add_candidate() - self.assignment.set_phase(self.assignment.PHASE_VOTING) - admin_client = Client() - admin_client.login(username='admin', password='admin') - self.assertFalse(AssignmentPoll.objects.exists()) - self.assertEqual(config['assignment_poll_vote_values'], 'auto') - response = admin_client.get('/assignments/1/create_poll/') - self.assertRedirects(response, '/assignments/1/') - poll = AssignmentPoll.objects.get() - self.assertEqual(poll.assignment, self.assignment) - self.assertEqual(poll.assignmentoption_set.count(), 1) - self.assertTrue(poll.yesnoabstain) - - -class TestAssignmentPollPdfView(TestCase): - """ - Tests the creation of the assignment poll pdf - """ - - def test_assignment_create_poll_pdf(self): - # Create a assignment with a poll - admin = User.objects.get(pk=1) - assignment = Assignment.objects.create(title='assignment1', open_posts=1) - assignment.set_candidate(admin) - assignment.set_phase(assignment.PHASE_VOTING) - assignment.create_poll() - client = Client() - client.login(username='admin', password='admin') - - # request the pdf - response = client.get('/assignments/poll/1/print/') - - # test the response - self.assertEqual(response.status_code, 200) - - -class TestPollUpdateView(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - def test_not_existing_poll(self): - """ - Tests that a 404 is returned, when a non existing poll is requested. - """ - Assignment.objects.create(title='test assignment', open_posts=1) - url = '/assignments/poll/1/edit/' - - response = self.admin_client.get(url) - - self.assertTrue(response.status_code, '404') diff --git a/tests/old/config/test_config.py b/tests/old/config/test_config.py index 0b754af5a..33341d20d 100644 --- a/tests/old/config/test_config.py +++ b/tests/old/config/test_config.py @@ -1,15 +1,16 @@ -import re -from unittest.mock import patch - from django import forms from django.contrib.auth.models import Permission from django.contrib.contenttypes.models import ContentType from django.dispatch import receiver from django.test.client import Client - -from openslides.config.api import (config, ConfigCollection, ConfigGroup, - ConfigGroupedCollection, ConfigVariable) +from openslides.config.api import ( + ConfigCollection, + ConfigGroup, + ConfigGroupedCollection, + ConfigVariable, + config, +) from openslides.config.exceptions import ConfigError, ConfigNotFound from openslides.config.signals import config_signal from openslides.users.models import User @@ -97,145 +98,6 @@ class HandleConfigTest(TestCase): 'unknown_var') -class ConfigFormTest(TestCase): - - def setUp(self): - # Setup the permission - ct = ContentType.objects.get(app_label='config', model='configstore') - perm = Permission.objects.get(content_type=ct, codename='can_manage') - - # Setup two users - self.manager = User.objects.create_user('config_test_manager', 'default') - self.manager.user_permissions.add(perm) - - self.normal_user = User.objects.create_user('config_test_normal_user', 'default') - - # Login - self.client_manager = Client() - self.client_manager.login(username='config_test_manager', password='default') - self.client_normal_user = Client() - self.client_normal_user.login(username='config_test_normal_user', password='default') - - def test_get_config_form_overview(self): - response = self.client_manager.get('/config/') - self.assertRedirects(response=response, expected_url='/config/general/', - status_code=302, target_status_code=200) - - def test_get_config_form_testgroupedpage1_manager_client(self): - response = self.client_manager.get('/config/testgroupedpage1/') - self.assertContains(response=response, text='default_string_rien4ooCZieng6ah', status_code=200) - self.assertTemplateUsed(response=response, template_name='base.html') - self.assertTemplateUsed(response=response, template_name='config/config_form.html') - self.assertTemplateNotUsed(response=response, template_name='form.html') - self.assertTemplateUsed(response=response, template_name='formbuttons_save.html') - - def test_get_config_form_testgroupedpage1_grouping(self): - response = self.client_manager.get('/config/testgroupedpage1/') - self.assertContains(response=response, text='Group 1 aiYeix2mCieQuae3', status_code=200) - self.assertContains(response=response, text='Group 2 Toongai7ahyahy7B', status_code=200) - - def test_get_config_form_testgroupedpage1_other_clients(self): - response = self.client_normal_user.get('/config/testgroupedpage1/') - self.assertEqual(response.status_code, 403) - - def test_get_config_form_testsimplepage1_manager_client(self): - response = self.client_manager.get('/config/testsimplepage1/') - self.assertNotContains(response=response, text='BaeB0ahcMae3feem', status_code=200) - self.assertTemplateUsed(response=response, template_name='base.html') - self.assertTemplateUsed(response=response, template_name='config/config_form.html') - self.assertTemplateUsed(response=response, template_name='form.html') - self.assertTemplateUsed(response=response, template_name='formbuttons_save.html') - - def test_get_config_form_testgroupedpage1_initial(self): - config['string_var'] = 'something unique AChie6eeiDie3Ieciy1bah4I' - response = self.client_manager.get('/config/testgroupedpage1/') - self.assertContains(response=response, text='AChie6eeiDie3Ieciy1bah4I', status_code=200) - - def test_get_config_form_testgroupedpage1_choices(self): - response = self.client_manager.get('/config/testgroupedpage1/') - self.assertContains(response=response, text='Ughoch4ocoche6Ee', status_code=200) - self.assertContains(response=response, text='Vahnoh5yalohv5Eb', status_code=200) - - def test_post_config_form_configtest1(self): - response = self.client_manager.post( - '/config/testgroupedpage1/', - {'string_var': 'other_special_unique_string faiPaid4utie6eeL', - 'integer_var': 3, - 'choices_var': 2}) - self.assertRedirects(response=response, expected_url='/config/testgroupedpage1/', - status_code=302, target_status_code=200) - self.assertEqual(config['string_var'], 'other_special_unique_string faiPaid4utie6eeL') - self.assertFalse(config['bool_var']) - self.assertEqual(config['integer_var'], 3) - self.assertEqual(config['choices_var'], '2') - - def test_post_config_form_error(self): - response = self.client_manager.post( - '/config/testgroupedpage1/', - {'integer_var': 'bad_string_value'}) - self.assertContains(response=response, text='errorlist', status_code=200) - - def test_disabled_config_view(self): - response = self.client_manager.get('/config/testsimplepage3/') - self.assertEqual(response.status_code, 404) - response = self.client_manager.get('/config/testgroupedpage1/') - self.assertNotContains(response=response, text='Ho5iengaoon5Hoht', status_code=200) - - def test_improperly_configured_config_view(self): - """ - Tests that a ConfigCollection object without an url raises ConfigError - when is_shown() is called. - """ - collection = ConfigCollection( - title='Only a small title but no url ci6xahb8Chula0Thesho', - variables=(ConfigVariable(name='some_var_paiji9theiW8ooXivae6', - default_value='', - form_field=forms.CharField()),)) - - self.assertRaisesMessage( - ConfigError, - 'The config collection %s must have a title and an url attribute.' % repr(collection), - collection.is_shown) - - def test_improperly_configured_config_view_two(self): - """ - Tests that a ConfigCollection object without a title raises ConfigError - when is_shown() is called. - """ - collection = ConfigCollection( - url='only_url_ureiraeY1Oochuad7xei', - variables=(ConfigVariable(name='some_var_vuuC6eiXeiyae3ik4gie', - default_value='', - form_field=forms.CharField()),)) - - self.assertRaisesMessage( - ConfigError, - 'The config collection %s must have a title and an url attribute.' % repr(collection), - collection.is_shown) - - def test_extra_stylefiles(self): - response = self.client_manager.get('/config/testgroupedpage1/') - text = '' - self.assertContains(response=response, text=text, status_code=200) - - def test_extra_javascript(self): - response = self.client_manager.get('/config/testgroupedpage1/') - text = '' - self.assertContains(response=response, text=text, status_code=200) - - @patch('openslides.config.views.FormView.get_context_data') - def test_extra_stylefiles_other_context(self, mock_get_context_data): - """ - Tests the view with empty context data at the beginning. - """ - mock_get_context_data.return_value = {} - response = self.client_manager.get('/config/testgroupedpage1/') - text1 = '' - text2 = '' - self.assertContains(response=response, text=text1, status_code=200) - self.assertContains(response=response, text=text2, status_code=200) - - class ConfigWeightTest(TestCase): def setUp(self): @@ -257,13 +119,6 @@ class ConfigWeightTest(TestCase): config_collection_dict[signal_receiver.__name__] = config_collection self.assertGreater(config_collection_dict['set_grouped_config_view'].weight, config_collection_dict['set_simple_config_view'].weight) - def test_order_of_config_collections_on_view(self): - response = self.client_manager.get('/config/testgroupedpage1/') - content = response.content.decode('utf-8') - m1 = re.search('\s*Config vars for testing 1\s*', content) - m2 = re.search('\s*Config vars for testing 2\s*', content) - self.assertGreater(m1.start(), m2.start()) - @receiver(config_signal, dispatch_uid='set_grouped_config_view_for_testing') def set_grouped_config_view(sender, **kwargs): diff --git a/tests/old/core/__init__.py b/tests/old/core/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/core/test_template_tags_filters.py b/tests/old/core/test_template_tags_filters.py deleted file mode 100644 index a45412a42..000000000 --- a/tests/old/core/test_template_tags_filters.py +++ /dev/null @@ -1,46 +0,0 @@ -from django.dispatch import receiver -from django.template import Context, Template - -from openslides.config.api import ConfigCollection, ConfigVariable, config -from openslides.config.signals import config_signal -from openslides.utils.test import TestCase - - -class ConfigTagAndFilter(TestCase): - def test_config_tag(self): - config['taiNg3reQuooGha4'] = 'iWoor0caThieK7yi' - template = Template("{% load tags %} The config var is {% get_config 'taiNg3reQuooGha4' %}.") - self.assertTrue('The config var is iWoor0caThieK7yi.' in template.render(Context({}))) - - def test_config_filter(self): - config['fkjTze56ncuejWqs'] = 'REG56Hnmfk9TdfsD' - template = Template("{% load tags %} The config var is {{ 'fkjTze56ncuejWqs'|get_config }}.") - self.assertTrue('The config var is REG56Hnmfk9TdfsD.' in template.render(Context({}))) - - def test_both_in_one(self): - config['jfhsnezfh452w6Fg'] = True - config['sdmvldkfgj4534gk'] = 'FdgfkR04jtg9f8bq' - template_code = """{% load tags %} - {% if 'jfhsnezfh452w6Fg'|get_config %} - {% get_config 'sdmvldkfgj4534gk' %} - {% else %} - bad_e0fvkfHFD - {% endif %}""" - template = Template(template_code) - self.assertTrue('FdgfkR04jtg9f8bq' in template.render(Context({}))) - self.assertFalse('bad_e0fvkfHFD' in template.render(Context({}))) - - -@receiver(config_signal, dispatch_uid='set_simple_config_view_template_tag_test') -def set_simple_config_view_template_tag_test(sender, **kwargs): - """ - Sets a simple config view with some config variables but without - grouping. - """ - return ConfigCollection( - title='Config vars for testing with template tag', - url='testsimplepagetemplatetag', - variables=(ConfigVariable(name='taiNg3reQuooGha4', default_value=None), - ConfigVariable(name='fkjTze56ncuejWqs', default_value=None), - ConfigVariable(name='jfhsnezfh452w6Fg', default_value=None), - ConfigVariable(name='sdmvldkfgj4534gk', default_value=None))) diff --git a/tests/old/core/test_views.py b/tests/old/core/test_views.py deleted file mode 100644 index a08953537..000000000 --- a/tests/old/core/test_views.py +++ /dev/null @@ -1,156 +0,0 @@ -from unittest import skip -from unittest.mock import MagicMock, patch - -from django.test.client import Client, RequestFactory - -from openslides import __version__ as openslides_version -from openslides.agenda.models import Item -from openslides.config.api import config -from openslides.core import views -from openslides.core.models import CustomSlide -from openslides.users.models import User -from openslides.utils.test import TestCase - - -class SelectWidgetsViewTest(TestCase): - rf = RequestFactory() - - @patch('openslides.core.views.SelectWidgetsForm') - @patch('openslides.core.views.utils_views.TemplateView.get_context_data') - @patch('openslides.core.views.Widget') - def test_get_context_data(self, mock_Widget, mock_get_context_data, - mock_SelectWidgetsForm): - view = views.SelectWidgetsView() - view.request = self.rf.get('/') - view.request.session = MagicMock() - widget = MagicMock() - widget.name = 'some_widget_Bohsh1Pa0eeziRaihu8O' - widget.is_active.return_value = True - mock_Widget.get_all.return_value = [widget] - mock_get_context_data.return_value = {} - - # Test get - context = view.get_context_data() - self.assertIn('widgets', context) - self.assertIn(widget, context['widgets']) - mock_SelectWidgetsForm.assert_called_with( - prefix='some_widget_Bohsh1Pa0eeziRaihu8O', initial={'widget': True}) - - # Test post - view.request = self.rf.post('/') - view.request.session = MagicMock() - context = view.get_context_data() - mock_SelectWidgetsForm.assert_called_with( - view.request.POST, prefix='some_widget_Bohsh1Pa0eeziRaihu8O', initial={'widget': True}) - - @patch('openslides.core.views.messages') - def test_post(self, mock_messages): - view = views.SelectWidgetsView() - view.request = self.rf.post('/') - view.request.session = {} - widget = MagicMock() - widget.name = 'some_widget_ahgaeree8JeReichue8u' - context = {'widgets': [widget]} - mock_context_data = MagicMock(return_value=context) - - with patch('openslides.core.views.SelectWidgetsView.get_context_data', mock_context_data): - widget.form.is_valid.return_value = True - view.post(view.request) - self.assertIn('some_widget_ahgaeree8JeReichue8u', view.request.session['widgets']) - - # Test with errors in form - widget.form.is_valid.return_value = False - view.request.session = {} - view.post(view.request) - self.assertNotIn('widgets', view.request.session) - mock_messages.error.assert_called_with(view.request, 'There are errors in the form.') - - -class VersionViewTest(TestCase): - def setUp(self): - User.objects.create_user('CoreMaximilian', 'default') - self.client = Client() - self.client.login(username='CoreMaximilian', password='default') - - def test_get(self): - response = self.client.get('/version/') - self.assertContains(response, openslides_version, status_code=200) - - @patch('openslides.core.views.settings') - def test_with_missing_plugin(self, mock_settings): - """ - Tests that a not existing app does not appear on the version view. - """ - mock_settings.INSTALLED_PLUGINS = ('unexisting_app_nvhbkdfgmnsd',) - self.assertRaises(ImportError, self.client.get, '/version/') - - -class SearchViewTest(TestCase): - @skip - def test_simple_search(self): - Item.objects.create(title='agenda_item_bnghfdjkgndkjdfg') - User.objects.create_user('CoreMaximilian', 'default') - self.client = Client() - self.client.login(username='CoreMaximilian', password='default') - response = self.client.get('/search/?q=agenda_item_bnghfd') - text = 'agenda_item_bnghfdjkgndkjdfg' - self.assertContains(response, text) - - def test_anonymous(self): - self.assertFalse(config['system_enable_anonymous']) - self.assertEqual(Client().get('/search/').status_code, 403) - config['system_enable_anonymous'] = True - self.assertEqual(Client().get('/search/').status_code, 200) - - -class CustomSlidesTest(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - def test_create(self): - url = '/customslide/new/' - response = self.admin_client.get(url) - self.assertTemplateUsed(response, 'core/customslide_update.html') - response = self.admin_client.post( - url, - {'title': 'test_title_roo2xi2EibooHie1kohd', 'weight': '0'}) - self.assertRedirects(response, '/dashboard/') - self.assertTrue(CustomSlide.objects.filter( - title='test_title_roo2xi2EibooHie1kohd').exists()) - - def test_update(self): - # Setup - custom_slide = CustomSlide.objects.create(title='test_title_jeeDeB3aedei8ahceeso') - url = '/customslide/%d/edit/' % custom_slide.pk - # Test - response = self.admin_client.get(url) - self.assertTemplateUsed(response, 'core/customslide_update.html') - self.assertContains(response, 'test_title_jeeDeB3aedei8ahceeso') - response = self.admin_client.post( - url, - {'title': 'test_title_ai8Ooboh5bahr6Ee7goo', 'weight': '0'}) - self.assertRedirects(response, '/dashboard/') - self.assertEqual(CustomSlide.objects.get(pk=custom_slide.pk).title, - 'test_title_ai8Ooboh5bahr6Ee7goo') - - -class TagListViewTest(TestCase): - def test_get_tag_queryset(self): - view = views.TagListView() - - with patch('openslides.core.views.Tag') as mock_tag: - view.get_tag_queryset('some_name_with_123', 15) - - self.assertEqual(view.pk, 123) - mock_tag.objects.filter.assert_called_with(pk=123) - - def test_get_tag_queryset_wrong_name(self): - view = views.TagListView() - - with patch('openslides.core.views.Tag'): - with self.assertRaises(views.TagException) as context: - view.get_tag_queryset('some_name_with_', 15) - - self.assertFalse(hasattr(view, 'pk')) - self.assertEqual(str(context.exception), 'Invalid name in request') diff --git a/tests/old/forms/__init__.py b/tests/old/forms/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/forms/test_clean_html.py b/tests/old/forms/test_clean_html.py deleted file mode 100644 index 886cd17fb..000000000 --- a/tests/old/forms/test_clean_html.py +++ /dev/null @@ -1,50 +0,0 @@ -from django import forms - -from openslides.utils.forms import CleanHtmlFormMixin -from openslides.utils.test import TestCase - - -class HtmlTestForm(CleanHtmlFormMixin, forms.Form): - text = forms.CharField() - text2 = forms.CharField() - clean_html_fields = ('text',) - - -class CleanHtmlTest(TestCase): - def clean_html(self, dirty='', clean=False): - form = HtmlTestForm({'text': dirty, 'text2': dirty}) - form.is_valid() - - # No forbidden HTML-tags, nothing should change - if not clean: - self.assertEqual(form.cleaned_data['text'], dirty) - - # Something was removed - else: - self.assertEqual(form.cleaned_data['text'], clean) - - # Field text2 has the same content, but is never passed through the - # HTML-cleanup and should never change - self.assertEqual(form.cleaned_data['text2'], dirty) - - def test_clean_html(self): - """ - Test that the correct HTML tags and attributes are removed - """ - - # Forbidden tags and attributes - self.clean_html('', 'do_evil();') - self.clean_html('evil', 'evil') - self.clean_html('

good?

', '

good?

') - self.clean_html('

Not evil

', '

Not evil

') - self.clean_html('
evil
', 'evil') - self.clean_html('
OK
', 'OK') - self.clean_html('

OK

', '

OK

') - - # Allowed tags and attributes - self.clean_html('good?') - self.clean_html('

OK

') - self.clean_html('

OK

') - self.clean_html('
OK
') - self.clean_html('
  • OK
') - self.clean_html('OK') diff --git a/tests/old/motions/test_csv_import.py b/tests/old/motions/test_csv_import.py deleted file mode 100644 index 2fc0c58d9..000000000 --- a/tests/old/motions/test_csv_import.py +++ /dev/null @@ -1,90 +0,0 @@ -from io import BytesIO -from unittest import skip - -from openslides.motions.models import Category, Motion -from openslides.users.models import User -from openslides.utils.test import TestCase -from openslides.config.api import config - - -class CSVImport(TestCase): - def setUp(self): - # User1 - self.user1 = User.objects.create_user('Admin_ieY0Eereimeimeizuosh', 'eHiK1aiRahxaix0Iequ2') - - # Normal user - self.normal_user = User.objects.create_user('User_CiuNgo4giqueeChie5oi', 'eihi1Eequaek4eagaiKu') - - # Category - Category.objects.create(name='Bildung', prefix='B1') - Category.objects.create(name='Bildung', prefix='B2') - - @skip - def test_example_file_de(self): - # Set config to sort names by first_name because the example csv-file - # expect this. - config['users_sort_users_by_first_name'] = True - special_user = User.objects.create_user(username='Harry_Holland', - password='iegheeChaje7guthie4a', - first_name='Harry', - last_name='Holland') - for i in range(2): - username = 'John_Doe_%d' % i - User.objects.create_user(username=username, - password='default', - first_name='John', - last_name='Doe') - - # csv_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'extras', 'csv-examples') - self.assertEqual(Motion.objects.count(), 0) - # with open(csv_dir + '/motions-demo_de.csv', 'rb') as f: - success_message, warning_message, error_message = None - # TODO: import_motions already deleted - # import_motions(csvfile=f, default_submitter=self.normal_user, - # override=False, importing_person=self.user1) - self.assertEqual(Motion.objects.count(), 11) - - motion1 = Motion.objects.get(pk=1) - self.assertEqual(motion1.identifier, '1') - self.assertEqual(motion1.title, u'Entlastung des Vorstandes') - self.assertEqual(motion1.text, u'Die Versammlung möge beschließen, den Vorstand für seine letzte Legislaturperiode zu entlasten.') - self.assertEqual(motion1.reason, u'Bericht erfolgt mündlich.') - self.assertEqual(len(motion1.submitters.all()), 1) - self.assertEqual(motion1.submitters.all()[0], self.normal_user) - self.assertTrue(motion1.category is None) - self.assertTrue('Submitter unknown.' in warning_message) - self.assertTrue('Category unknown.' in warning_message) - - motion2 = Motion.objects.get(pk=2) - self.assertEqual(motion2.identifier, 'SA 1') - self.assertEqual(motion2.title, u'Satzungsänderung § 2 Abs. 3') - self.assertHTMLEqual(motion2.text, u'''

Die Versammlung möge beschließen, die Satzung in § 2 Abs. 3 wie folgt zu ändern:

-

Es wird vor dem Wort "Zweck" das Wort "gemeinnütziger" eingefügt.

''') - self.assertEqual(motion2.reason, u'Die Änderung der Satzung ist aufgrund der letzten Erfahrungen eine sinnvolle Maßnahme, weil ...') - self.assertEqual(len(motion2.submitters.all()), 1) - self.assertEqual(motion2.submitters.all()[0], special_user) - self.assertEqual(motion2.category.name, u"Satzungsanträge") # category is created automatically - - # check user 'John Doe' - self.assertTrue('Several suitable submitters found.' in warning_message) - # check category 'Bildung' - self.assertTrue('Several suitable categories found.' in warning_message) - - @skip - def test_malformed_file(self): - csv_file = BytesIO() - csv_file.write(bytes('Header\nMalformed data,\n,Title,Text,,,\n', 'utf8')) - success_message, warning_message, error_message = None - # TODO: import_motions already deleted - # import_motions(csvfile=csv_file, default_submitter=self.normal_user.id, override=False) - self.assertEqual(success_message, '') - self.assertTrue('Line is malformed.' in error_message) - - @skip - def test_wrong_encoding(self): - # csv_file = BytesIO(bytes('Müller', 'iso-8859-15')) - success_message, warning_message, error_message = None - # TODO: import_motions already deleted - # import_motions(csvfile=csv_file, default_submitter=self.normal_user.id, override=False) - self.assertEqual(success_message, '') - self.assertIn('Import file has wrong character encoding, only UTF-8 is supported!', error_message) diff --git a/tests/old/motions/test_pdf.py b/tests/old/motions/test_pdf.py index 8c5502af1..322be02de 100644 --- a/tests/old/motions/test_pdf.py +++ b/tests/old/motions/test_pdf.py @@ -1,4 +1,5 @@ from django.test.client import Client + from openslides.motions.models import Motion from openslides.users.models import User from openslides.utils.test import TestCase diff --git a/tests/old/motions/test_views.py b/tests/old/motions/test_views.py deleted file mode 100644 index fc3d0cc02..000000000 --- a/tests/old/motions/test_views.py +++ /dev/null @@ -1,715 +0,0 @@ -import os -import tempfile -from unittest import skip -from unittest.mock import MagicMock - -from django.conf import settings -from django.test import RequestFactory -from django.test.client import Client - -from openslides.config.api import config -from openslides.mediafiles.models import Mediafile -from openslides.motions import views -from openslides.motions.models import Category, Motion, MotionLog, State -from openslides.users.models import Group, User -from openslides.utils.test import TestCase - - -class MotionViewTestCase(TestCase): - def setUp(self): - # Admin - self.admin = User.objects.get(pk=1) - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - # Staff - self.staff = User.objects.create_user('staff', 'staff') - staff_group = Group.objects.get(name='Staff') - self.staff.groups.add(staff_group) - self.staff.save() - self.staff_client = Client() - self.staff_client.login(username='staff', password='staff') - - # Delegate - self.delegate = User.objects.create_user('delegate', 'delegate') - delegate_group = Group.objects.get(name='Delegates') - self.delegate.groups.add(delegate_group) - self.delegate.save() - self.delegate_client = Client() - self.delegate_client.login(username='delegate', password='delegate') - - # Registered - self.registered = User.objects.create_user('registered', 'registered') - self.registered_client = Client() - self.registered_client.login(username='registered', password='registered') - - self.motion1 = Motion.objects.create(title='motion1') - self.motion2 = Motion.objects.create(title='motion2') - - def check_url(self, url, test_client, response_cose): - response = test_client.get(url) - self.assertEqual(response.status_code, response_cose) - return response - - -class TestMotionListView(MotionViewTestCase): - @skip - def test_get(self): - self.check_url('/motions/', self.admin_client, 200) - - @skip - def test_get_with_motion(self): - self.motion1.title = 'motion1_iozaixeeDuMah8sheGhe' - self.motion1.save() - response = self.admin_client.get('/motions/') - self.assertContains(response, 'motion1_iozaixeeDuMah8sheGhe') - - @skip - def test_get_with_filtered_motion_list(self): - self.motion1.state.required_permission_to_see = 'motions.can_manage' - self.motion1.state.save() - self.motion1.title = 'motion1_djfplquczyxasvvgdnmbr' - self.motion1.save() - response = self.registered_client.get('/motions/') - self.assertNotContains(response, 'motion1_djfplquczyxasvvgdnmbr') - - -class TestMotionDetailView(MotionViewTestCase): - @skip - def test_get(self): - self.check_url('/motions/1/', self.admin_client, 200) - self.check_url('/motions/2/', self.admin_client, 200) - self.check_url('/motions/500/', self.admin_client, 404) - - @skip - def test_attachment(self): - # Preparation - tmpfile_no, attachment_path = tempfile.mkstemp(prefix='tmp_openslides_test_', dir=settings.MEDIA_ROOT) - os.close(tmpfile_no) - attachment = Mediafile.objects.create(title='TestFile_Neiri4xai4ueseGohzid', mediafile=attachment_path) - self.motion1.attachments.add(attachment) - - # Test appearance - response = self.registered_client.get('/motions/1/') - self.assertContains(response, '

Attachments:

') - self.assertContains(response, 'TestFile_Neiri4xai4ueseGohzid') - - # Test disappearance - attachment.mediafile.delete() - attachment.delete() - response = self.registered_client.get('/motions/1/') - self.assertNotContains(response, '

Attachments:

') - self.assertNotContains(response, 'TestFile_Neiri4xai4ueseGohzid') - - @skip - def test_poll(self): - response = self.staff_client.get('/motions/1/create_poll/') - self.assertRedirects(response, '/motions/1/poll/1/edit/') - response = self.staff_client.post( - '/motions/1/poll/1/edit/', - {'option-1-Yes': '10', - 'pollform-votesvalid': '50'}) - self.assertRedirects(response, '/motions/1/') - response = self.staff_client.get('/motions/1/') - self.assertContains(response, '50 (100') - - @skip - def test_deleted_supporter(self): - config['motion_min_supporters'] = 1 - self.motion1.support(self.registered) - self.assertContains(self.admin_client.get('/motions/1/'), 'registered') - self.registered.delete() - self.assertNotContains(self.admin_client.get('/motions/1/'), 'registered') - - @skip - def test_get_without_required_permission_from_state(self): - self.motion1.state.required_permission_to_see = 'motions.can_manage' - self.motion1.state.save() - self.check_url('/motions/1/', self.admin_client, 200) - self.check_url('/motions/1/', self.registered_client, 403) - self.motion1.set_state(state=State.objects.get(name='permitted')) - self.motion1.save() - self.check_url('/motions/1/', self.registered_client, 200) - - @skip - def test_get_without_required_permission_from_state_but_by_submitter(self): - self.motion1.state.required_permission_to_see = 'motions.can_manage' - self.motion1.state.save() - self.motion1.submitters.add(self.registered) - self.check_url('/motions/1/', self.registered_client, 200) - - -class TestMotionDetailVersionView(MotionViewTestCase): - @skip - def test_get(self): - self.motion1.title = 'AFWEROBjwerGwer' - self.motion1.save(use_version=self.motion1.get_new_version()) - self.check_url('/motions/1/version/1/', self.admin_client, 200) - response = self.check_url('/motions/1/version/2/', self.admin_client, 200) - self.assertContains(response, 'AFWEROBjwerGwer') - self.check_url('/motions/1/version/500/', self.admin_client, 404) - - -class TestMotionVersionDiffView(MotionViewTestCase): - @skip - def test_get_without_required_permission_from_state(self): - self.motion1.reason = 'reason1_bnmkjiutufjbnvcde334' - self.motion1.save() - self.motion1.title = 'motion1_bnvhfzqsgxcyvasfr57t' - self.motion1.save(use_version=self.motion1.get_new_version()) - - response = self.registered_client.get( - '/motions/1/diff/', - {'rev1': '1', 'rev2': '2'}) - self.assertNotContains(response, 'At least one version number is not valid.') - self.assertEqual(response.status_code, 200) - - self.motion1.state.required_permission_to_see = 'motions.can_manage' - self.motion1.state.save() - - response = self.registered_client.get( - '/motions/1/diff/', - {'rev1': '1', 'rev2': '2'}) - self.assertEqual(response.status_code, 403) - - -class TestMotionCreateView(MotionViewTestCase): - url = '/motions/new/' - - @skip - def test_get(self): - self.check_url(self.url, self.admin_client, 200) - - @skip - def test_admin(self): - response = self.admin_client.post(self.url, {'title': 'new motion', - 'text': 'motion text', - 'reason': 'motion reason', - 'workflow': '1'}) - self.assertEqual(response.status_code, 302) - self.assertTrue(Motion.objects.filter(versions__title='new motion').exists()) - - @skip - def test_delegate(self): - response = self.delegate_client.post(self.url, {'title': 'delegate motion', - 'text': 'motion text', - 'reason': 'motion reason', - 'submitter': self.admin.id}) - self.assertEqual(response.status_code, 302) - motion = Motion.objects.get(versions__title='delegate motion') - self.assertTrue(motion.is_submitter(self.delegate)) - - @skip - def test_registered(self): - response = self.registered_client.post(self.url, {'title': 'registered motion', - 'text': 'motion text', - 'reason': 'motion reason', - 'submitter': self.admin.id}) - self.assertEqual(response.status_code, 403) - self.assertFalse(Motion.objects.filter(versions__title='registered motion').exists()) - - @skip - def test_delegate_after_stop_submitting_new_motions(self): - config['motion_stop_submitting'] = True - response = self.delegate_client.get(self.url) - self.assertEqual(response.status_code, 403) - - @skip - def test_delegate_after_stop_submitting_new_motions_overview(self): - config['motion_stop_submitting'] = True - response = self.delegate_client.get('/motions/') - self.assertNotContains(response, 'href="/motions/new/"', status_code=200) - - @skip - def test_staff_after_stop_submitting_new_motions(self): - config['motion_stop_submitting'] = True - response = self.staff_client.get(self.url) - self.assertEqual(response.status_code, 200) - - @skip - def test_staff_after_stop_submitting_new_motions_overview(self): - config['motion_stop_submitting'] = True - response = self.staff_client.get('/motions/') - self.assertContains(response, 'href="/motions/new/"', status_code=200) - - @skip - def test_identifier_not_unique(self): - Motion.objects.create(title='Another motion 3', identifier='uufag5faoX0thahBi8Fo') - config['motion_identifier'] = 'manually' - response = self.admin_client.post(self.url, {'title': 'something', - 'text': 'bar', - 'submitter': self.admin.id, - 'identifier': 'uufag5faoX0thahBi8Fo'}) - self.assertFormError(response, 'form', 'identifier', 'Motion with this Identifier already exists.') - - @skip - def test_empty_text_field(self): - response = self.admin_client.post(self.url, {'title': 'foo', - 'submitter': self.admin.id}) - self.assertFormError(response, 'form', 'text', 'This field is required.') - - @skip - def test_identifier_with_category_prefix(self): - category = Category.objects.create(name='category_oosozieh9eBa9aegujee', prefix='prefix_raiLie6keik6Eikeiphi') - response = self.admin_client.post(self.url, {'title': 'motion io2iez3Iwoh3aengi5hu', - 'text': 'motion text thoiveshoongoNg7ceek', - 'category': 1, - 'workflow': 1}) - self.assertEqual(response.status_code, 302) - motion = Motion.objects.filter(category=category).get() - self.assertEqual(motion.identifier, 'prefix_raiLie6keik6Eikeiphi 1') - - @skip - def test_log(self): - self.assertFalse(MotionLog.objects.all().exists()) - self.admin_client.post(self.url, {'title': 'new motion', - 'text': 'motion text', - 'workflow': 1}) - self.assertEqual(MotionLog.objects.get(pk=1).message_list, ['Motion created']) - - -class TestMotionCreateAmendmentView(MotionViewTestCase): - url = '/motions/1/new_amendment/' - - @skip - def test_get_amendment_active(self): - config['motion_amendments_enabled'] = True - self.check_url(self.url, self.admin_client, 200) - - @skip - def test_get_amendment_inactive(self): - config['motion_amendments_enabled'] = False - self.check_url(self.url, self.admin_client, 404) - - @skip - def test_get_parent_motion(self): - motion = Motion.objects.create(title='Test Motion') - view = views.MotionCreateAmendmentView() - view.request = RequestFactory().get(self.url) - view.kwargs = {'pk': motion.pk} - - self.assertEqual(view.get_parent_motion(), motion) - - @skip - def test_manipulate_object(self): - motion = Motion.objects.create(title='Test Motion') - view = views.MotionCreateAmendmentView() - view.request = RequestFactory().get(self.url) - view.kwargs = {'pk': motion.pk} - view.object = MagicMock() - - view.manipulate_object(MagicMock()) - - self.assertEqual(view.object.parent, motion) - - @skip - def test_get_initial(self): - motion = Motion.objects.create( - title='Test Motion', text='Parent Motion text', reason='test reason') - view = views.MotionCreateAmendmentView() - view.request = MagicMock() - view.kwargs = {'pk': motion.pk} - - self.assertEqual(view.get_initial(), { - 'reason': u'test reason', - 'text': u'Parent Motion text', - 'title': u'Test Motion', - 'category': None, - 'workflow': '1'}) - - @skip - def test_get_initial_with_category(self): - category = Category.objects.create(name='test category') - motion = Motion.objects.create( - title='Test Motion', text='Parent Motion text', reason='test reason', - category=category) - view = views.MotionCreateAmendmentView() - view.request = MagicMock() - view.kwargs = {'pk': motion.pk} - - self.assertEqual(view.get_initial(), { - 'reason': u'test reason', - 'text': u'Parent Motion text', - 'title': u'Test Motion', - 'category': category, - 'workflow': '1'}) - - -class TestMotionUpdateView(MotionViewTestCase): - url = '/motions/1/edit/' - - @skip - def test_get(self): - self.check_url(self.url, self.admin_client, 200) - - @skip - def test_admin(self): - response = self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'motion text', - 'reason': 'motion reason', - 'submitter': self.admin.id, - 'workflow': 1}) - self.assertRedirects(response, '/motions/1/') - motion = Motion.objects.get(pk=1) - self.assertEqual(motion.title, 'new motion_title') - - @skip - def test_delegate(self): - response = self.delegate_client.post(self.url, {'title': 'my title', - 'text': 'motion text', - 'reason': 'motion reason'}) - self.assertEqual(response.status_code, 403) - motion = Motion.objects.get(pk=1) - motion.submitters.add(self.delegate) - response = self.delegate_client.post(self.url, {'title': 'my title', - 'text': 'motion text', - 'reason': 'motion reason'}) - self.assertRedirects(response, '/motions/1/') - motion = Motion.objects.get(pk=1) - self.assertEqual(motion.title, 'my title') - - @skip - def test_versioning(self): - self.assertFalse(self.motion1.state.versioning) - workflow = self.motion1.state.workflow - versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) - self.motion1.state = versioning_state - self.motion1.save() - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertTrue(self.motion1.state.versioning) - - self.assertEqual(motion.versions.count(), 1) - response = self.admin_client.post(self.url, {'title': 'another new motion_title', - 'text': 'another motion text', - 'reason': 'another motion reason', - 'workflow': workflow.pk, - 'submitter': self.admin.id}) - self.assertRedirects(response, '/motions/1/') - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertEqual(motion.versions.count(), 2) - - @skip - def test_disable_versioning(self): - self.assertFalse(self.motion1.state.versioning) - workflow = self.motion1.state.workflow - versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) - self.motion1.state = versioning_state - self.motion1.save() - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertTrue(self.motion1.state.versioning) - - config['motion_allow_disable_versioning'] = True - self.assertEqual(motion.versions.count(), 1) - response = self.admin_client.post(self.url, {'title': 'another new motion_title', - 'text': 'another motion text', - 'reason': 'another motion reason', - 'submitter': self.admin.id, - 'workflow': workflow.pk, - 'disable_versioning': 'true'}) - self.assertRedirects(response, '/motions/1/') - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertEqual(motion.versions.count(), 1) - - @skip - def test_no_versioning_without_new_data(self): - self.assertFalse(self.motion1.state.versioning) - workflow = self.motion1.state.workflow - versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) - self.motion1.state = versioning_state - self.motion1.title = 'Chah4kaaKasiVuishi5x' - self.motion1.text = 'eedieFoothae2iethuo3' - self.motion1.reason = 'ier2laiy1veeGoo0mau2' - self.motion1.save() - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertTrue(self.motion1.state.versioning) - - self.assertEqual(motion.versions.count(), 1) - response = self.admin_client.post(self.url, {'title': 'Chah4kaaKasiVuishi5x', - 'text': 'eedieFoothae2iethuo3', - 'reason': 'ier2laiy1veeGoo0mau2', - 'workflow': workflow.pk, - 'submitter': self.admin.id}) - self.assertRedirects(response, '/motions/1/') - motion = Motion.objects.get(pk=self.motion1.pk) - self.assertEqual(motion.versions.count(), 1) - - @skip - def test_set_another_workflow(self): - self.assertEqual(self.motion1.state.workflow.pk, 1) - response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2', - 'text': 'eequei1Tee1aegeNgee0', - 'submitter': self.admin.id}) - self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1) - response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2', - 'text': 'eequei1Tee1aegeNgee0', - 'submitter': self.admin.id, - 'workflow': 2}) - self.assertRedirects(response, '/motions/1/') - self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2) - - @skip - def test_remove_supporters(self): - # Setup a new motion with one supporter - config['motion_min_supporters'] = 1 - motion = Motion.objects.create(title='cuoPhoX4Baifoxoothi3', text='zee7xei3taediR9loote') - response = self.staff_client.get('/motions/%s/' % motion.id) - self.assertNotContains(response, 'aengeing3quair3fieGi') - - motion.support(self.registered) - self.registered.last_name = 'aengeing3quair3fieGi' - self.registered.save() - response = self.staff_client.get('/motions/%s/' % motion.id) - self.assertContains(response, 'aengeing3quair3fieGi') - - # Check editing by submitter - response = self.delegate_client.post( - '/motions/%s/edit/' % motion.id, - {'title': 'oori4KiaghaeSeuzaim2', - 'text': 'eequei1Tee1aegeNgee0', - 'submitter': self.delegate.id}) - self.assertEqual(response.status_code, 403) - motion.submitters.add(self.delegate) - - # Edit three times, without removal of supporters, with removal and in another state - for i in range(3): - if i == 1: - config['motion_remove_supporters'] = True - response = self.delegate_client.post( - '/motions/%s/edit/' % motion.id, - {'title': 'iezae8reevaT6phiesoa', - 'text': 'Lohjuu1aebewiu2or3oh'}) - self.assertRedirects(response, '/motions/%s/' % motion.id) - if i == 0 or i == 2: - self.assertTrue(self.registered in Motion.objects.get(pk=motion.pk).supporters.all()) - else: - self.assertFalse(self.registered in Motion.objects.get(pk=motion.pk).supporters.all()) - # Preparing the comming (third) run - motion = Motion.objects.get(pk=motion.pk) - motion.support(self.registered) - motion.state = State.objects.create( - name='not_support', - workflow=self.motion1.state.workflow, - allow_submitter_edit=True, - allow_support=False) - motion.save() - - @skip - def test_form_version_content(self): - """ - The content seen in the update view should be the last version - independently from the active_version. - """ - motion = Motion.objects.create(title='test', text='wrowerjlgw') - new_version = motion.get_new_version() - new_version.text = 'tpdfgojwerldkfgertdfg' - motion.save(use_version=new_version) - motion.active_version = motion.versions.all()[0] - motion.save(use_version=False) - self.assertNotEqual(motion.active_version, motion.get_last_version()) - - response = self.admin_client.get('/motions/%s/edit/' % motion.id) - self.assertEqual(response.context['form'].initial['text'], 'tpdfgojwerldkfgertdfg') - - @skip - def test_log(self): - self.assertFalse(MotionLog.objects.all().exists()) - - # Update motion without versioning - self.assertFalse(self.motion1.state.versioning) - self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'motion text', - 'workflow': 2}) - self.assertEqual(MotionLog.objects.get(pk=1).message_list, ['Motion version', ' 1 ', 'updated']) - - # Update motion by creating a new version - self.motion1.set_state(6) # Set to state 'permitted' which has versioning=True - self.assertTrue(self.motion1.state.versioning) - self.motion1.save(use_version=False) - self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'new motion text', - 'workflow': 2}) - self.assertEqual(MotionLog.objects.get(pk=2).message_list, ['Motion version', ' 2 ', 'created']) - - # Update motion with so called 'trivial changes' - config['motion_allow_disable_versioning'] = True - self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'more new motion text', - 'disable_versioning': 'on', - 'workflow': 2}) - self.assertEqual(MotionLog.objects.get(pk=3).message_list, ['Motion version', ' 2 ', 'updated']) - - # Update motion without changes in the version data - self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'more new motion text', - 'workflow': 2}) - self.assertEqual(MotionLog.objects.get(pk=4).message_list, ['Motion version', ' 2 ', 'updated']) - - # Update motion without changes in the version data but also with the 'trivial changes' flag - self.admin_client.post(self.url, {'title': 'new motion_title', - 'text': 'more new motion text', - 'disable_versioning': 'on', - 'workflow': 2}) - self.assertEqual(MotionLog.objects.get(pk=5).message_list, ['Motion version', ' 2 ', 'updated']) - - @skip - def test_attachment_initial(self): - attachment = Mediafile.objects.create(title='test_title_iech1maatahShiecohca') - self.motion1.attachments.add(attachment) - response = self.admin_client.get(self.url) - self.assertContains(response, '' % attachment.title) - self.motion1.attachments.clear() - response = self.admin_client.get(self.url) - self.assertNotContains(response, '' % attachment.title) - - -class TestMotionDeleteView(MotionViewTestCase): - @skip - def test_get(self): - response = self.check_url('/motions/2/del/', self.admin_client, 302) - self.assertRedirects(response, '/motions/2/') - - @skip - def test_admin(self): - response = self.admin_client.post('/motions/2/del/', {'yes': 'yes'}) - self.assertRedirects(response, '/motions/') - - @skip - def test_delegate(self): - response = self.delegate_client.post('/motions/2/del/', {'yes': 'yes'}) - self.assertEqual(response.status_code, 403) - Motion.objects.get(pk=2).submitters.add(self.delegate) - response = self.delegate_client.post('/motions/2/del/', {'yes': 'yes'}) - self.assertEqual(response.status_code, 403) - - -class TestVersionPermitView(MotionViewTestCase): - def setUp(self): - super(TestVersionPermitView, self).setUp() - self.motion1.title = 'new' - self.motion1.save(use_version=self.motion1.get_new_version()) - - @skip - def test_get(self): - response = self.check_url('/motions/1/version/2/permit/', self.admin_client, 302) - self.assertRedirects(response, '/motions/1/version/2/') - - @skip - def test_post(self): - new_version = self.motion1.get_last_version() - response = self.admin_client.post('/motions/1/version/2/permit/', {'yes': 1}) - self.assertRedirects(response, '/motions/1/') - self.assertEqual(self.motion1.get_active_version(), new_version) - - @skip - def test_activate_old_version(self): - new_version = self.motion1.get_last_version() - first_version = self.motion1.versions.order_by('version_number')[0] - - self.motion1.active_version = new_version - self.motion1.save() - self.assertEqual(self.motion1.versions.count(), 2) - self.admin_client.post('/motions/1/version/1/permit/', {'yes': 1}) - self.motion1 = Motion.objects.get(pk=1) - self.assertEqual(self.motion1.active_version, first_version) - self.assertEqual(self.motion1.versions.count(), 2) - - -class TestVersionDeleteView(MotionViewTestCase): - @skip - def test_get(self): - self.motion1.save(use_version=self.motion1.get_new_version(title='new', text='new')) - response = self.check_url('/motions/1/version/1/del/', self.admin_client, 302) - self.assertRedirects(response, '/motions/1/version/1/') - - @skip - def test_post(self): - new_version = self.motion1.get_new_version - self.motion1.save(use_version=new_version(title='new', text='new')) - self.motion1.save(use_version=new_version(title='new2', text='new')) - self.assertEqual(self.motion1.versions.count(), 3) - - response = self.admin_client.post('/motions/1/version/2/del/', {'yes': 1}) - self.assertRedirects(response, '/motions/1/') - self.assertEqual(self.motion1.versions.count(), 2) - - @skip - def test_delete_active_version(self): - self.motion1.save(use_version=self.motion1.get_new_version(title='new_title_yae6Aequaiw5saeb8suG', text='new')) - motion = Motion.objects.all()[0] - self.assertEqual(motion.get_active_version().title, 'new_title_yae6Aequaiw5saeb8suG') - response = self.admin_client.post('/motions/1/version/2/del/', {'yes': 1}) - self.assertEqual(response.status_code, 404) - - -class MotionSetStatusView(MotionViewTestCase): - @skip - def test_set_status(self): - self.assertEqual(self.motion1.state, State.objects.get(name='submitted')) - self.check_url('/motions/1/set_state/4/', self.registered_client, 403) - response = self.staff_client.get('/motions/1/set_state/4/') - self.assertRedirects(response, '/motions/1/') - self.assertEqual(Motion.objects.get(pk=1).state, State.objects.get(name='not decided')) - response = self.staff_client.get('/motions/1/set_state/1/') - self.assertTrue('You can not set the state of the motion to submitted.' in response.cookies['messages'].value) - self.assertRedirects(response, '/motions/1/') - response = self.staff_client.get('/motions/1/set_state/4/') - self.assertTrue('You can not set the state of the motion. It is already done.' in response.cookies['messages'].value) - self.assertRedirects(response, '/motions/1/') - - -class CategoryViewsTest(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - @skip - def test_create(self): - url = '/motions/category/new/' - response = self.admin_client.get(url) - self.assertTemplateUsed(response, 'motions/category_form.html') - response = self.admin_client.post(url, {'name': 'test_title_eingee0hiveeZ6coohoo'}) - self.assertRedirects(response, '/motions/category/') - self.assertTrue(Category.objects.filter(name='test_title_eingee0hiveeZ6coohoo').exists()) - - @skip - def test_update(self): - # Setup - url = '/motions/category/1/edit/' - Category.objects.create(name='test_title_chu6zu3deithae1gooL1') - # Test - response = self.admin_client.get(url) - self.assertTemplateUsed(response, 'motions/category_form.html') - self.assertContains(response, 'test_title_chu6zu3deithae1gooL1') - response = self.admin_client.post(url, {'name': 'test_title_jaiShae1sheingahlee2'}) - self.assertRedirects(response, '/motions/category/') - self.assertEqual(Category.objects.get(pk=1).name, 'test_title_jaiShae1sheingahlee2') - - @skip - def test_delete(self): - # Setup - url = '/motions/category/1/del/' - Category.objects.create(name='test_title_nei1wooHes2aiquuraep') - # Test - response = self.admin_client.get(url) - self.assertRedirects(response, '/motions/category/') - response = self.admin_client.post(url, {'yes': 'true'}) - self.assertRedirects(response, '/motions/category/') - self.assertFalse(Category.objects.exists()) - - -class PollUpdateViewTest(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - @skip - def test_not_existing_poll(self): - """ - Tests that a 404 is returned, when a non existing poll is requested - """ - Motion.objects.create(title='test_motion') - url = '/motions/1/poll/1/edit/' - - response = self.admin_client.get(url) - - self.assertTrue(response.status_code, '404') diff --git a/tests/old/plugin_api/__init__.py b/tests/old/plugin_api/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/plugin_api/test_plugin_api.py b/tests/old/plugin_api/test_plugin_api.py deleted file mode 100644 index 3e0d58ee6..000000000 --- a/tests/old/plugin_api/test_plugin_api.py +++ /dev/null @@ -1,27 +0,0 @@ -from django.test.client import Client -from django.test.utils import override_settings - -from openslides.utils.test import TestCase - - -@override_settings(INSTALLED_PLUGINS=('tests.old.plugin_api.test_plugin_one',)) -class TestPluginOne(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - def test_version_page(self): - response = self.admin_client.get('/version/') - self.assertContains(response, 'Test Plugin ta3Ohmaiquee2phaf9ei') - self.assertContains(response, '(Short description of test plugin Sah9aiQuae5hoocai7ai)') - self.assertContains(response, '– Version test_version_string_MoHonepahfofiree6Iej') - - -@override_settings(INSTALLED_PLUGINS=('tests.old.plugin_api.test_plugin_two',)) -class TestPluginTwo(TestCase): - def test_version_page(self): - admin_client = Client() - admin_client.login(username='admin', password='admin') - response = admin_client.get('/version/') - self.assertContains(response, 'tests.old.plugin_api.test_plugin_two') - self.assertContains(response, '– Version unknown') diff --git a/tests/old/plugin_api/test_plugin_one/__init__.py b/tests/old/plugin_api/test_plugin_one/__init__.py deleted file mode 100644 index ef7772481..000000000 --- a/tests/old/plugin_api/test_plugin_one/__init__.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.conf.urls import patterns, url -from django.views.generic import RedirectView - -__verbose_name__ = 'Test Plugin ta3Ohmaiquee2phaf9ei' -__version__ = 'test_version_string_MoHonepahfofiree6Iej' -__description__ = 'Short description of test plugin Sah9aiQuae5hoocai7ai' - -urlpatterns = patterns( - '', - url(r'^test_plugin_one_url_Eexea4nie1fexaax3oX7/$', - RedirectView.as_view(pattern_name='core_version', permanent=False))) diff --git a/tests/old/plugin_api/test_plugin_two/__init__.py b/tests/old/plugin_api/test_plugin_two/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/projector/__init__.py b/tests/old/projector/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/projector/test_api.py b/tests/old/projector/test_api.py deleted file mode 100644 index 5c8a05b3f..000000000 --- a/tests/old/projector/test_api.py +++ /dev/null @@ -1,142 +0,0 @@ -from unittest.mock import MagicMock, patch - -from openslides.projector import api as projector_api -from openslides.utils.test import TestCase - - -class ApiFunctions(TestCase): - @patch('openslides.projector.api.default_slide') - def test_get_projector_content(self, mock_default_slide): - mock_slide = MagicMock() - mock_slide.return_value = 'slide content' - - with patch.dict('openslides.projector.api.slide_callback', - values={'mock_slide': mock_slide}): - value = projector_api.get_projector_content({'callback': 'mock_slide'}) - self.assertEqual(value, 'slide content') - - projector_api.get_projector_content({'callback': 'unknown_slide'}) - self.assertTrue(mock_default_slide.called) - - with patch('openslides.projector.api.config', - {'projector_active_slide': {'callback': 'mock_slide'}}): - value = projector_api.get_projector_content() - self.assertEqual(value, 'slide content') - - mock_slide.side_effect = projector_api.SlideError - projector_api.get_projector_content({'callback': 'mock_slide'}) - self.assertTrue(mock_default_slide.called) - - @patch('openslides.projector.api.render_to_string') - def test_default_slide(self, mock_render_to_string): - projector_api.default_slide() - mock_render_to_string.assert_called_with('projector/default_slide.html') - - @patch('openslides.projector.api.projector_overlays') - def test_get_overlays(self, mock_projector_overlays): - mock_overlay = MagicMock() - mock_overlay.name = 'mock_overlay' - mock_projector_overlays.send.return_value = ((None, mock_overlay), ) - value = projector_api.get_overlays() - self.assertEqual(value, {'mock_overlay': mock_overlay}) - - @patch('openslides.projector.api.projector_overlays') - def test_get_overlays_inactive(self, mock_projector_overlays): - mock_overlay = MagicMock() - mock_overlay.name = 'mock_overlay_2' - mock_overlay.is_active.return_value = False - mock_projector_overlays.send.return_value = ((None, mock_overlay), ) - value = projector_api.get_overlays(only_active=True) - self.assertNotEqual(value, {'mock_overlay_2': mock_overlay}) - - @patch('openslides.projector.api.get_overlays') - def test_get_projector_overlays_js(self, mock_get_overlays): - overlay = MagicMock() - mock_get_overlays.return_value = {'overlay': overlay} - - # Test with inactive overlay - overlay.is_active.return_value = False - value = projector_api.get_projector_overlays_js() - self.assertEqual(value, []) - - # Test with active overlay without js - overlay.is_active.return_value = True - overlay.get_javascript.return_value = None - value = projector_api.get_projector_overlays_js() - self.assertEqual(value, []) - - # Test with active overlay with js - overlay.get_javascript.return_value = 'some javascript' - value = projector_api.get_projector_overlays_js() - self.assertEqual(value, ['some javascript']) - - def test_register_slide(self): - mock_slide_callback = {} - mock_slide_model = {} - with patch('openslides.projector.api.slide_model', mock_slide_model): - with patch('openslides.projector.api.slide_callback', mock_slide_callback): - projector_api.register_slide('some name', 'some callback') - projector_api.register_slide('other name', 'other callback', 'Model') - self.assertEqual(mock_slide_callback, {'some name': 'some callback', - 'other name': 'other callback'}) - self.assertEqual(mock_slide_model, {'other name': 'Model'}) - - @patch('openslides.projector.api.render_to_string') - @patch('openslides.projector.api.register_slide') - def test_register_slide_model(self, mock_register_slide, mock_render_to_string): - mock_SlideModel = MagicMock() - mock_SlideModel.slide_callback_name = 'mock_callback_name' - mock_SlideModel.DoesNotExist = Exception - mock_slide_object = MagicMock() - mock_slide_object.get_slide_context.return_value = 'some context' - mock_SlideModel.objects.get.return_value = mock_slide_object - - projector_api.register_slide_model(mock_SlideModel, 'some template') - used_args, __ = mock_register_slide.call_args - self.assertEqual(used_args[0], 'mock_callback_name') - self.assertEqual(used_args[2], mock_SlideModel) - - # Test the generated slide function - used_args[1](pk=1) - mock_render_to_string.assert_called_with('some template', 'some context') - - # Test with non existing object - mock_SlideModel.objects.get.side_effect = Exception - self.assertRaises(projector_api.SlideError, used_args[1], pk=1) - - def test_set_active_slide(self): - mock_config = {} - with patch('openslides.projector.api.config', mock_config): - projector_api.set_active_slide('callback_name', some='kwargs') - self.assertEqual(mock_config, - {'projector_active_slide': {'callback': 'callback_name', - 'some': 'kwargs'}}) - - def test_get_active_slide(self): - mock_config = {'projector_active_slide': 'value'} - with patch('openslides.projector.api.config', mock_config): - value = projector_api.get_active_slide() - self.assertEqual(value, 'value') - - def test_get_active_object(self): - mock_Model = MagicMock() - mock_Model.DoesNotExist = Exception - mock_slide_model = {'mock_model': mock_Model} - mock_active_slide = {'callback': 'unknown'} - mock_get_active_slide = MagicMock(return_value=mock_active_slide) - - with patch('openslides.projector.api.get_active_slide', mock_get_active_slide): - with patch('openslides.projector.api.slide_model', mock_slide_model): - # test unknwon slide_callback_name - self.assertIsNone(projector_api.get_active_object()) - - # test unknown object - mock_Model.objects.get.side_effect = Exception - mock_active_slide.update(callback='mock_model', pk=42) - self.assertIsNone(projector_api.get_active_object()) - mock_Model.objects.get.assert_called_with(pk=42) - - # test success - mock_Model.objects.get.side_effect = None - mock_Model.objects.get.return_value = 'success' - self.assertEqual(projector_api.get_active_object(), 'success') diff --git a/tests/old/projector/test_overlays.py b/tests/old/projector/test_overlays.py deleted file mode 100644 index 1638b7789..000000000 --- a/tests/old/projector/test_overlays.py +++ /dev/null @@ -1,19 +0,0 @@ -import warnings - -from unittest.mock import MagicMock - -from openslides.projector.projector import Overlay -from openslides.utils.test import TestCase - - -class OverlayTest(TestCase): - def test_error_in_html(self): - """ - Tests that the method get_projector_html does not raise any errors. - """ - get_projector_html = MagicMock(side_effect=Exception('no good error')) - overlay = Overlay('test_overlay', lambda: 'widget_html', get_projector_html) - - with warnings.catch_warnings(record=True) as warning: - overlay.get_projector_html() - self.assertEqual(str(warning[0].message), 'Exception in overlay "test_overlay": no good error') diff --git a/tests/old/projector/test_signals.py b/tests/old/projector/test_signals.py deleted file mode 100644 index 7e74c094e..000000000 --- a/tests/old/projector/test_signals.py +++ /dev/null @@ -1,19 +0,0 @@ -from openslides.projector.signals import countdown -from openslides.utils.test import TestCase - - -class CountdownTest(TestCase): - def test_order_of_get_projector_js(self): - """ - Tests that the order of the js values is in the right order. Especially - the value 'call' has to come at the end. - """ - overlay = countdown('fake sender') - test_value = overlay.get_javascript() - - self.assertIsInstance(test_value, dict) - self.assertEqual( - list(test_value.keys()), - ['load_file', 'projector_countdown_start', - 'projector_countdown_duration', 'projector_countdown_pause', - 'projector_countdown_state', 'call']) diff --git a/tests/old/projector/test_views.py b/tests/old/projector/test_views.py deleted file mode 100644 index 84201e933..000000000 --- a/tests/old/projector/test_views.py +++ /dev/null @@ -1,124 +0,0 @@ -from django.contrib.auth.models import AnonymousUser -from django.test.client import Client, RequestFactory -from unittest.mock import MagicMock, patch - -from openslides.config.api import config -from openslides.projector import views -from openslides.utils.test import TestCase - - -class ProjectorViewTest(TestCase): - rf = RequestFactory() - - @patch('openslides.projector.views.get_projector_overlays_js') - @patch('openslides.projector.views.get_overlays') - @patch('openslides.projector.views.get_projector_content') - def test_get(self, mock_get_projector_content, mock_get_overlays, - mock_get_projector_overlays_js): - view = views.ProjectorView() - view.request = self.rf.get('/') - view.request.user = AnonymousUser() - - # Test preview - view.kwargs = {'callback': 'slide_callback'} - context = view.get_context_data() - mock_get_projector_content.assert_called_with( - {'callback': 'slide_callback'}) - self.assertFalse(context['reload']) - - # Test live view - view.kwargs = {} - mock_config = {'projector_js_cache': 'js_cache'} - with patch('openslides.projector.views.config', mock_config): - context = view.get_context_data() - mock_get_projector_content.assert_called_with() - mock_get_overlays.assert_called_with(only_active=True) - mock_get_projector_overlays_js.assert_called_with(as_json=True) - self.assertTrue(context['reload']) - self.assertEqual(context['calls'], 'js_cache') - - -class ActivateViewTest(TestCase): - rf = RequestFactory() - - @patch('openslides.projector.views.config') - @patch('openslides.projector.views.set_active_slide') - def test_get(self, mock_set_active_slide, mock_config): - view = views.ActivateView() - view.request = self.rf.get('/?some_key=some_value') - - view.pre_redirect(view.request, callback='some_callback') - - mock_set_active_slide.assert_called_with('some_callback', - **{'some_key': 'some_value'}) - mock_config.get_default.assert_has_calls([]) - self.assertEqual(mock_config.__setitem__.call_count, 0) - - -class ProjectorControllViewTest(TestCase): - @patch('openslides.projector.views.call_on_projector') - def test_bigger(self, mock_call_on_projector): - view = views.ProjectorControllView() - request = True # request is required, but not used in the method - mock_config = MagicMock() - - mock_config_store = {'projector_scale': 5, 'projector_scroll': 5} - - def getter(key): - return mock_config_store[key] - - def setter(key, value): - mock_config_store[key] = value - - mock_config.__getitem__.side_effect = getter - mock_config.__setitem__.side_effect = setter - mock_config.get_default.return_value = 0 - - self.assertRaises(KeyError, view.pre_redirect, request) - with patch('openslides.projector.views.config', mock_config): - view.pre_redirect(request, direction='bigger') - self.assertEqual(mock_config_store['projector_scale'], 6) - mock_call_on_projector.assert_called_with({'scroll': 5, 'scale': 6}) - - view.pre_redirect(request, direction='smaller') - self.assertEqual(mock_config_store['projector_scale'], 5) - mock_call_on_projector.assert_called_with({'scroll': 5, 'scale': 5}) - - view.pre_redirect(request, direction='down') - self.assertEqual(mock_config_store['projector_scroll'], 6) - mock_call_on_projector.assert_called_with({'scroll': 6, 'scale': 5}) - - view.pre_redirect(request, direction='up') - self.assertEqual(mock_config_store['projector_scroll'], 5) - mock_call_on_projector.assert_called_with({'scroll': 5, 'scale': 5}) - - view.pre_redirect(request, direction='clean_scale') - self.assertEqual(mock_config_store['projector_scale'], 0) - mock_call_on_projector.assert_called_with({'scroll': 5, 'scale': 0}) - - view.pre_redirect(request, direction='clean_scroll') - self.assertEqual(mock_config_store['projector_scroll'], 0) - mock_call_on_projector.assert_called_with({'scroll': 0, 'scale': 0}) - - def test_get_ajax_context(self): - view = views.ProjectorControllView() - with patch('openslides.projector.views.config', {'projector_scale': 1, - 'projector_scroll': 2}): - context = view.get_ajax_context() - self.assertEqual(context, {'scale_level': 1, 'scroll_level': 2}) - - -class CountdownControllView(TestCase): - def setUp(self): - self.admin_client = Client() - self.admin_client.login(username='admin', password='admin') - - @patch('openslides.projector.views.reset_countdown') - def test_set_default(self, mock_reset_countdown): - """ - Test, that the url /countdown/set-default/ sets the time for the countdown - and reset the countdown. - """ - self.admin_client.get('/projector/countdown/set-default/', {'countdown_time': 42}) - self.assertEqual(config['countdown_time'], 42) - mock_reset_countdown.assert_called_with() diff --git a/tests/old/settings.py b/tests/old/settings.py index b92724134..eb6f9b9f7 100644 --- a/tests/old/settings.py +++ b/tests/old/settings.py @@ -3,6 +3,7 @@ Settings file for OpenSlides' tests """ import os + from openslides.global_settings import * # noqa # Path to the directory for user specific data files diff --git a/tests/old/users/__init__.py b/tests/old/users/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/old/users/test_csv.py b/tests/old/users/test_csv.py deleted file mode 100644 index 5fe895703..000000000 --- a/tests/old/users/test_csv.py +++ /dev/null @@ -1,23 +0,0 @@ -from io import BytesIO -from textwrap import dedent - -from openslides.utils.test import TestCase -from openslides.users.csv_import import import_users - - -class TestCSVImport(TestCase): - def test_csv_import(self): - # Create CSV-File - csv_file = BytesIO(bytes(dedent(""" - "Title";"First Name";"Last Name";"Gender";"Email";"Group id";"Structure Level";"Committee";"About me";"Comment";"Is active" - ;"Fred";"Nurk";"male";;3;"Australia";;;;1 - ;"Jan";"Jansen";"male";;3;"Belgium";;;;1 - ;"Juan";"Pérez";"male";;3;"Chile";;;;1 - """.strip()), 'utf8')) - - # Import file - success_message, warning_message, error_message = import_users(csvfile=csv_file) - - # Test result - self.assertEqual(warning_message, '') - self.assertEqual(error_message, '') diff --git a/tests/old/utils/test_forms.py b/tests/old/utils/test_forms.py deleted file mode 100644 index ee94c0e23..000000000 --- a/tests/old/utils/test_forms.py +++ /dev/null @@ -1,12 +0,0 @@ -from types import GeneratorType -from unittest.mock import MagicMock - -from openslides.utils.forms import LocalizedModelMultipleChoiceField -from openslides.utils.test import TestCase - - -class TestLocalizedModelMultipleChoiceField(TestCase): - def test_localized_get_choices(self): - test_field = LocalizedModelMultipleChoiceField(queryset=MagicMock()) - - self.assertEqual(type(test_field.choices), GeneratorType) diff --git a/tests/old/utils/test_main_menu.py b/tests/old/utils/test_main_menu.py deleted file mode 100644 index d939a4c49..000000000 --- a/tests/old/utils/test_main_menu.py +++ /dev/null @@ -1,52 +0,0 @@ -from django.contrib.auth.models import AnonymousUser -from django.test.client import RequestFactory - -from openslides.utils.test import TestCase -from openslides.utils.main_menu import MainMenuEntry - - -class MainMenuEntryObject(TestCase): - request_factory = RequestFactory() - - def tearDown(self): - """ - Remove all receivers of the MainMenuEntry-signal, so it is not called in - other tests. - """ - MainMenuEntry.signal.receivers = [] - - def get_entry(self, cls): - request = self.request_factory.get('/') - request.user = AnonymousUser() - for entry in MainMenuEntry.get_all(request): - if type(entry) == cls: - value = entry - break - else: - value = False - return value - - def test_appearance(self): - class TestMenuEntryOne(MainMenuEntry): - pattern_name = 'core_version' - verbose_name = 'Menu entry for testing gae2thooc4che4thaoNo' - - self.assertEqual(str(self.get_entry(TestMenuEntryOne)), 'Menu entry for testing gae2thooc4che4thaoNo') - - def test_missing_verbose_name(self): - class TestMenuEntryBadOne(MainMenuEntry): - pattern_name = 'core_version' - - entry = self.get_entry(TestMenuEntryBadOne) - text = ('The main menu entry class TestMenuEntryBadOne must provide a ' - 'verbose_name attribute or override the __str__ method.') - self.assertRaisesMessage(NotImplementedError, text, str, entry) - - def test_missing_pattern_name(self): - class TestMenuEntryBadTwo(MainMenuEntry): - verbose_name = 'Menu entry for testing ahVeibai1iecaish2aeR' - - entry = self.get_entry(TestMenuEntryBadTwo) - text = ('The main menu entry class TestMenuEntryBadTwo must provide a ' - 'pattern_name attribute or override the get_url method.') - self.assertRaisesMessage(NotImplementedError, text, entry.get_url) diff --git a/tests/old/utils/test_personal_info.py b/tests/old/utils/test_personal_info.py deleted file mode 100644 index d9140dc8a..000000000 --- a/tests/old/utils/test_personal_info.py +++ /dev/null @@ -1,29 +0,0 @@ -from django.contrib.auth.models import AnonymousUser -from django.test.client import RequestFactory - -from openslides.utils.test import TestCase -from openslides.utils.personal_info import PersonalInfo - - -class PersonalInfoObject(TestCase): - def get_infoblock(self, name): - request = RequestFactory().get('/') - request.user = AnonymousUser() - for infoblock in PersonalInfo.get_all(request): - if type(infoblock).__name__ == name: - value = infoblock - break - else: - value = False - return value - - def test_get_queryset(self): - - class TestInfoBlock_cu1Beir1zie5yeitie4e(PersonalInfo): - pass - - infoblock = self.get_infoblock('TestInfoBlock_cu1Beir1zie5yeitie4e') - self.assertRaisesMessage( - NotImplementedError, - 'Your class %s has to define a get_queryset method.' % repr(TestInfoBlock_cu1Beir1zie5yeitie4e), - infoblock.get_queryset) diff --git a/tests/old/utils/test_widgets.py b/tests/old/utils/test_widgets.py deleted file mode 100644 index 3348b07cb..000000000 --- a/tests/old/utils/test_widgets.py +++ /dev/null @@ -1,49 +0,0 @@ -from django.contrib.auth.models import AnonymousUser -from django.test.client import RequestFactory - -from openslides.utils.test import TestCase -from openslides.utils.widgets import Widget - - -class WidgetObject(TestCase): - request_factory = RequestFactory() - - def get_widget(self, name): - request = self.request_factory.get('/') - request.user = AnonymousUser() - for widget in Widget.get_all(request): - if widget.name == name: - value = widget - break - else: - value = False - return value - - def test_connecting_signal(self): - - class TestWidgetOne(Widget): - name = 'test_case_widget_begae7poh1Ahshohfi1r' - - self.assertTrue(self.get_widget('test_case_widget_begae7poh1Ahshohfi1r')) - - def test_not_connecting_signal(self): - - class TestWidgetTwo(Widget): - name = 'test_case_widget_zuRietaewiCii9mahDah' - - @classmethod - def get_dispatch_uid(cls): - return None - - self.assertFalse(self.get_widget('test_case_widget_zuRietaewiCii9mahDah')) - - def test_missing_template(self): - - class TestWidgetThree(Widget): - name = 'test_widget_raiLaiPhahQuahngeer4' - - widget = self.get_widget('test_widget_raiLaiPhahQuahngeer4') - self.assertRaisesMessage( - NotImplementedError, - 'A widget class must define either a get_html method or have template_name argument.', - widget.get_html) diff --git a/tests/settings.py b/tests/settings.py index b92724134..eb6f9b9f7 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -3,6 +3,7 @@ Settings file for OpenSlides' tests """ import os + from openslides.global_settings import * # noqa # Path to the directory for user specific data files diff --git a/tests/unit/agenda/test_models.py b/tests/unit/agenda/test_models.py index 2c6a03fc8..8435ec224 100644 --- a/tests/unit/agenda/test_models.py +++ b/tests/unit/agenda/test_models.py @@ -1,5 +1,5 @@ from unittest import TestCase -from unittest.mock import MagicMock, patch +from unittest.mock import patch from openslides.agenda.models import Item @@ -58,32 +58,3 @@ class ItemTitle(TestCase): with self.assertRaises(NotImplementedError): item.get_title_supplement() - - -@patch('openslides.agenda.models.Item.objects.rebuild') -@patch('openslides.agenda.models.Item.get_children') -class ItemDelete(TestCase): - def test_delete_with_children_is_true(self, get_children, rebuild): - item = Item() - - with patch('builtins.super') as mock_super: - item.delete(with_children=True) - - self.assertFalse(get_children.called) - rebuild.assert_called_once_with() - mock_super().delete.assert_called_once_with() - - def test_delete_with_children_is_false(self, get_children, rebuild): - parent = Item() - item = Item() - item.parent = parent - child_item = MagicMock() - get_children.return_value = [child_item] - - with patch('builtins.super') as mock_super: - item.delete(with_children=False) - - child_item.move_to.assert_called_once_with(item.parent) - child_item.save_assert_called_once_with() - rebuild.assert_called_once_with() - mock_super().delete.assert_called_once_with() diff --git a/tests/unit/core/test_views.py b/tests/unit/core/test_views.py index f146ad8dc..5b24250f8 100644 --- a/tests/unit/core/test_views.py +++ b/tests/unit/core/test_views.py @@ -1,5 +1,5 @@ from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch from openslides.core import views from openslides.utils.rest_api import ValidationError diff --git a/tests/unit/users/test_models.py b/tests/unit/users/test_models.py index ff15a0f95..0f2611d5d 100644 --- a/tests/unit/users/test_models.py +++ b/tests/unit/users/test_models.py @@ -32,60 +32,6 @@ class UserTest(TestCase): "User.get_slide_context returns a wrong context.") -class UserGetAbsoluteUrlTest(TestCase): - def test_get_absolute_url_default(self): - """ - Tests get_absolute_url() with no argument. - - It should return the url for the url-pattern of user_detail. - """ - user = User(pk=5) - - self.assertEqual( - user.get_absolute_url(), - '/users/5/') - - def test_get_absolute_url_detail(self): - """ - Tests get_absolute_url() with 'detail' as argument. - """ - user = User(pk=5) - - url = user.get_absolute_url('detail') - - self.assertEqual( - url, - '/users/5/') - - def test_get_absolute_url_update(self): - """ - Tests get_absolute_url() with 'update' as argument. - """ - user = User(pk=5) - - url = user.get_absolute_url('update') - - self.assertEqual( - url, - '/users/5/edit/') - - def test_get_absolute_url_other(self): - """ - Tests get_absolute_url() with any other argument. - """ - user = User(pk=5) - dummy_argument = MagicMock() - - with patch('builtins.super') as mock_super: - mock_super().get_absolute_url.return_value = 'test url' - url = user.get_absolute_url(dummy_argument) - - self.assertEqual( - url, - 'test url') - mock_super().get_absolute_url.assert_called_once_with(dummy_argument) - - class UserGetFullName(TestCase): def test_get_full_name_with_structure_level_and_title(self): """ diff --git a/tests/unit/utils/test_models.py b/tests/unit/utils/test_models.py deleted file mode 100644 index 3a03266f9..000000000 --- a/tests/unit/utils/test_models.py +++ /dev/null @@ -1,16 +0,0 @@ -from unittest import TestCase - -from openslides.utils.models import AbsoluteUrlMixin - - -class TestAbsoluteUrlMixin(TestCase): - def test_get_absolute_url(self): - """ - Tests, that AbsoluteUrlMixin raises a ValueError if called. - """ - object = AbsoluteUrlMixin() - - with self.assertRaisesRegex( - ValueError, - 'Unknown Link "argument" for model ""'): - object.get_absolute_url('argument') diff --git a/tests/unit/utils/test_utils.py b/tests/unit/utils/test_utils.py deleted file mode 100644 index 772b21238..000000000 --- a/tests/unit/utils/test_utils.py +++ /dev/null @@ -1,14 +0,0 @@ -from unittest import TestCase - -from openslides.utils.utils import html_strong, int_or_none - - -class Test_functions(TestCase): - def test_html_strong(self): - self.assertEqual(html_strong('some text'), 'some text') - - def test_int_or_none(self): - self.assertEqual(int_or_none('5'), 5) - self.assertEqual(int_or_none(5), 5) - self.assertIsNone(int_or_none('text')) - self.assertIsNone(int_or_none(None)) diff --git a/tests/unit/utils/test_views.py b/tests/unit/utils/test_views.py index 180ed59f5..f58f52b64 100644 --- a/tests/unit/utils/test_views.py +++ b/tests/unit/utils/test_views.py @@ -1,26 +1,11 @@ from unittest import TestCase -from unittest.mock import patch, MagicMock +from unittest.mock import MagicMock, patch -from django.core.exceptions import ImproperlyConfigured, PermissionDenied +from django.core.exceptions import PermissionDenied from openslides.utils import views -@patch('builtins.super') -class LoginMixinTest(TestCase): - def test_dispatch(self, mock_super): - """ - Tests that the function calls super - """ - # TODO: find a way to test the call of the decorator. - view = views.LoginMixin() - request = MagicMock() - - view.dispatch(request) - - mock_super().dispatch.assert_called_once_with(request) - - class PermissionMixinTest(TestCase): def test_check_permission_non_required_permission(self): view = views.PermissionMixin() @@ -77,199 +62,6 @@ class PermissionMixinTest(TestCase): view.dispatch(request) -class AjaxMixinTest(TestCase): - @patch('openslides.utils.views.json') - @patch('openslides.utils.views.HttpResponse') - def test_ajax_get(self, mock_response, mock_json): - view = views.AjaxMixin() - view.get_ajax_context = MagicMock(return_value='context') - mock_json.dumps.return_value = 'json' - - view.ajax_get(MagicMock()) - - mock_response.assert_called_once_with('json') - mock_json.dumps.assert_called_once_with('context') - - def test_get_ajax_context(self): - view = views.AjaxMixin() - - context = view.get_ajax_context(t1=1, t2=2, t3=3) - - self.assertEqual(context, {'t1': 1, 't2': 2, 't3': 3}) - - -class ExtraContextMixin(TestCase): - @patch('openslides.utils.views.template_manipulation') - @patch('builtins.super') - def test_get_context_data(self, mock_super, mock_signal): - """ - Tests that super is called with the kwargs, that the signal is called - with the returnd context and that the context is returned. - """ - view = views.ExtraContextMixin() - view.request = 'test_request' - mock_super().get_context_data.return_value = 'new_context' - - returned_context = view.get_context_data(a1=1, a2=2) - - mock_super().get_context_data.assert_called_once_with(a1=1, a2=2) - mock_signal.send.assert_called_once_with( - sender=views.ExtraContextMixin, - request='test_request', - context='new_context') - self.assertEqual(returned_context, 'new_context') - - -@patch('openslides.utils.views.reverse') -class UrlMixinGetUrl(TestCase): - """ - Tests the method 'get_url' from the UrlMixin. - """ - - def test_url_name(self, reverse): - """ - Tests that the return value of reverse(url_pattern) is returned. - """ - view = views.UrlMixin() - reverse.return_value = 'reverse_url' - - returned_url = view.get_url('test_url_name') - - reverse.assert_called_once_with('test_url_name', args=[]) - self.assertEqual(returned_url, 'reverse_url') - - def test_url_name_with_args(self, reverse): - """ - Tests that the return value of reverse(url_pattern) with args is returned. - """ - view = views.UrlMixin() - reverse.return_value = 'reverse_url' - - returned_url = view.get_url('test_url_name', args=[1, 2, 3]) - - reverse.assert_called_once_with('test_url_name', args=[1, 2, 3]) - self.assertEqual(returned_url, 'reverse_url') - - def test_url(self, reverse): - view = views.UrlMixin() - - returned_url = view.get_url(url='my_url') - - self.assertFalse( - reverse.called, - "reverse should not be called") - self.assertEqual(returned_url, 'my_url') - - def test_priority_of_url_name(self, reverse): - view = views.UrlMixin() - reverse.return_value = 'reverse_url' - - returned_url = view.get_url(url_name='test_url_name', url='my_url') - - reverse.assert_called_once_with('test_url_name', args=[]) - self.assertEqual(returned_url, 'reverse_url') - - def test_get_absolute_url(self, reverse): - view = views.UrlMixin() - view.object = MagicMock() - view.object.get_absolute_url.return_value = 'object_url' - - returned_url = view.get_url() - - view.object.get_absolute_url.assert_called_once_with() - self.assertEqual(returned_url, 'object_url') - self.assertFalse( - reverse.called, - "reverse should not be called") - - def test_get_absolute_url_with_link(self, reverse): - view = views.UrlMixin() - view.object = MagicMock() - view.object.get_absolute_url.return_value = 'object_url' - - returned_url = view.get_url(use_absolute_url_link='test_link') - - view.object.get_absolute_url.assert_called_once_with('test_link') - self.assertEqual(returned_url, 'object_url') - self.assertFalse( - reverse.called, - "reverse should not be called") - - def test_get_absolute_url_with_invalid_object(self, reverse): - view = views.UrlMixin() - view.object = MagicMock() - del view.object.get_absolute_url - - with self.assertRaisesRegex( - ImproperlyConfigured, - 'No url to redirect to\. See openslides\.utils\.views\.UrlMixin ' - 'for more details\.'): - view.get_url() - - -class UrlMixinGetUrlNameArgs(TestCase): - """ - Tests the method 'get_url_name_args' from the UrlMixin. - """ - - def test_has_attribute(self): - view = views.UrlMixin() - view.url_name_args = 'name_args' - - returned_args = view.get_url_name_args() - - self.assertEqual( - returned_args, - 'name_args', - "The object attribute 'url_name_args' should be returned") - - def test_without_attribute_with_object_with_pk(self): - view = views.UrlMixin() - view.object = MagicMock() - view.object.pk = 5 - - returned_args = view.get_url_name_args() - - self.assertEqual( - returned_args, - [5], - "object.pk should be returned as a one element list") - - def test_without_attribute_with_object_with_pk_is_none(self): - view = views.UrlMixin() - view.object = MagicMock() - view.object.pk = None - - returned_args = view.get_url_name_args() - - self.assertEqual( - returned_args, - [], - "An empty list should be returned") - - def test_without_attribute_with_object_without_pk(self): - view = views.UrlMixin() - view.object = MagicMock() - del view.object.pk - - returned_args = view.get_url_name_args() - - self.assertEqual( - returned_args, - [], - "An empty list should be returned") - - def test_without_attribute_without_object(self): - view = views.UrlMixin() - - returned_args = view.get_url_name_args() - - self.assertEqual( - returned_args, - [], - "An empty list should be returned") - - @patch('builtins.super') class SingleObjectMixinTest(TestCase): def test_get_object_cache(self, mock_super):