Merge pull request #651 from normanjaeckel/Workflow_Adjustablility_2

Workflow adjustablility (new feature)
This commit is contained in:
Oskar Hahn 2013-05-16 04:35:58 -07:00
commit 9a881eceeb
6 changed files with 47 additions and 10 deletions

View File

@ -17,7 +17,7 @@ from openslides.config.api import config
from openslides.utils.forms import CssClassMixin from openslides.utils.forms import CssClassMixin
from openslides.utils.forms import CleanHtmlFormMixin from openslides.utils.forms import CleanHtmlFormMixin
from openslides.utils.person import PersonFormField, MultiplePersonFormField from openslides.utils.person import PersonFormField, MultiplePersonFormField
from .models import Motion, Category from .models import Motion, Category, Workflow
class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm): class BaseMotionForm(CleanHtmlFormMixin, CssClassMixin, forms.ModelForm):
@ -137,6 +137,19 @@ class MotionIdentifierMixin(forms.ModelForm):
fields = ('identifier',) fields = ('identifier',)
class MotionSetWorkflowMixin(forms.ModelForm):
"""
Mixin to let the user change the workflow of the motion. When he does
so, the motion's state is reset.
"""
set_workflow = forms.ModelChoiceField(
queryset=Workflow.objects.all(),
required=False,
label=ugettext_lazy('Workflow'),
help_text=ugettext_lazy('Set a specific workflow to switch to it. If you do so, the state of the motion will be reset.'))
class MotionImportForm(CssClassMixin, forms.Form): class MotionImportForm(CssClassMixin, forms.Form):
""" """
Form for motion import via csv file. Form for motion import via csv file.

View File

@ -421,13 +421,16 @@ class Motion(SlideMixin, models.Model):
self.set_identifier() self.set_identifier()
self.state = state self.state = state
def reset_state(self): def reset_state(self, workflow=None):
""" """
Set the state to the default state. Set the state to the default state.
If the motion is new, it chooses the default workflow from config. If the motion is new and workflow is None, it chooses the default
workflow from config.
""" """
if self.state: if workflow:
new_state = workflow.first_state
elif self.state:
new_state = self.state.workflow.first_state new_state = self.state.workflow.first_state
else: else:
new_state = (Workflow.objects.get(pk=config['motion_workflow']).first_state or new_state = (Workflow.objects.get(pk=config['motion_workflow']).first_state or

View File

@ -187,10 +187,11 @@ def create_builtin_workflows(sender, **kwargs):
versioning=True) versioning=True)
state_2_9 = State.objects.create(name=ugettext_noop('needs review'), state_2_9 = State.objects.create(name=ugettext_noop('needs review'),
workflow=workflow_2, workflow=workflow_2,
action_word=ugettext_noop('Needs review'),
versioning=True) versioning=True)
state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'), state_2_10 = State.objects.create(name=ugettext_noop('rejected (not authorized)'),
workflow=workflow_2, workflow=workflow_2,
action_word=ugettext_noop('reject (not authorized)'), action_word=ugettext_noop('Reject (not authorized)'),
versioning=True) versioning=True)
state_2_1.next_states.add(state_2_2, state_2_5, state_2_10) 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_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)

View File

@ -57,7 +57,7 @@
<div class="row-fluid"> <div class="row-fluid">
<div class="span8"> <div class="span8">
{# TODO: show only for complex workflow #} {# TODO: show only for workflow with versioning #}
{% if motion.version.version_number < motion.last_version.version_number %} {% if motion.version.version_number < motion.last_version.version_number %}
<span class="label label-warning"> <span class="label label-warning">
<i class="icon-warning-sign icon-white"></i> {% trans "This is not the newest version." %} <i class="icon-warning-sign icon-white"></i> {% trans "This is not the newest version." %}
@ -289,7 +289,7 @@
<h4>{% trans "Manage motion" %}</h4> <h4>{% trans "Manage motion" %}</h4>
<div class="btn-group btn-group-vertical"> <div class="btn-group btn-group-vertical">
{% for state in motion.state.next_states.all %} {% for state in motion.state.next_states.all %}
<a href="{% url 'motion_set_state' motion.pk state.pk %}" class="btn btn-small">{% trans state.name %}</a> <a href="{% url 'motion_set_state' motion.pk state.pk %}" class="btn btn-small">{% trans state.get_action_word %}</a>
{% endfor %} {% endfor %}
</div> </div>
<p></p> <p></p>

View File

@ -38,7 +38,7 @@ 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, MotionCategoryMixin, MotionDisableVersioningMixin, MotionCategoryMixin,
MotionIdentifierMixin, MotionImportForm) MotionIdentifierMixin, MotionSetWorkflowMixin, MotionImportForm)
from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf
from .csv_import import import_motions from .csv_import import import_motions
@ -91,8 +91,9 @@ class MotionMixin(object):
def manipulate_object(self, form): def manipulate_object(self, form):
""" """
Save the version data into the motion object before it is saved in Saves the version data into the motion object before it is saved in
the Database. the Database. Does also set category, identifier and new workflow
if given.
""" """
super(MotionMixin, self).manipulate_object(form) super(MotionMixin, self).manipulate_object(form)
for attr in ['title', 'text', 'reason']: for attr in ['title', 'text', 'reason']:
@ -120,6 +121,10 @@ class MotionMixin(object):
except KeyError: except KeyError:
pass pass
workflow = form.cleaned_data.get('set_workflow', None)
if workflow:
self.object.reset_state(workflow)
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.
@ -161,6 +166,8 @@ class MotionMixin(object):
if self.object: if self.object:
if config['motion_allow_disable_versioning'] and self.object.state.versioning: if config['motion_allow_disable_versioning'] and self.object.state.versioning:
form_classes.append(MotionDisableVersioningMixin) form_classes.append(MotionDisableVersioningMixin)
if self.request.user.has_perm('motion.can_manage_motion'):
form_classes.append(MotionSetWorkflowMixin)
return type('MotionForm', tuple(form_classes), {}) return type('MotionForm', tuple(form_classes), {})

View File

@ -219,6 +219,19 @@ class TestMotionUpdateView(MotionViewTestCase):
motion = Motion.objects.get(pk=self.motion1.pk) motion = Motion.objects.get(pk=self.motion1.pk)
self.assertEqual(motion.versions.count(), 1) self.assertEqual(motion.versions.count(), 1)
def test_set_another_workflow(self):
self.assertEqual(self.motion1.state.workflow.pk, 1)
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
'text': 'eequei1Tee1aegeNgee0',
'submitter': self.admin})
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 1)
response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2',
'text': 'eequei1Tee1aegeNgee0',
'submitter': self.admin,
'set_workflow': 2})
self.assertRedirects(response, '/motion/1/')
self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2)
class TestMotionDeleteView(MotionViewTestCase): class TestMotionDeleteView(MotionViewTestCase):
def test_get(self): def test_get(self):