Merge remote branch 'upstream/master' into motiontemplate

Conflicts:
	openslides/motion/models.py
	openslides/motion/views.py
This commit is contained in:
Emanuel Schuetze 2013-03-14 21:36:02 +01:00
commit 247f6c9431
26 changed files with 385 additions and 629 deletions

View File

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

View File

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

View File

View File

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

View File

@ -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 -*-
@ -270,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):

View File

@ -1,3 +1,4 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
openslides.motion

View File

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

View File

@ -96,6 +96,12 @@ class MotionCategoryMixin(forms.ModelForm):
category = forms.ModelChoiceField(queryset=Category.objects.all(), required=False)
class MotionIdentifierMixin(forms.ModelForm):
"""Mixin to let the user choose the identifier for the motion."""
identifier = forms.CharField(required=False)
class ConfigForm(CssClassMixin, forms.Form):
"""Form for the configuration tab of OpenSlides."""
motion_min_supporters = forms.IntegerField(

View File

@ -18,7 +18,7 @@ from datetime import datetime
import difflib
from django.core.urlresolvers import reverse
from django.db import models
from django.db import models, IntegrityError
from django.db.models import Max
from django.dispatch import receiver
from django.utils import formats
@ -67,6 +67,12 @@ class Motion(SlideMixin, models.Model):
unique=True)
"""A string as human readable identifier for the motion."""
identifier_number = models.IntegerField(null=True)
"""Counts the number of the motion in one category.
Needed to find the next free motion-identifier.
"""
category = models.ForeignKey('Category', null=True, blank=True)
"""ForeignKey to one category of motions."""
@ -172,6 +178,33 @@ class Motion(SlideMixin, models.Model):
if link == 'delete':
return reverse('motion_delete', args=[str(self.id)])
def set_identifier(self):
if config['motion_identifier'] == 'manually':
# Do not set an identifier.
return
elif config['motion_identifier'] == 'per_category':
motions = Motion.objects.filter(category=self.category)
else:
motions = Motion.objects.all()
number = motions.aggregate(Max('identifier_number'))['identifier_number__max'] or 0
if self.category is None or not self.category.prefix:
prefix = ''
else:
prefix = self.category.prefix + ' '
while True:
number += 1
self.identifier = '%s%d' % (prefix, number)
try:
self.save()
except IntegrityError:
continue
else:
self.number = number
self.save()
break
def get_title(self):
"""Get the title of the motion.
@ -348,12 +381,25 @@ class Motion(SlideMixin, models.Model):
else:
raise WorkflowError('You can not create a poll in state %s.' % self.state.name)
def set_state(self, state):
"""Set the state of the motion.
State can be the id of a state object or a state object.
"""
if type(state) is int:
state = State.objects.get(pk=state)
if not state.dont_set_identifier:
self.set_identifier()
self.state = state
def reset_state(self):
"""Set the state to the default state. If the motion is new, it chooses the default workflow from config."""
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."""
@ -535,7 +581,13 @@ class MotionSupporter(models.Model):
class Category(models.Model):
name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
prefix = models.CharField(max_length=32, verbose_name=ugettext_lazy("Prefix"))
"""Name of the category."""
prefix = models.CharField(blank=True, max_length=32, verbose_name=ugettext_lazy("Prefix"))
"""Prefix of the category.
Used to build the identifier of a motion.
"""
def __unicode__(self):
return self.name
@ -702,6 +754,12 @@ class State(models.Model):
dont_set_new_version_active = models.BooleanField(default=False)
"""If true, new versions are not automaticly set active."""
dont_set_identifier = models.BooleanField(default=False)
"""Decides if the motion gets an identifier.
If true, the motion does not get an identifier if the state change to
this one, else it does."""
def __unicode__(self):
"""Returns the name of the state."""
return self.name
@ -734,7 +792,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):
@ -751,5 +809,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))

View File

@ -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,77 @@ 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,
dont_set_identifier=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()

View File

