Merge pull request #3785 from FinnStutzenstein/no-changeable-first-state

Do not allow changing a workflow's first state (closes #3778)
This commit is contained in:
Emanuel Schütze 2018-08-23 09:24:29 +02:00 committed by GitHub
commit 768c97e89c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 6 additions and 92 deletions

View File

@ -15,7 +15,7 @@ Motions:
- New possibility to sort submitters [#3647]. - New possibility to sort submitters [#3647].
- New representation of amendments (paragraph based creation, new diff - New representation of amendments (paragraph based creation, new diff
and list views for amendments) [#3637]. and list views for amendments) [#3637].
- New feature to customize workflows and states [#3772]. - New feature to customize workflows and states [#3772, #3785].
- New config options to show logos on the right side in PDF [#3768]. - New config options to show logos on the right side in PDF [#3768].
- New table of contents with page numbers and categories in PDF [#3766]. - New table of contents with page numbers and categories in PDF [#3766].
- New teporal field "modified final version" where the final version can - New teporal field "modified final version" where the final version can

View File

@ -101,12 +101,11 @@ class WorkflowSerializer(ModelSerializer):
Serializer for motion.models.Workflow objects. Serializer for motion.models.Workflow objects.
""" """
states = StateSerializer(many=True, read_only=True) states = StateSerializer(many=True, read_only=True)
# The first_state is checked in the update() method
first_state = PrimaryKeyRelatedField(queryset=State.objects.all(), required=False)
class Meta: class Meta:
model = Workflow model = Workflow
fields = ('id', 'name', 'states', 'first_state',) fields = ('id', 'name', 'states', 'first_state',)
read_only_fields = ('first_state',)
@transaction.atomic @transaction.atomic
def create(self, validated_data): def create(self, validated_data):
@ -127,17 +126,6 @@ class WorkflowSerializer(ModelSerializer):
workflow.save() workflow.save()
return workflow return workflow
@transaction.atomic
def update(self, workflow, validated_data):
"""
Check, if the first state is in the right workflow.
"""
first_state = validated_data.get('first_state')
if first_state is not None:
if workflow.pk != first_state.workflow.pk:
raise ValidationError({'detail': 'You cannot select a state which is not in the workflow as the first state.'})
return super().update(workflow, validated_data)
class MotionCommentsJSONSerializerField(Field): class MotionCommentsJSONSerializerField(Field):
""" """

View File

@ -54,7 +54,7 @@ angular.module('OpenSlidesApp.motions', [
name: 'motions/workflow', name: 'motions/workflow',
methods: { methods: {
getFirstState: function () { getFirstState: function () {
return DS.get('motions/state', this.first_state); return DS.get('motions/state', this.first_state_id);
}, },
}, },
relations: { relations: {

View File

@ -120,13 +120,6 @@ angular.module('OpenSlidesApp.motions.workflow', [])
}); });
}; };
$scope.setFirstState = function (state) {
$scope.workflow.first_state = state.id;
Workflow.save($scope.workflow).then(null, function (error) {
$scope.alert = ErrorMessage.forAlert(error);
});
};
// Save expand state so the session // Save expand state so the session
if ($sessionStorage.motionStateTableExpandState) { if ($sessionStorage.motionStateTableExpandState) {
$scope.toggleExpandContent(); $scope.toggleExpandContent();

View File

@ -24,22 +24,9 @@
</h1> </h1>
</div> </div>
<div class="title"> <div class="title">
<h3 ng-mouseover="firstStateHover=true" ng-mouseleave="firstStateHover=false"> <h3>
<translate>First state</translate>: <translate>First state</translate>:
{{ workflow.getFirstState().name | translate }} {{ workflow.getFirstState().name | translate }}
<span uib-dropdown>
<span id="firstStateDropdown" class="pointer" uib-dropdown-toggle>
<i class="fa fa-cog" ng-if="firstStateHover"></i>
</span>
<ul class="dropdown-menu" aria-labelledby="firstStateDropdown">
<li ng-repeat="state in workflow.states">
<a href ng-click="setFirstState(state)">
<i class="fa fa-check" ng-if="workflow.first_state === state.id"></i>
{{ state.name | translate }}
</a>
</li>
</ul>
</span>
</h3> </h3>
</div> </div>
</div> </div>
@ -67,7 +54,7 @@
<i class="fa fa-pencil fa-lg"></i></a> <i class="fa fa-pencil fa-lg"></i></a>
&nbsp; &nbsp;
<!--delete--> <!--delete-->
<a href="" class="text-danger" ng-if="state.id !== workflow.first_state" <a href="" class="text-danger" ng-if="state.id !== workflow.first_state_id"
ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br> ng-bootbox-confirm="{{ 'Are you sure you want to delete this entry?' | translate }}<br>
<b>{{ state.name | translate }}</b>" <b>{{ state.name | translate }}</b>"
ng-bootbox-confirm-action="delete(state)"> ng-bootbox-confirm-action="delete(state)">

View File

@ -919,13 +919,6 @@ class WorkflowViewSet(ModelViewSet, ProtectedErrorMessageMixin):
result = False result = False
return result return result
def create(self, *args, **kwargs):
try:
response = super().create(*args, **kwargs)
except WorkflowError as e:
raise ValidationError({'detail': e.args[0]})
return response
def destroy(self, *args, **kwargs): def destroy(self, *args, **kwargs):
""" """
Customized view endpoint to delete a motion poll. Customized view endpoint to delete a motion poll.

View File

@ -1258,21 +1258,6 @@ class CreateWorkflow(TestCase):
first_state = workflow.first_state first_state = workflow.first_state
self.assertEqual(type(first_state), State) self.assertEqual(type(first_state), State)
def test_creation_with_wrong_first_state(self):
response = self.client.post(
reverse('workflow-list'),
{'name': 'test_name_OoCoo3MeiT9li5Iengu9',
'first_state': 1})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
def test_creation_with_not_existing_first_state(self):
Workflow.objects.all().delete()
response = self.client.post(
reverse('workflow-list'),
{'name': 'test_name_OoCoo3MeiT9li5Iengu9',
'first_state': 49})
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
class UpdateWorkflow(TestCase): class UpdateWorkflow(TestCase):
""" """
@ -1292,38 +1277,6 @@ class UpdateWorkflow(TestCase):
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(workflow.name, 'test_name_wofi38DiWLT"8d3lwfo3') self.assertEqual(workflow.name, 'test_name_wofi38DiWLT"8d3lwfo3')
def test_change_first_state_correct(self):
first_state = self.workflow.first_state
other_workflow_state = self.workflow.states.exclude(pk=first_state.pk).first()
response = self.client.patch(
reverse('workflow-detail', args=[self.workflow.pk]),
{'first_state': other_workflow_state.pk})
workflow = Workflow.objects.get(pk=self.workflow.id)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(workflow.first_state, other_workflow_state)
def test_change_first_state_not_existing(self):
first_state = self.workflow.first_state
response = self.client.patch(
reverse('workflow-detail', args=[self.workflow.pk]),
{'first_state': 42})
workflow = Workflow.objects.get(pk=self.workflow.id)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(workflow.first_state, first_state)
def test_change_first_state_wrong_workflow(self):
first_state = self.workflow.first_state
other_workflow = Workflow.objects.exclude(pk=self.workflow.pk).first()
response = self.client.patch(
reverse('workflow-detail', args=[self.workflow.pk]),
{'first_state': other_workflow.first_state.pk})
workflow = Workflow.objects.get(pk=self.workflow.id)
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
self.assertEqual(workflow.first_state, first_state)
class DeleteWorkflow(TestCase): class DeleteWorkflow(TestCase):
""" """