From 514577f7c6970b953c3b3e116934ff63a0f4e245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Mon, 11 Mar 2013 21:32:09 +0100 Subject: [PATCH 1/4] Built-in Workflows are created by listening to a signal which is sent after syncdb. --- openslides/core/__init__.py | 0 openslides/core/signals.py | 16 + openslides/main.py | 1 + openslides/motion/fixtures/initial_data.json | 280 ------------------ openslides/motion/models.py | 7 +- openslides/motion/signals.py | 78 ++++- openslides/motion/workflow.py | 55 ---- .../utils/management/commands/syncdb.py | 25 ++ tests/motion/test_models.py | 8 +- 9 files changed, 128 insertions(+), 342 deletions(-) create mode 100644 openslides/core/__init__.py create mode 100644 openslides/core/signals.py delete mode 100644 openslides/motion/fixtures/initial_data.json delete mode 100644 openslides/motion/workflow.py create mode 100644 openslides/utils/management/commands/syncdb.py diff --git a/openslides/core/__init__.py b/openslides/core/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/openslides/core/signals.py b/openslides/core/signals.py new file mode 100644 index 000000000..9d253035a --- /dev/null +++ b/openslides/core/signals.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.core.signals + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Core Signals. + + :copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from django.dispatch import Signal + + +post_database_setup = Signal() diff --git a/openslides/main.py b/openslides/main.py index 1baa2c62e..c6b819cf4 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -27,6 +27,7 @@ from django.core.management import execute_from_command_line from openslides import get_version from openslides.utils.tornado_webserver import run_tornado + CONFIG_TEMPLATE = """#!/usr/bin/env python # -*- coding: utf-8 -*- diff --git a/openslides/motion/fixtures/initial_data.json b/openslides/motion/fixtures/initial_data.json deleted file mode 100644 index 6dc7091c5..000000000 --- a/openslides/motion/fixtures/initial_data.json +++ /dev/null @@ -1,280 +0,0 @@ -[ - { - "pk":1, - "model":"motion.workflow", - "fields":{ - "name":"Simple Workflow", - "first_state":1 - } - }, - { - "pk":2, - "model":"motion.workflow", - "fields":{ - "name":"Complex Workflow", - "first_state":5 - } - }, - { - "pk":1, - "model":"motion.state", - "fields":{ - "name":"submitted", - "workflow":1, - "dont_set_new_version_active":false, - "allow_submitter_edit":true, - "next_states":[ - 2, - 3, - 4 - ], - "allow_support":true, - "action_word":"", - "icon":"", - "versioning":false, - "allow_create_poll":true - } - }, - { - "pk":2, - "model":"motion.state", - "fields":{ - "name":"accepted", - "workflow":1, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"accept", - "icon":"", - "versioning":false, - "allow_create_poll":false - } - }, - { - "pk":3, - "model":"motion.state", - "fields":{ - "name":"rejected", - "workflow":1, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"reject", - "icon":"", - "versioning":false, - "allow_create_poll":false - } - }, - { - "pk":4, - "model":"motion.state", - "fields":{ - "name":"not decided", - "workflow":1, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"do not decide", - "icon":"", - "versioning":false, - "allow_create_poll":false - } - }, - { - "pk":5, - "model":"motion.state", - "fields":{ - "name":"published", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":true, - "next_states":[ - 6, - 9, - 14 - ], - "allow_support":true, - "action_word":"", - "icon":"", - "versioning":false, - "allow_create_poll":false - } - }, - { - "pk":6, - "model":"motion.state", - "fields":{ - "name":"permitted", - "workflow":2, - "dont_set_new_version_active":true, - "allow_submitter_edit":true, - "next_states":[ - 7, - 8, - 9, - 10, - 11, - 12, - 13 - ], - "allow_support":false, - "action_word":"permit", - "icon":"", - "versioning":true, - "allow_create_poll":true - } - }, - { - "pk":7, - "model":"motion.state", - "fields":{ - "name":"accepted", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"accept", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":8, - "model":"motion.state", - "fields":{ - "name":"rejected", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"reject", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":9, - "model":"motion.state", - "fields":{ - "name":"withdrawed", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"withdraw", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":10, - "model":"motion.state", - "fields":{ - "name":"adjourned", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"adjourn", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":11, - "model":"motion.state", - "fields":{ - "name":"not concerned", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":12, - "model":"motion.state", - "fields":{ - "name":"commited a bill", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"commit a bill", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":13, - "model":"motion.state", - "fields":{ - "name":"needs review", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - }, - { - "pk":14, - "model":"motion.state", - "fields":{ - "name":"rejected (not authorized)", - "workflow":2, - "dont_set_new_version_active":false, - "allow_submitter_edit":false, - "next_states":[ - - ], - "allow_support":false, - "action_word":"reject (not authorized)", - "icon":"", - "versioning":true, - "allow_create_poll":false - } - } -] diff --git a/openslides/motion/models.py b/openslides/motion/models.py index f8de1544d..989fe6857 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -349,7 +349,8 @@ class Motion(SlideMixin, models.Model): if self.state: self.state = self.state.workflow.first_state else: - self.state = Workflow.objects.get(pk=config['motion_workflow']).first_state + self.state = (Workflow.objects.get(pk=config['motion_workflow']).first_state or + Workflow.objects.get(pk=config['motion_workflow']).state_set.all()[0]) def slide(self): """Return the slide dict.""" @@ -716,7 +717,7 @@ class Workflow(models.Model): name = models.CharField(max_length=255) """A string representing the workflow.""" - first_state = models.OneToOneField(State, related_name='+') + first_state = models.OneToOneField(State, related_name='+', null=True) """A one-to-one relation to a state, the starting point for the workflow.""" def __unicode__(self): @@ -733,5 +734,5 @@ class Workflow(models.Model): def check_first_state(self): """Checks whether the first_state itself belongs to the workflow.""" - if not self.first_state.workflow == self: + if self.first_state and not self.first_state.workflow == self: raise WorkflowError('%s can not be first state of %s because it does not belong to it.' % (self.first_state, self)) diff --git a/openslides/motion/signals.py b/openslides/motion/signals.py index 3b1cb38d3..526a56783 100644 --- a/openslides/motion/signals.py +++ b/openslides/motion/signals.py @@ -11,9 +11,12 @@ """ from django.dispatch import receiver -from django.utils.translation import ugettext as _ +from django.utils.translation import ugettext as _, ugettext_noop from openslides.config.signals import default_config_value +from openslides.core.signals import post_database_setup + +from .models import Workflow, State @receiver(default_config_value, dispatch_uid="motion_default_config") @@ -28,3 +31,76 @@ def default_config(sender, key, **kwargs): 'motion_pdf_preamble': '', 'motion_allow_disable_versioning': False, 'motion_workflow': 1}.get(key) + + +@receiver(post_database_setup, dispatch_uid='motion_create_builtin_workflows') +def create_builtin_workflows(sender, **kwargs): + """ + Creates a simple and a complex workflow. + """ + workflow_1 = Workflow.objects.create(name=ugettext_noop('Simple Workflow')) + state_1_1 = State.objects.create(name=ugettext_noop('submitted'), + workflow=workflow_1, + allow_create_poll=True, + allow_support=True, + allow_submitter_edit=True) + state_1_2 = State.objects.create(name=ugettext_noop('accepted'), + workflow=workflow_1, + action_word=ugettext_noop('Accept')) + state_1_3 = State.objects.create(name=ugettext_noop('rejected'), + workflow=workflow_1, + action_word=ugettext_noop('Reject')) + state_1_4 = State.objects.create(name=ugettext_noop('not decided'), + workflow=workflow_1, + action_word=ugettext_noop('Do not decide')) + state_1_1.next_states.add(state_1_2, state_1_3, state_1_4) + workflow_1.first_state = state_1_1 + workflow_1.save() + + workflow_2 = Workflow.objects.create(name=ugettext_noop('Complex Workflow')) + state_2_1 = State.objects.create(name=ugettext_noop('published'), + workflow=workflow_2, + allow_support=True, + allow_submitter_edit=True) + state_2_2 = State.objects.create(name=ugettext_noop('permitted'), + workflow=workflow_2, + action_word=ugettext_noop('Permit'), + allow_create_poll=True, + allow_submitter_edit=True, + versioning=True, + dont_set_new_version_active=True) + state_2_3 = State.objects.create(name=ugettext_noop('accepted'), + workflow=workflow_2, + action_word=ugettext_noop('Accept'), + versioning=True) + state_2_4 = State.objects.create(name=ugettext_noop('rejected'), + workflow=workflow_2, + action_word=ugettext_noop('Reject'), + versioning=True) + state_2_5 = State.objects.create(name=ugettext_noop('withdrawed'), + workflow=workflow_2, + action_word=ugettext_noop('Withdraw'), + versioning=True) + state_2_6 = State.objects.create(name=ugettext_noop('adjourned'), + workflow=workflow_2, + action_word=ugettext_noop('Adjourn'), + versioning=True) + state_2_7 = State.objects.create(name=ugettext_noop('not concerned'), + workflow=workflow_2, + action_word=ugettext_noop('Do not concern'), + versioning=True) + state_2_8 = State.objects.create(name=ugettext_noop('commited a bill'), + workflow=workflow_2, + action_word=ugettext_noop('Commit a bill'), + versioning=True) + state_2_9 = State.objects.create(name=ugettext_noop('needs review'), + workflow=workflow_2, + versioning=True) + state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'), + workflow=workflow_2, + action_word=ugettext_noop('reject (not authorized)'), + versioning=True) + state_2_1.next_states.add(state_2_2, state_2_5, state_2_10) + state_2_2.next_states.add(state_2_3, state_2_4, state_2_5, state_2_6, state_2_7, state_2_8, state_2_9) + workflow_2.first_state = state_2_1 + workflow_2.save() diff --git a/openslides/motion/workflow.py b/openslides/motion/workflow.py deleted file mode 100644 index 42d1e1814..000000000 --- a/openslides/motion/workflow.py +++ /dev/null @@ -1,55 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" - openslides.motion.workflow - ~~~~~~~~~~~~~~~~~~~~~~~~~~ - - This file is only for development. It will be moved out of - the openslides module before the next release. - - :copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS. - :license: GNU GPL, see LICENSE for more details. -""" - -from django.utils.translation import ugettext_noop - -from .models import Workflow, State - - -def _init_builtin_workflows(): - """ - Saves a simple and a complex workflow into the database. - This function is only called manually and lives here only for development. - """ - workflow_1 = Workflow(name=ugettext_noop('Simple Workflow'), id=1) - state_1_1 = State.objects.create(name=ugettext_noop('submitted'), workflow=workflow_1, - allow_create_poll=True, allow_support=True, allow_submitter_edit=True) - state_1_2 = State.objects.create(name=ugettext_noop('accepted'), workflow=workflow_1, action_word=ugettext_noop('accept')) - state_1_3 = State.objects.create(name=ugettext_noop('rejected'), workflow=workflow_1, action_word=ugettext_noop('reject')) - state_1_4 = State.objects.create(name=ugettext_noop('not decided'), workflow=workflow_1, action_word=ugettext_noop('do not decide')) - state_1_1.next_states.add(state_1_2, state_1_3, state_1_4) - state_1_1.save() # Is this neccessary? - workflow_1.first_state = state_1_1 - workflow_1.save() - - workflow_2 = Workflow(name=ugettext_noop('Complex Workflow'), id=2) - state_2_1 = State.objects.create(name=ugettext_noop('published'), workflow=workflow_2, allow_support=True, allow_submitter_edit=True) - state_2_2 = State.objects.create(name=ugettext_noop('permitted'), workflow=workflow_2, action_word=ugettext_noop('permit'), - allow_create_poll=True, allow_submitter_edit=True, versioning=True, dont_set_new_version_active=True) - state_2_3 = State.objects.create(name=ugettext_noop('accepted'), workflow=workflow_2, action_word=ugettext_noop('accept'), versioning=True) - state_2_4 = State.objects.create(name=ugettext_noop('rejected'), workflow=workflow_2, action_word=ugettext_noop('reject'), versioning=True) - state_2_5 = State.objects.create(name=ugettext_noop('withdrawed'), workflow=workflow_2, - action_word=ugettext_noop('withdraw'), versioning=True) - state_2_6 = State.objects.create(name=ugettext_noop('adjourned'), workflow=workflow_2, action_word=ugettext_noop('adjourn'), versioning=True) - state_2_7 = State.objects.create(name=ugettext_noop('not concerned'), workflow=workflow_2, versioning=True) - state_2_8 = State.objects.create(name=ugettext_noop('commited a bill'), workflow=workflow_2, - action_word=ugettext_noop('commit a bill'), versioning=True) - state_2_9 = State.objects.create(name=ugettext_noop('needs review'), workflow=workflow_2, versioning=True) - state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'), workflow=workflow_2, - action_word=ugettext_noop('reject (not authorized)'), versioning=True) - state_2_1.next_states.add(state_2_2, state_2_5, state_2_10) - state_2_2.next_states.add(state_2_3, state_2_4, state_2_5, state_2_6, state_2_7, state_2_8, state_2_9) - state_2_1.save() # Is this neccessary? - state_2_2.save() # Is this neccessary? - workflow_2.first_state = state_2_1 - workflow_2.save() diff --git a/openslides/utils/management/commands/syncdb.py b/openslides/utils/management/commands/syncdb.py new file mode 100644 index 000000000..2835fb220 --- /dev/null +++ b/openslides/utils/management/commands/syncdb.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.utils.management.commands.syncdb + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + Overrides the Django syncdb command to setup the database. + + :copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from django.core.management.commands.syncdb import Command as _Command + +from openslides.core.signals import post_database_setup + + +class Command(_Command): + """ + Setup the database and sends the signal post_database_setup. + """ + def handle_noargs(self, *args, **kwargs): + return_value = super(Command, self).handle_noargs(*args, **kwargs) + post_database_setup.send(sender=self) + return return_value diff --git a/tests/motion/test_models.py b/tests/motion/test_models.py index 9c647f296..bc1a304f4 100644 --- a/tests/motion/test_models.py +++ b/tests/motion/test_models.py @@ -10,6 +10,7 @@ from django.test import TestCase +from openslides.core.signals import post_database_setup from openslides.participant.models import User from openslides.config.models import config from openslides.motion.models import Motion, Workflow, State @@ -18,6 +19,7 @@ from openslides.motion.exceptions import WorkflowError class ModelTest(TestCase): def setUp(self): + post_database_setup.send(sender=self) self.motion = Motion.objects.create(title='v1') self.test_user = User.objects.create(username='blub') self.workflow = Workflow.objects.get(pk=1) @@ -113,18 +115,18 @@ class ModelTest(TestCase): self.motion.state = State.objects.get(pk=6) self.assertEqual(self.motion.state.name, 'permitted') - self.assertEqual(self.motion.state.get_action_word(), 'permit') + self.assertEqual(self.motion.state.get_action_word(), 'Permit') with self.assertRaises(WorkflowError): self.motion.support(self.test_user) with self.assertRaises(WorkflowError): self.motion.unsupport(self.test_user) def test_new_states_or_workflows(self): - workflow_1 = Workflow(name='W1', id=1000) + workflow_1 = Workflow.objects.create(name='W1') state_1 = State.objects.create(name='S1', workflow=workflow_1) workflow_1.first_state = state_1 workflow_1.save() - workflow_2 = Workflow(name='W2', id=2000) + workflow_2 = Workflow.objects.create(name='W2') state_2 = State.objects.create(name='S2', workflow=workflow_2) workflow_2.first_state = state_2 workflow_2.save() From 9679be186710b20df557b7c4e58e93d61036fd9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Tue, 12 Mar 2013 20:58:22 +0100 Subject: [PATCH 2/4] Move builtin groups to a signal. --- openslides/config/views.py | 4 - openslides/main.py | 1 - openslides/participant/__init__.py | 15 ++ openslides/participant/api.py | 23 +- .../participant/fixtures/groups_de.json | 233 ------------------ openslides/participant/forms.py | 4 +- openslides/participant/models.py | 4 +- openslides/participant/signals.py | 70 ++++++ 8 files changed, 94 insertions(+), 260 deletions(-) delete mode 100644 openslides/participant/fixtures/groups_de.json create mode 100644 openslides/participant/signals.py diff --git a/openslides/config/views.py b/openslides/config/views.py index 997527384..09f0a6968 100644 --- a/openslides/config/views.py +++ b/openslides/config/views.py @@ -22,9 +22,6 @@ from openslides.utils.views import FormView, TemplateView from .forms import GeneralConfigForm from .models import config -# TODO: Do not import the participant module in config -from openslides.participant.api import get_or_create_anonymous_group - class GeneralConfig(FormView): """ @@ -62,7 +59,6 @@ class GeneralConfig(FormView): # system if form.cleaned_data['system_enable_anonymous']: config['system_enable_anonymous'] = True - get_or_create_anonymous_group() else: config['system_enable_anonymous'] = False diff --git a/openslides/main.py b/openslides/main.py index c6b819cf4..b7107317d 100644 --- a/openslides/main.py +++ b/openslides/main.py @@ -271,7 +271,6 @@ def run_syncdb(): # now initialize the database argv = ["", "syncdb", "--noinput"] execute_from_command_line(argv) - execute_from_command_line(["", "loaddata", "groups_de"]) def set_system_url(url): diff --git a/openslides/participant/__init__.py b/openslides/participant/__init__.py index f04c201d4..12b9658f8 100644 --- a/openslides/participant/__init__.py +++ b/openslides/participant/__init__.py @@ -1,3 +1,18 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.participant + ~~~~~~~~~~~~~~~~~~~~~~ + + The OpenSlides participant app. + + :copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + from django.utils.translation import ugettext_noop +import openslides.participant.signals + + NAME = ugettext_noop('Participant') diff --git a/openslides/participant/api.py b/openslides/participant/api.py index f7b5a80a2..7ffd2fe93 100644 --- a/openslides/participant/api.py +++ b/openslides/participant/api.py @@ -97,21 +97,8 @@ def import_users(csv_file): return (count_success, error_messages) -def get_or_create_registered_group(): - registered, created = Group.objects.get_or_create( - name__iexact='Registered', defaults={'name': 'Registered'}) - if created: - registered.permissions = Permission.objects.filter( - codename__in=DEFAULT_PERMS) - registered.save() - return registered - - -def get_or_create_anonymous_group(): - anonymous, created = Group.objects.get_or_create( - name__iexact='Anonymous', defaults={'name': 'Anonymous'}) - if created: - anonymous.permissions = Permission.objects.filter( - codename__in=DEFAULT_PERMS) - anonymous.save() - return anonymous +def get_registered_group(): + """ + Returns the Group 'Registered'. Upper and lower case is possible. + """ + return Group.objects.get(name__iexact='Registered') diff --git a/openslides/participant/fixtures/groups_de.json b/openslides/participant/fixtures/groups_de.json deleted file mode 100644 index b662beff6..000000000 --- a/openslides/participant/fixtures/groups_de.json +++ /dev/null @@ -1,233 +0,0 @@ -[ - { - "pk": 1, - "model": "auth.group", - "fields": { - "name": "Beobachter/in", - "permissions": [ - [ - "can_see_agenda", - "agenda", - "item" - ], - [ - "can_create_motion", - "motion", - "motion" - ], - [ - "can_see_motion", - "motion", - "motion" - ], - [ - "can_nominate_other", - "assignment", - "assignment" - ], - [ - "can_nominate_self", - "assignment", - "assignment" - ], - [ - "can_see_assignment", - "assignment", - "assignment" - ], - [ - "can_see_participant", - "participant", - "user" - ], - [ - "can_see_projector", - "projector", - "projectorslide" - ], - [ - "can_see_dashboard", - "projector", - "projectorslide" - ] - ] - } - }, - { - "pk": 2, - "model": "auth.group", - "fields": { - "name": "Delegierte/r", - "permissions": [ - [ - "can_see_agenda", - "agenda", - "item" - ], - [ - "can_create_motion", - "motion", - "motion" - ], - [ - "can_see_motion", - "motion", - "motion" - ], - [ - "can_support_motion", - "motion", - "motion" - ], - [ - "can_nominate_other", - "assignment", - "assignment" - ], - [ - "can_nominate_self", - "assignment", - "assignment" - ], - [ - "can_see_assignment", - "assignment", - "assignment" - ], - [ - "can_see_participant", - "participant", - "user" - ], - [ - "can_see_projector", - "projector", - "projectorslide" - ], - [ - "can_see_dashboard", - "projector", - "projectorslide" - ] - ] - } - }, - { - "pk": 3, - "model": "auth.group", - "fields": { - "name": "Versammlungsleitung", - "permissions": [ - [ - "can_manage_agenda", - "agenda", - "item" - ], - [ - "can_see_agenda", - "agenda", - "item" - ], - [ - "can_create_motion", - "motion", - "motion" - ], - [ - "can_manage_motion", - "motion", - "motion" - ], - [ - "can_see_motion", - "motion", - "motion" - ], - [ - "can_manage_assignment", - "assignment", - "assignment" - ], - [ - "can_nominate_other", - "assignment", - "assignment" - ], - [ - "can_nominate_self", - "assignment", - "assignment" - ], - [ - "can_see_assignment", - "assignment", - "assignment" - ], - [ - "can_manage_config", - "config", - "configstore" - ], - [ - "can_manage_participant", - "participant", - "user" - ], - [ - "can_see_participant", - "participant", - "user" - ], - [ - "can_manage_projector", - "projector", - "projectorslide" - ], - [ - "can_see_projector", - "projector", - "projectorslide" - ], - [ - "can_see_dashboard", - "projector", - "projectorslide" - ] - ] - } - }, - { - "pk": 4, - "model": "auth.group", - "fields": { - "name": "Teilnehmerverwaltung", - "permissions": [ - [ - "can_see_agenda", - "agenda", - "item" - ], - [ - "can_manage_participant", - "participant", - "user" - ], - [ - "can_see_participant", - "participant", - "user" - ], - [ - "can_see_projector", - "projector", - "projectorslide" - ], - [ - "can_see_dashboard", - "projector", - "projectorslide" - ] - ] - } - } -] diff --git a/openslides/participant/forms.py b/openslides/participant/forms.py index 854abe91e..2da72bafe 100644 --- a/openslides/participant/forms.py +++ b/openslides/participant/forms.py @@ -19,7 +19,7 @@ from openslides.utils.forms import ( CssClassMixin, LocalizedModelMultipleChoiceField) from openslides.participant.models import User, Group -from openslides.participant.api import get_or_create_registered_group +from openslides.participant.api import get_registered_group class UserCreateForm(forms.ModelForm, CssClassMixin): @@ -30,7 +30,7 @@ class UserCreateForm(forms.ModelForm, CssClassMixin): def __init__(self, *args, **kwargs): if kwargs.get('instance', None) is None: initial = kwargs.setdefault('initial', {}) - registered = get_or_create_registered_group() + registered = get_registered_group() initial['groups'] = [registered.pk] super(UserCreateForm, self).__init__(*args, **kwargs) diff --git a/openslides/participant/models.py b/openslides/participant/models.py index 6cad7364f..754096386 100644 --- a/openslides/participant/models.py +++ b/openslides/participant/models.py @@ -257,9 +257,9 @@ def djangogroup_post_save(sender, instance, signal, *args, **kwargs): @receiver(signals.post_save, sender=User) def user_post_save(sender, instance, *args, **kwargs): - from openslides.participant.api import get_or_create_registered_group if not kwargs['created']: return - registered = get_or_create_registered_group() + from openslides.participant.api import get_registered_group # TODO: Test, if global import is possible + registered = get_registered_group() instance.groups.add(registered) instance.save() diff --git a/openslides/participant/signals.py b/openslides/participant/signals.py new file mode 100644 index 000000000..7cace7b4f --- /dev/null +++ b/openslides/participant/signals.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.participant.signals + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Signals for the participant app. + + :copyright: (c) 2011-2013 by the OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + +from django.dispatch import receiver +from django.utils.translation import ugettext_noop +from django.contrib.contenttypes.models import ContentType +from django.contrib.auth.models import Permission + +from openslides.core.signals import post_database_setup + +from .models import Group + + +@receiver(post_database_setup, dispatch_uid='participant_create_builtin_groups') +def create_builtin_groups(sender, **kwargs): + """ + Creates the builtin groups: Anonymous, Registered, Delegates and Staff. + """ + # Anonymous and Registered + ct_projector = ContentType.objects.get(app_label='projector', model='projectorslide') + perm_1 = Permission.objects.get(content_type=ct_projector, codename='can_see_projector') + perm_2 = Permission.objects.get(content_type=ct_projector, codename='can_see_dashboard') + + ct_agenda = ContentType.objects.get(app_label='agenda', model='item') + perm_3 = Permission.objects.get(content_type=ct_agenda, codename='can_see_agenda') + + ct_motion = ContentType.objects.get(app_label='motion', model='motion') + perm_4 = Permission.objects.get(content_type=ct_motion, codename='can_see_motion') + + ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment') + perm_5 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment') + + ct_participant = ContentType.objects.get(app_label='participant', model='user') + perm_6 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant') + + group_anonymous = Group.objects.create(name=ugettext_noop('Anonymous')) + group_anonymous.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6) + group_registered = Group.objects.create(name=ugettext_noop('Registered')) + group_registered.permissions.add(perm_1, perm_2, perm_3, perm_4, perm_5, perm_6) + + # Delegates + perm_7 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion') + perm_8 = Permission.objects.get(content_type=ct_motion, codename='can_support_motion') + perm_9 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_other') + perm_10 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_self') + + group_delegates = Group.objects.create(name=ugettext_noop('Delegates')) + group_delegates.permissions.add(perm_7, perm_8, perm_9, perm_10) + + # Staff + perm_11 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda') + perm_12 = Permission.objects.get(content_type=ct_motion, codename='can_manage_motion') + perm_13 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment') + perm_14 = Permission.objects.get(content_type=ct_participant, codename='can_manage_participant') + perm_15 = Permission.objects.get(content_type=ct_projector, codename='can_manage_projector') + + ct_config = ContentType.objects.get(app_label='config', model='configstore') + perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage_config') + + group_staff = Group.objects.create(name=ugettext_noop('Staff')) + group_staff.permissions.add(perm_7, perm_9, perm_10, perm_11, perm_12, perm_13, perm_14, perm_15, perm_16) From 03fff46517eb599bab665f6b6622d57817267f78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Tue, 12 Mar 2013 21:38:22 +0100 Subject: [PATCH 3/4] Add new TestCase class in utils to call the signal on running every test. --- openslides/utils/test.py | 27 +++++++++++++++++++++++++++ tests/agenda/tests.py | 2 +- tests/motion/test_models.py | 5 +---- tests/test_participant.py | 3 +-- 4 files changed, 30 insertions(+), 7 deletions(-) create mode 100644 openslides/utils/test.py diff --git a/openslides/utils/test.py b/openslides/utils/test.py new file mode 100644 index 000000000..9d7ca3df9 --- /dev/null +++ b/openslides/utils/test.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" + openslides.utils.test + ~~~~~~~~~~~~~~~~~~~~~ + + Unit test class. + + :copyright: 2011-2013 by OpenSlides team, see AUTHORS. + :license: GNU GPL, see LICENSE for more details. +""" + + +from django.test import TestCase as _TestCase + +from openslides.core.signals import post_database_setup + + +class TestCase(_TestCase): + """ + Overwrites Django's TestCase class to call the post_database_setup + signal after the preparation of every test. + """ + def _pre_setup(self, *args, **kwargs): + return_value = super(TestCase, self)._pre_setup(*args, **kwargs) + post_database_setup.send(sender=self) + return return_value diff --git a/tests/agenda/tests.py b/tests/agenda/tests.py index d28c5f993..5472fc6a8 100644 --- a/tests/agenda/tests.py +++ b/tests/agenda/tests.py @@ -10,10 +10,10 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.test import TestCase from django.test.client import Client from django.db.models.query import EmptyQuerySet +from openslides.utils.test import TestCase from openslides.projector.api import get_active_slide from openslides.participant.models import User from openslides.agenda.models import Item diff --git a/tests/motion/test_models.py b/tests/motion/test_models.py index bc1a304f4..86498912f 100644 --- a/tests/motion/test_models.py +++ b/tests/motion/test_models.py @@ -8,9 +8,7 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.test import TestCase - -from openslides.core.signals import post_database_setup +from openslides.utils.test import TestCase from openslides.participant.models import User from openslides.config.models import config from openslides.motion.models import Motion, Workflow, State @@ -19,7 +17,6 @@ from openslides.motion.exceptions import WorkflowError class ModelTest(TestCase): def setUp(self): - post_database_setup.send(sender=self) self.motion = Motion.objects.create(title='v1') self.test_user = User.objects.create(username='blub') self.workflow = Workflow.objects.get(pk=1) diff --git a/tests/test_participant.py b/tests/test_participant.py index 4227dc66b..608ee686f 100644 --- a/tests/test_participant.py +++ b/tests/test_participant.py @@ -10,8 +10,7 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.test import TestCase - +from openslides.utils.test import TestCase from openslides.utils.person import get_person, Persons from openslides.participant.api import gen_username, gen_password from openslides.participant.models import User, Group From edce8c59f18365c490eba90b6feef727a5055cd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Tue, 12 Mar 2013 21:56:13 +0100 Subject: [PATCH 4/4] Fix PEP8, and forgotten import change. Update .travis. --- .travis.yml | 1 + tests/agenda/tests.py | 1 - tests/motion/test_models.py | 2 +- tests/test_init.py | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9a87327fd..aa3f468b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,4 @@ install: script: - coverage run ./manage.py test tests && coverage report -m - pep8 --max-line-length=150 --exclude="urls.py," --statistics openslides + - pep8 --max-line-length=150 --statistics tests diff --git a/tests/agenda/tests.py b/tests/agenda/tests.py index 5472fc6a8..d1a76cf6f 100644 --- a/tests/agenda/tests.py +++ b/tests/agenda/tests.py @@ -95,7 +95,6 @@ class ItemTest(TestCase): self.assertEqual(self.item5.print_related_type(), 'Releateditem') - class ViewTest(TestCase): def setUp(self): self.item1 = Item.objects.create(title='item1') diff --git a/tests/motion/test_models.py b/tests/motion/test_models.py index 86498912f..8e7ddcd4d 100644 --- a/tests/motion/test_models.py +++ b/tests/motion/test_models.py @@ -75,7 +75,7 @@ class ModelTest(TestCase): self.assertEqual(motion.title, 'v2') motion.version = None - motion.version = None # Test to set a version to None, which is already None + motion.version = None # Test to set a version to None, which is already None self.assertEqual(motion.title, 'v3') with self.assertRaises(ValueError): diff --git a/tests/test_init.py b/tests/test_init.py index 411860d79..aa665c62f 100644 --- a/tests/test_init.py +++ b/tests/test_init.py @@ -7,9 +7,9 @@ :license: GNU GPL, see LICENSE for more details. """ -from django.test import TestCase - from openslides import get_version, get_git_commit_id +from openslides.utils.test import TestCase + class InitTest(TestCase): def test_get_version(self):