@ -8,11 +8,11 @@
{% block content %}
<h1>
{{ motion.title }}
{{ motion.title }} {{ motion.category }}
<br>
<small>
{% if motion.number != None %}
{% trans "Motion" %} {{ motion.number }},
{% if motion.identifier != None %}
{% trans "Motion" %} {{ motion.identifier }},
{% else %}
<i>[{% trans "no number" %}]</i>,
{% endif %}
@ -56,14 +56,12 @@
<div class="row-fluid">
<div class="span8">
<span class="label label-warning"><i class="icon-warning-sign icon-white"></i>
{% if motion.version.version_number < motion.last_version.version_number %}
{% trans "This is not the newest version." %}</span> <a href="{% model_url motion.last_version %}">{% trans "Go to version" %} {{ motion.last_version.version_number }}.</a>
{% else %}
{% trans "This is not the authorized version." %}</span> <a href="{% model_url motion.active_version %}">{% trans "Go to version" %} {{ motion.active_version.version_number }}.</a>
{% endif %}
<span class="label label-warning"><i class="icon-warning-sign icon-white"></i>
{% if motion.version.version_number < motion.last_version.version_number %}
{% trans "This is not the newest version." %}</span> <a href="{% model_url motion.last_version %}">{% trans "Go to version" %} {{ motion.last_version.version_number }}.</a>
{% else %}
{% trans "This is not the authorized version." %}</span> <a href="{% model_url motion.active_version %}">{% trans "Go to version" %} {{ motion.active_version.version_number }}.</a>
{% endif %}
<!-- Text -->
<h4>{% trans "Motion text" %}:</h4>
@ -283,5 +281,4 @@
</div> <!--/span-->
</div> <!--/row-->
{% endblock %}

View File

@ -40,6 +40,11 @@ urlpatterns = patterns('openslides.motion.views',
name='motion_delete',
),
url(r'^(?P<pk>\d+)/set_identifier/',
'set_identifier',
name='motion_set_identifier',
),
url(r'^(?P<pk>\d+)/version/(?P<version_number>\d+)/$',
'motion_detail',
name='motion_version_detail',

View File

@ -35,10 +35,15 @@ from openslides.agenda.models import Item
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
MotionVersion, State, WorkflowError, Category)
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin)
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin,
MotionIdentifierMixin)
from .pdf import motions_to_pdf, motion_to_pdf
# TODO: into the config-tab
config['motion_identifier'] = ('manually', 'per_category', 'serially_numbered')[2]
class MotionListView(ListView):
"""View, to list all motions."""
permission_required = 'motion.can_see_motion'
@ -98,6 +103,16 @@ class MotionMixin(object):
except KeyError:
pass
try:
self.object.category = form.cleaned_data['category']
except KeyError:
pass
try:
self.object.identifier = form.cleaned_data['identifier']
except KeyError:
pass
def post_save(self, form):
"""Save the submitter an the supporter so the motion."""
super(MotionMixin, self).post_save(form)
@ -120,8 +135,14 @@ class MotionMixin(object):
will be mixed in dependence of some config values. See motion.forms
for more information on the mixins.
"""
form_classes = []
if (self.request.user.has_perm('motion.can_manage_motion') and
config['motion_identifier'] == 'manually'):
form_classes.append(MotionIdentifierMixin)
form_classes.append(BaseMotionForm)
form_classes = [BaseMotionForm]
if self.request.user.has_perm('motion.can_manage_motion'):
form_classes.append(MotionSubmitterMixin)
form_classes.append(MotionCategoryMixin)
@ -262,7 +283,32 @@ class VersionDiffView(GetVersionMixin, DetailView):
version_diff = VersionDiffView.as_view()
class SupportView(SingleObjectMixin, RedirectView):
class SetIdentifierView(SingleObjectMixin, RedirectView):
"""Set the identifier of the motion.
See motion.set_identifier for more informations
"""
permission_required = 'motion.can_manage_motion'
model = Motion
url_name = 'motion_detail'
def get(self, request, *args, **kwargs):
"""Set self.object to a motion."""
self.object = self.get_object()
return super(SetIdentifierView, self).get(request, *args, **kwargs)
def pre_redirect(self, request, *args, **kwargs):
"""Set the identifier."""
self.object.set_identifier()
def get_url_name_args(self):
return [self.object.id]
set_identifier = SetIdentifierView.as_view()
class SupportView(SingleObjectMixin, QuestionMixin, RedirectView):
"""View to support or unsupport a motion.
If self.support is True, the view will append a request.user to the supporter list.
@ -427,7 +473,7 @@ class MotionSetStateView(SingleObjectMixin, RedirectView):
if self.reset:
self.object.reset_state()
else:
self.object.state = State.objects.get(pk=kwargs['state'])
self.object.set_state(int(kwargs['state']))
except WorkflowError, e: # TODO: Is a WorkflowError still possible here?
messages.error(request, e)
else:

View File

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

View File

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

View File

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

View File

@ -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"
]
]
}
}
]

View File

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

View File

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

View File

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

View File

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

27
openslides/utils/test.py Normal file
View File

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

View File

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

View File

@ -8,8 +8,7 @@
:license: GNU GPL, see LICENSE for more details.
"""
from django.test import TestCase
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
@ -76,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):
@ -113,18 +112,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()

View File

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

View File

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