diff --git a/openslides/motion/forms.py b/openslides/motion/forms.py index 2a177e7a8..f738fa75c 100644 --- a/openslides/motion/forms.py +++ b/openslides/motion/forms.py @@ -140,17 +140,17 @@ class MotionIdentifierMixin(forms.ModelForm): fields = ('identifier',) -class MotionSetWorkflowMixin(forms.ModelForm): +class MotionWorkflowMixin(forms.ModelForm): """ - Mixin to let the user change the workflow of the motion. When he does - so, the motion's state is reset. + Mixin to let the user change the workflow of the motion. """ - set_workflow = LocalizedModelChoiceField( + workflow = LocalizedModelChoiceField( queryset=Workflow.objects.all(), - required=False, + empty_label=None, label=ugettext_lazy('Workflow'), - help_text=ugettext_lazy('Set a specific workflow to switch to it. If you do so, the state of the motion will be reset.')) + 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): diff --git a/openslides/motion/models.py b/openslides/motion/models.py index 3dd2683aa..8c819b99f 100644 --- a/openslides/motion/models.py +++ b/openslides/motion/models.py @@ -133,7 +133,7 @@ class Motion(SlideMixin, models.Model): # Solves the problem, that there can only be one motion with an empty # string as identifier. - if self.identifier is '': + if not self.identifier and isinstance(self.identifier, basestring): self.identifier = None super(Motion, self).save(*args, **kwargs) @@ -432,7 +432,7 @@ class Motion(SlideMixin, models.Model): """ Set the state of the motion. - State can be the id of a state object or a state object. + 'state' can be the id of a state object or a state object. """ if type(state) is int: state = State.objects.get(pk=state) @@ -445,10 +445,15 @@ class Motion(SlideMixin, models.Model): """ Set the state to the default state. + 'workflow' can be a workflow, an id of a workflow or None. + If the motion is new and workflow is None, it chooses the default workflow from config. """ - if workflow: + if type(workflow) is int: + workflow = Workflow.objects.get(pk=workflow) + + if workflow is not None: new_state = workflow.first_state elif self.state: new_state = self.state.workflow.first_state diff --git a/openslides/motion/views.py b/openslides/motion/views.py index 544f58d15..7d68dd1ae 100644 --- a/openslides/motion/views.py +++ b/openslides/motion/views.py @@ -39,7 +39,7 @@ from .models import (Motion, MotionSubmitter, MotionSupporter, MotionPoll, MotionVersion, State, WorkflowError, Category) from .forms import (BaseMotionForm, MotionSubmitterMixin, MotionSupporterMixin, MotionDisableVersioningMixin, MotionCategoryMixin, - MotionIdentifierMixin, MotionSetWorkflowMixin, MotionImportForm) + MotionIdentifierMixin, MotionWorkflowMixin, MotionImportForm) from .pdf import motions_to_pdf, motion_to_pdf, motion_poll_to_pdf from .csv_import import import_motions @@ -98,19 +98,10 @@ class MotionEditMixin(object): Saves the CreateForm or UpdateForm into a motion object. """ self.object = form.save(commit=False) - - if type(self) == MotionUpdateView: - # Decide if a new version is saved to the database - if (self.object.state.versioning and - not form.cleaned_data.get('disable_versioning', False)): - version = self.object.get_new_version() - else: - version = self.object.get_last_version() - else: - version = self.object.get_new_version() + self.manipulate_object(form) for attr in ['title', 'text', 'reason']: - setattr(version, attr, form.cleaned_data[attr]) + setattr(self.version, attr, form.cleaned_data[attr]) try: self.object.category = form.cleaned_data['category'] @@ -122,11 +113,7 @@ class MotionEditMixin(object): except KeyError: pass - workflow = form.cleaned_data.get('set_workflow', None) - if workflow: - self.object.reset_state(workflow) - - self.object.save(use_version=version) + self.object.save(use_version=self.version) # Save the submitter an the supporter so the motion. # TODO: Only delete and save neccessary submitters and supporters @@ -163,11 +150,12 @@ class MotionEditMixin(object): form_classes.append(MotionCategoryMixin) if config['motion_min_supporters'] > 0: form_classes.append(MotionSupporterMixin) + form_classes.append(MotionWorkflowMixin) + if self.object: if config['motion_allow_disable_versioning'] and self.object.state.versioning: form_classes.append(MotionDisableVersioningMixin) - if self.request.user.has_perm('motion.can_manage_motion'): - form_classes.append(MotionSetWorkflowMixin) + return type('MotionForm', tuple(form_classes), {}) @@ -195,10 +183,23 @@ class MotionCreateView(MotionEditMixin, CreateView): """ response = super(MotionCreateView, self).form_valid(form) self.object.write_log([ugettext_noop('Motion created')], self.request.user) - if not 'submitter' in form.cleaned_data: + if (not 'submitter' in form.cleaned_data or + not form.cleaned_data['submitter']): self.object.add_submitter(self.request.user) return response + def get_initial(self): + initial = super(MotionCreateView, self).get_initial() + if self.request.user.has_perm('motion.can_manage_motion'): + initial['workflow'] = config['motion_workflow'] + return initial + + def manipulate_object(self, form): + self.version = self.object.get_new_version() + + workflow = form.cleaned_data.get('workflow', config['motion_workflow']) + self.object.reset_state(workflow) + motion_create = MotionCreateView.as_view() @@ -226,6 +227,25 @@ class MotionUpdateView(MotionEditMixin, UpdateView): self.object.write_log([ugettext_noop('All supporters removed')], self.request.user) return response + def get_initial(self): + initial = super(MotionUpdateView, self).get_initial() + if self.request.user.has_perm('motion.can_manage_motion'): + initial['workflow'] = self.object.state.workflow + return initial + + def manipulate_object(self, form): + workflow = form.cleaned_data.get('workflow', None) + if (workflow is not None and + workflow != self.object.state.workflow): + self.object.reset_state(workflow) + + # Decide if a new version is saved to the database + if (self.object.state.versioning and + not form.cleaned_data.get('disable_versioning', False)): + self.version = self.object.get_new_version() + else: + self.version = self.object.get_last_version() + motion_edit = MotionUpdateView.as_view() diff --git a/tests/motion/test_views.py b/tests/motion/test_views.py index 6cda947c9..fe01ccbd2 100644 --- a/tests/motion/test_views.py +++ b/tests/motion/test_views.py @@ -85,7 +85,7 @@ class TestMotionCreateView(MotionViewTestCase): response = self.admin_client.post(self.url, {'title': 'new motion', 'text': 'motion text', 'reason': 'motion reason', - 'submitter': self.admin.person_id}) + 'workflow': 1}) self.assertEqual(response.status_code, 302) self.assertTrue(Motion.objects.filter(versions__title='new motion').exists()) @@ -151,7 +151,8 @@ class TestMotionUpdateView(MotionViewTestCase): response = self.admin_client.post(self.url, {'title': 'new motion_title', 'text': 'motion text', 'reason': 'motion reason', - 'submitter': self.admin.person_id}) + 'submitter': self.admin.person_id, + 'workflow': 1}) self.assertRedirects(response, '/motion/1/') motion = Motion.objects.get(pk=1) self.assertEqual(motion.title, 'new motion_title') @@ -172,7 +173,8 @@ class TestMotionUpdateView(MotionViewTestCase): def test_versioning(self): self.assertFalse(self.motion1.state.versioning) - versioning_state = State.objects.create(name='automatic_versioning', workflow=self.motion1.state.workflow, versioning=True) + workflow = self.motion1.state.workflow + versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) self.motion1.state = versioning_state self.motion1.save() motion = Motion.objects.get(pk=self.motion1.pk) @@ -182,6 +184,7 @@ class TestMotionUpdateView(MotionViewTestCase): response = self.admin_client.post(self.url, {'title': 'another new motion_title', 'text': 'another motion text', 'reason': 'another motion reason', + 'workflow': workflow.pk, 'submitter': self.admin.person_id}) self.assertRedirects(response, '/motion/1/') motion = Motion.objects.get(pk=self.motion1.pk) @@ -189,7 +192,8 @@ class TestMotionUpdateView(MotionViewTestCase): def test_disable_versioning(self): self.assertFalse(self.motion1.state.versioning) - versioning_state = State.objects.create(name='automatic_versioning', workflow=self.motion1.state.workflow, versioning=True) + workflow = self.motion1.state.workflow + versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) self.motion1.state = versioning_state self.motion1.save() motion = Motion.objects.get(pk=self.motion1.pk) @@ -201,6 +205,7 @@ class TestMotionUpdateView(MotionViewTestCase): 'text': 'another motion text', 'reason': 'another motion reason', 'submitter': self.admin.person_id, + 'workflow': workflow.pk, 'disable_versioning': 'true'}) self.assertRedirects(response, '/motion/1/') motion = Motion.objects.get(pk=self.motion1.pk) @@ -208,7 +213,8 @@ class TestMotionUpdateView(MotionViewTestCase): def test_no_versioning_without_new_data(self): self.assertFalse(self.motion1.state.versioning) - versioning_state = State.objects.create(name='automatic_versioning', workflow=self.motion1.state.workflow, versioning=True) + workflow = self.motion1.state.workflow + versioning_state = State.objects.create(name='automatic_versioning', workflow=workflow, versioning=True) self.motion1.state = versioning_state self.motion1.title = 'Chah4kaaKasiVuishi5x' self.motion1.text = 'eedieFoothae2iethuo3' @@ -221,6 +227,7 @@ class TestMotionUpdateView(MotionViewTestCase): response = self.admin_client.post(self.url, {'title': 'Chah4kaaKasiVuishi5x', 'text': 'eedieFoothae2iethuo3', 'reason': 'ier2laiy1veeGoo0mau2', + 'workflow': workflow.pk, 'submitter': self.admin.person_id}) self.assertRedirects(response, '/motion/1/') motion = Motion.objects.get(pk=self.motion1.pk) @@ -235,7 +242,7 @@ class TestMotionUpdateView(MotionViewTestCase): response = self.admin_client.post(self.url, {'title': 'oori4KiaghaeSeuzaim2', 'text': 'eequei1Tee1aegeNgee0', 'submitter': self.admin.person_id, - 'set_workflow': 2}) + 'workflow': 2}) self.assertRedirects(response, '/motion/1/') self.assertEqual(Motion.objects.get(pk=self.motion1.pk).state.workflow.pk, 2)