Merge remote branch 'upstream/master' into motiontemplate
Conflicts: openslides/motion/models.py openslides/motion/views.py
This commit is contained in:
commit
247f6c9431
@ -8,3 +8,4 @@ install:
|
|||||||
script:
|
script:
|
||||||
- coverage run ./manage.py test tests && coverage report -m
|
- coverage run ./manage.py test tests && coverage report -m
|
||||||
- pep8 --max-line-length=150 --exclude="urls.py," --statistics openslides
|
- pep8 --max-line-length=150 --exclude="urls.py," --statistics openslides
|
||||||
|
- pep8 --max-line-length=150 --statistics tests
|
||||||
|
@ -22,9 +22,6 @@ from openslides.utils.views import FormView, TemplateView
|
|||||||
from .forms import GeneralConfigForm
|
from .forms import GeneralConfigForm
|
||||||
from .models import config
|
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):
|
class GeneralConfig(FormView):
|
||||||
"""
|
"""
|
||||||
@ -62,7 +59,6 @@ class GeneralConfig(FormView):
|
|||||||
# system
|
# system
|
||||||
if form.cleaned_data['system_enable_anonymous']:
|
if form.cleaned_data['system_enable_anonymous']:
|
||||||
config['system_enable_anonymous'] = True
|
config['system_enable_anonymous'] = True
|
||||||
get_or_create_anonymous_group()
|
|
||||||
else:
|
else:
|
||||||
config['system_enable_anonymous'] = False
|
config['system_enable_anonymous'] = False
|
||||||
|
|
||||||
|
0
openslides/core/__init__.py
Normal file
0
openslides/core/__init__.py
Normal file
16
openslides/core/signals.py
Normal file
16
openslides/core/signals.py
Normal 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()
|
@ -27,6 +27,7 @@ from django.core.management import execute_from_command_line
|
|||||||
from openslides import get_version
|
from openslides import get_version
|
||||||
from openslides.utils.tornado_webserver import run_tornado
|
from openslides.utils.tornado_webserver import run_tornado
|
||||||
|
|
||||||
|
|
||||||
CONFIG_TEMPLATE = """#!/usr/bin/env python
|
CONFIG_TEMPLATE = """#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
@ -270,7 +271,6 @@ def run_syncdb():
|
|||||||
# now initialize the database
|
# now initialize the database
|
||||||
argv = ["", "syncdb", "--noinput"]
|
argv = ["", "syncdb", "--noinput"]
|
||||||
execute_from_command_line(argv)
|
execute_from_command_line(argv)
|
||||||
execute_from_command_line(["", "loaddata", "groups_de"])
|
|
||||||
|
|
||||||
|
|
||||||
def set_system_url(url):
|
def set_system_url(url):
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
openslides.motion
|
openslides.motion
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -96,6 +96,12 @@ class MotionCategoryMixin(forms.ModelForm):
|
|||||||
category = forms.ModelChoiceField(queryset=Category.objects.all(), required=False)
|
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):
|
class ConfigForm(CssClassMixin, forms.Form):
|
||||||
"""Form for the configuration tab of OpenSlides."""
|
"""Form for the configuration tab of OpenSlides."""
|
||||||
motion_min_supporters = forms.IntegerField(
|
motion_min_supporters = forms.IntegerField(
|
||||||
|
@ -18,7 +18,7 @@ from datetime import datetime
|
|||||||
import difflib
|
import difflib
|
||||||
|
|
||||||
from django.core.urlresolvers import reverse
|
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.db.models import Max
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
from django.utils import formats
|
from django.utils import formats
|
||||||
@ -67,6 +67,12 @@ class Motion(SlideMixin, models.Model):
|
|||||||
unique=True)
|
unique=True)
|
||||||
"""A string as human readable identifier for the motion."""
|
"""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)
|
category = models.ForeignKey('Category', null=True, blank=True)
|
||||||
"""ForeignKey to one category of motions."""
|
"""ForeignKey to one category of motions."""
|
||||||
|
|
||||||
@ -172,6 +178,33 @@ class Motion(SlideMixin, models.Model):
|
|||||||
if link == 'delete':
|
if link == 'delete':
|
||||||
return reverse('motion_delete', args=[str(self.id)])
|
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):
|
def get_title(self):
|
||||||
"""Get the title of the motion.
|
"""Get the title of the motion.
|
||||||
|
|
||||||
@ -348,12 +381,25 @@ class Motion(SlideMixin, models.Model):
|
|||||||
else:
|
else:
|
||||||
raise WorkflowError('You can not create a poll in state %s.' % self.state.name)
|
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):
|
def reset_state(self):
|
||||||
"""Set the state to the default state. If the motion is new, it chooses the default workflow from config."""
|
"""Set the state to the default state. If the motion is new, it chooses the default workflow from config."""
|
||||||
if self.state:
|
if self.state:
|
||||||
self.state = self.state.workflow.first_state
|
self.state = self.state.workflow.first_state
|
||||||
else:
|
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):
|
def slide(self):
|
||||||
"""Return the slide dict."""
|
"""Return the slide dict."""
|
||||||
@ -535,7 +581,13 @@ class MotionSupporter(models.Model):
|
|||||||
|
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
name = models.CharField(max_length=255, verbose_name=ugettext_lazy("Category name"))
|
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):
|
def __unicode__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -702,6 +754,12 @@ class State(models.Model):
|
|||||||
dont_set_new_version_active = models.BooleanField(default=False)
|
dont_set_new_version_active = models.BooleanField(default=False)
|
||||||
"""If true, new versions are not automaticly set active."""
|
"""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):
|
def __unicode__(self):
|
||||||
"""Returns the name of the state."""
|
"""Returns the name of the state."""
|
||||||
return self.name
|
return self.name
|
||||||
@ -734,7 +792,7 @@ class Workflow(models.Model):
|
|||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
"""A string representing the workflow."""
|
"""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."""
|
"""A one-to-one relation to a state, the starting point for the workflow."""
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
@ -751,5 +809,5 @@ class Workflow(models.Model):
|
|||||||
|
|
||||||
def check_first_state(self):
|
def check_first_state(self):
|
||||||
"""Checks whether the first_state itself belongs to the workflow."""
|
"""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))
|
raise WorkflowError('%s can not be first state of %s because it does not belong to it.' % (self.first_state, self))
|
||||||
|
@ -11,9 +11,12 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from django.dispatch import receiver
|
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.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")
|
@receiver(default_config_value, dispatch_uid="motion_default_config")
|
||||||
@ -28,3 +31,77 @@ def default_config(sender, key, **kwargs):
|
|||||||
'motion_pdf_preamble': '',
|
'motion_pdf_preamble': '',
|
||||||
'motion_allow_disable_versioning': False,
|
'motion_allow_disable_versioning': False,
|
||||||
'motion_workflow': 1}.get(key)
|
'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()
|
||||||
|
@ -8,11 +8,11 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<h1>
|
<h1>
|
||||||
{{ motion.title }}
|
{{ motion.title }} {{ motion.category }}
|
||||||
<br>
|
<br>
|
||||||
<small>
|
<small>
|
||||||
{% if motion.number != None %}
|
{% if motion.identifier != None %}
|
||||||
{% trans "Motion" %} {{ motion.number }},
|
{% trans "Motion" %} {{ motion.identifier }},
|
||||||
{% else %}
|
{% else %}
|
||||||
<i>[{% trans "no number" %}]</i>,
|
<i>[{% trans "no number" %}]</i>,
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -56,7 +56,6 @@
|
|||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<div class="span8">
|
<div class="span8">
|
||||||
|
|
||||||
<span class="label label-warning"><i class="icon-warning-sign icon-white"></i>
|
<span class="label label-warning"><i class="icon-warning-sign icon-white"></i>
|
||||||
{% if motion.version.version_number < motion.last_version.version_number %}
|
{% 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>
|
{% 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>
|
||||||
@ -64,7 +63,6 @@
|
|||||||
{% 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>
|
{% 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 %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<!-- Text -->
|
<!-- Text -->
|
||||||
<h4>{% trans "Motion text" %}:</h4>
|
<h4>{% trans "Motion text" %}:</h4>
|
||||||
{{ motion.version.text|linebreaks }}
|
{{ motion.version.text|linebreaks }}
|
||||||
@ -283,5 +281,4 @@
|
|||||||
</div> <!--/span-->
|
</div> <!--/span-->
|
||||||
</div> <!--/row-->
|
</div> <!--/row-->
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -40,6 +40,11 @@ urlpatterns = patterns('openslides.motion.views',
|
|||||||
name='motion_delete',
|
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+)/$',
|
url(r'^(?P<pk>\d+)/version/(?P<version_number>\d+)/$',
|
||||||
'motion_detail',
|
'motion_detail',
|
||||||
name='motion_version_detail',
|
name='motion_version_detail',
|
||||||
|
@ -35,10 +35,15 @@ from openslides.agenda.models import Item
|
|||||||
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll,
|
||||||
MotionVersion, State, WorkflowError, Category)
|
MotionVersion, State, WorkflowError, Category)
|
||||||
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin,
|
||||||
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin)
|
MotionDisableVersioningMixin, ConfigForm, MotionCategoryMixin,
|
||||||
|
MotionIdentifierMixin)
|
||||||
from .pdf import motions_to_pdf, motion_to_pdf
|
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):
|
class MotionListView(ListView):
|
||||||
"""View, to list all motions."""
|
"""View, to list all motions."""
|
||||||
permission_required = 'motion.can_see_motion'
|
permission_required = 'motion.can_see_motion'
|
||||||
@ -98,6 +103,16 @@ class MotionMixin(object):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
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):
|
def post_save(self, form):
|
||||||
"""Save the submitter an the supporter so the motion."""
|
"""Save the submitter an the supporter so the motion."""
|
||||||
super(MotionMixin, self).post_save(form)
|
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
|
will be mixed in dependence of some config values. See motion.forms
|
||||||
for more information on the mixins.
|
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'):
|
if self.request.user.has_perm('motion.can_manage_motion'):
|
||||||
form_classes.append(MotionSubmitterMixin)
|
form_classes.append(MotionSubmitterMixin)
|
||||||
form_classes.append(MotionCategoryMixin)
|
form_classes.append(MotionCategoryMixin)
|
||||||
@ -262,7 +283,32 @@ class VersionDiffView(GetVersionMixin, DetailView):
|
|||||||
|
|
||||||
version_diff = VersionDiffView.as_view()
|
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.
|
"""View to support or unsupport a motion.
|
||||||
|
|
||||||
If self.support is True, the view will append a request.user to the supporter list.
|
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:
|
if self.reset:
|
||||||
self.object.reset_state()
|
self.object.reset_state()
|
||||||
else:
|
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?
|
except WorkflowError, e: # TODO: Is a WorkflowError still possible here?
|
||||||
messages.error(request, e)
|
messages.error(request, e)
|
||||||
else:
|
else:
|
||||||
|
@ -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()
|
|
@ -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
|
from django.utils.translation import ugettext_noop
|
||||||
|
|
||||||
|
import openslides.participant.signals
|
||||||
|
|
||||||
|
|
||||||
NAME = ugettext_noop('Participant')
|
NAME = ugettext_noop('Participant')
|
||||||
|
@ -97,21 +97,8 @@ def import_users(csv_file):
|
|||||||
return (count_success, error_messages)
|
return (count_success, error_messages)
|
||||||
|
|
||||||
|
|
||||||
def get_or_create_registered_group():
|
def get_registered_group():
|
||||||
registered, created = Group.objects.get_or_create(
|
"""
|
||||||
name__iexact='Registered', defaults={'name': 'Registered'})
|
Returns the Group 'Registered'. Upper and lower case is possible.
|
||||||
if created:
|
"""
|
||||||
registered.permissions = Permission.objects.filter(
|
return Group.objects.get(name__iexact='Registered')
|
||||||
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
|
|
||||||
|
@ -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"
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
@ -19,7 +19,7 @@ from openslides.utils.forms import (
|
|||||||
CssClassMixin, LocalizedModelMultipleChoiceField)
|
CssClassMixin, LocalizedModelMultipleChoiceField)
|
||||||
|
|
||||||
from openslides.participant.models import User, Group
|
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):
|
class UserCreateForm(forms.ModelForm, CssClassMixin):
|
||||||
@ -30,7 +30,7 @@ class UserCreateForm(forms.ModelForm, CssClassMixin):
|
|||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
if kwargs.get('instance', None) is None:
|
if kwargs.get('instance', None) is None:
|
||||||
initial = kwargs.setdefault('initial', {})
|
initial = kwargs.setdefault('initial', {})
|
||||||
registered = get_or_create_registered_group()
|
registered = get_registered_group()
|
||||||
initial['groups'] = [registered.pk]
|
initial['groups'] = [registered.pk]
|
||||||
super(UserCreateForm, self).__init__(*args, **kwargs)
|
super(UserCreateForm, self).__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -257,9 +257,9 @@ def djangogroup_post_save(sender, instance, signal, *args, **kwargs):
|
|||||||
|
|
||||||
@receiver(signals.post_save, sender=User)
|
@receiver(signals.post_save, sender=User)
|
||||||
def user_post_save(sender, instance, *args, **kwargs):
|
def user_post_save(sender, instance, *args, **kwargs):
|
||||||
from openslides.participant.api import get_or_create_registered_group
|
|
||||||
if not kwargs['created']:
|
if not kwargs['created']:
|
||||||
return
|
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.groups.add(registered)
|
||||||
instance.save()
|
instance.save()
|
||||||
|
70
openslides/participant/signals.py
Normal file
70
openslides/participant/signals.py
Normal 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)
|
25
openslides/utils/management/commands/syncdb.py
Normal file
25
openslides/utils/management/commands/syncdb.py
Normal 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
27
openslides/utils/test.py
Normal 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
|
@ -10,10 +10,10 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
from django.test.client import Client
|
from django.test.client import Client
|
||||||
from django.db.models.query import EmptyQuerySet
|
from django.db.models.query import EmptyQuerySet
|
||||||
|
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
from openslides.projector.api import get_active_slide
|
from openslides.projector.api import get_active_slide
|
||||||
from openslides.participant.models import User
|
from openslides.participant.models import User
|
||||||
from openslides.agenda.models import Item
|
from openslides.agenda.models import Item
|
||||||
@ -95,7 +95,6 @@ class ItemTest(TestCase):
|
|||||||
self.assertEqual(self.item5.print_related_type(), 'Releateditem')
|
self.assertEqual(self.item5.print_related_type(), 'Releateditem')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class ViewTest(TestCase):
|
class ViewTest(TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.item1 = Item.objects.create(title='item1')
|
self.item1 = Item.objects.create(title='item1')
|
||||||
|
@ -8,8 +8,7 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
: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.participant.models import User
|
||||||
from openslides.config.models import config
|
from openslides.config.models import config
|
||||||
from openslides.motion.models import Motion, Workflow, State
|
from openslides.motion.models import Motion, Workflow, State
|
||||||
@ -113,18 +112,18 @@ class ModelTest(TestCase):
|
|||||||
|
|
||||||
self.motion.state = State.objects.get(pk=6)
|
self.motion.state = State.objects.get(pk=6)
|
||||||
self.assertEqual(self.motion.state.name, 'permitted')
|
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):
|
with self.assertRaises(WorkflowError):
|
||||||
self.motion.support(self.test_user)
|
self.motion.support(self.test_user)
|
||||||
with self.assertRaises(WorkflowError):
|
with self.assertRaises(WorkflowError):
|
||||||
self.motion.unsupport(self.test_user)
|
self.motion.unsupport(self.test_user)
|
||||||
|
|
||||||
def test_new_states_or_workflows(self):
|
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)
|
state_1 = State.objects.create(name='S1', workflow=workflow_1)
|
||||||
workflow_1.first_state = state_1
|
workflow_1.first_state = state_1
|
||||||
workflow_1.save()
|
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)
|
state_2 = State.objects.create(name='S2', workflow=workflow_2)
|
||||||
workflow_2.first_state = state_2
|
workflow_2.first_state = state_2
|
||||||
workflow_2.save()
|
workflow_2.save()
|
||||||
|
@ -7,9 +7,9 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
:license: GNU GPL, see LICENSE for more details.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
from openslides import get_version, get_git_commit_id
|
from openslides import get_version, get_git_commit_id
|
||||||
|
from openslides.utils.test import TestCase
|
||||||
|
|
||||||
|
|
||||||
class InitTest(TestCase):
|
class InitTest(TestCase):
|
||||||
def test_get_version(self):
|
def test_get_version(self):
|
||||||
|
@ -10,8 +10,7 @@
|
|||||||
:license: GNU GPL, see LICENSE for more details.
|
: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.utils.person import get_person, Persons
|
||||||
from openslides.participant.api import gen_username, gen_password
|
from openslides.participant.api import gen_username, gen_password
|
||||||
from openslides.participant.models import User, Group
|
from openslides.participant.models import User, Group
|
||||||
|
Loading…
Reference in New Issue
Block a user