From 798126551b9a72f2a26d08a69f62f2812fa68851 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Tue, 13 Oct 2015 21:28:14 +0200 Subject: [PATCH 1/5] Motions template improvements. - Added quickedit form for motion list. --- .../templates/motions/motion-detail.html | 8 ++- .../static/templates/motions/motion-form.html | 55 +++++++++++-------- 2 files changed, 39 insertions(+), 24 deletions(-) diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index c4a2c8183..ef8e7e0cc 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -5,7 +5,13 @@ | Version {{ motion.active_version }} -{{ motion.tags }} + + + + {{ tag.name }} + +   + {{ motion.agenda_item }} diff --git a/openslides/motions/static/templates/motions/motion-form.html b/openslides/motions/static/templates/motions/motion-form.html index 840defd7c..c89766836 100644 --- a/openslides/motions/static/templates/motions/motion-form.html +++ b/openslides/motions/static/templates/motions/motion-form.html @@ -21,19 +21,14 @@
- - +
@@ -55,34 +50,48 @@
- + + + {{ $item.name }} + + + {{ tag.name }} + +
+ + + {{ $item.name }} + + + {{ file.title }} + +
- -
- - - - {{ $select.selected.get_short_name() }} + +
+ + + + {{ $item.get_full_name() }} -
- +
- +
From 2495ba609b7d49c07f8b20ae2a41c188283255ac Mon Sep 17 00:00:00 2001 From: Oskar Hahn Date: Tue, 13 Oct 2015 21:23:21 +0200 Subject: [PATCH 2/5] create MotionPoll in JS --- openslides/motions/serializers.py | 1 + .../motions/static/js/motions/motions.js | 50 ++++++++++++++++--- .../templates/motions/motion-detail.html | 6 +++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index f86689280..67eddad0c 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -97,6 +97,7 @@ class MotionPollSerializer(ModelSerializer): model = MotionPoll fields = ( 'id', + 'motion', 'yes', 'no', 'abstain', diff --git a/openslides/motions/static/js/motions/motions.js b/openslides/motions/static/js/motions/motions.js index c08bc8cd3..0fa5f22a4 100644 --- a/openslides/motions/static/js/motions/motions.js +++ b/openslides/motions/static/js/motions/motions.js @@ -2,10 +2,20 @@ angular.module('OpenSlidesApp.motions', []) +.factory('MotionPoll', [ + 'DS', + function(DS) { + return DS.defineResource({ + name: 'motions/motionpoll', + }); + } +]) + .factory('Motion', [ 'DS', + 'MotionPoll', 'jsDataModel', - function(DS, jsDataModel) { + function(DS, MotionPoll, jsDataModel) { var name = 'motions/motion' return DS.defineResource({ name: name, @@ -70,6 +80,10 @@ angular.module('OpenSlidesApp.motions', []) localKeys: 'supporters_id', } ], + 'motions/motionpoll': { + localField: 'polls', + foreignKey: 'motion_id', + } } } }); @@ -252,12 +266,34 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) }; }) -.controller('MotionDetailCtrl', function($scope, Motion, Category, User, motion) { - Motion.bindOne(motion.id, $scope, 'motion'); - Category.bindAll({}, $scope, 'categories'); - User.bindAll({}, $scope, 'users'); - Motion.loadRelations(motion); -}) +.controller('MotionDetailCtrl', [ + '$scope', + 'Motion', + 'Category', + 'User', + 'motion', + '$http', + function($scope, Motion, Category, User, motion, $http) { + Motion.bindOne(motion.id, $scope, 'motion'); + Category.bindAll({}, $scope, 'categories'); + User.bindAll({}, $scope, 'users'); + Motion.loadRelations(motion); + + $scope.create_poll = function () { + $http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {}) + .success(function(data){ + $scope.alert.show = false; + }) + .error(function(data){ + $scope.alert = { type: 'danger', msg: data.detail, show: true }; + }); + } + + $scope.delete_poll = function (poll) { + poll.DSDestroy(); + } + } +]) .controller('MotionCreateCtrl', function($scope, $state, $http, Motion, Agenda, User, Category, Workflow, Tag, Mediafile) { diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index ef8e7e0cc..d9915a9f1 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -39,6 +39,12 @@
+Create Poll +
+ {{ poll.id }} + delete +
+

Text

From 5b37a21c87e773801bdcfc70ebf9597dccc356f8 Mon Sep 17 00:00:00 2001 From: Emanuel Schuetze Date: Thu, 15 Oct 2015 17:25:40 +0200 Subject: [PATCH 3/5] Motion detail updated for polls. bower.json: updated ui-select New QuickEdit feature in motion list. --- bower.json | 4 +- .../static/templates/agenda/item-list.html | 2 +- openslides/core/static/css/app.css | 24 ++- openslides/motions/models.py | 1 + .../motions/static/js/motions/motions.js | 198 +++++++++++++++--- .../templates/motions/motion-detail.html | 161 ++++++++++++-- .../static/templates/motions/motion-form.html | 9 +- .../static/templates/motions/motion-list.html | 173 +++++++++++---- .../static/templates/users/user-list.html | 2 +- 9 files changed, 477 insertions(+), 97 deletions(-) diff --git a/bower.json b/bower.json index 9c4fd3c94..6026df924 100644 --- a/bower.json +++ b/bower.json @@ -7,13 +7,13 @@ "jquery.cookie": "~1.4.1", "bootstrap-css-only": "~3.3.4", "angular": "~1.3.15", - "angular-bootstrap": "~0.13.0", + "angular-bootstrap": "~0.14.2", "angular-messages": "~1.3.15", "angular-animate": "~1.3.15", "angular-csv-import": "~0.0.15", "angular-loading-bar": "~0.7.1", "angular-ui-router": "~0.2.13", - "angular-ui-select": "~0.12", + "angular-ui-select": "~0.13", "angular-ui-switch": "~0.1.0", "angular-ui-tree": "~2.2.0", "angular-gettext": "~2.0.2", diff --git a/openslides/agenda/static/templates/agenda/item-list.html b/openslides/agenda/static/templates/agenda/item-list.html index 8c7b2aee8..084409fd8 100644 --- a/openslides/agenda/static/templates/agenda/item-list.html +++ b/openslides/agenda/static/templates/agenda/item-list.html @@ -71,7 +71,7 @@ - + diff --git a/openslides/core/static/css/app.css b/openslides/core/static/css/app.css index 758a4e8d7..e1e677347 100644 --- a/openslides/core/static/css/app.css +++ b/openslides/core/static/css/app.css @@ -49,6 +49,15 @@ body { color: red; font-weight: bold; } +.spacer { + margin-top: 7px; +} +.hoverActions { + font-size: 85%; +} +.hiddenDiv { + visibility: hidden; +} /* TODO: used by ng-fab-forms */ .validation-success { @@ -274,14 +283,25 @@ tr.offline td, li.offline { tr.activeline td, li.activeline, .projected { background-color: #bed4de; } +tr.selected td { + background-color: #ff9999; +} .nopadding { padding: 0; } .alert form { margin-bottom: 0; } -tr.total td { - border-top: 1px solid #333333; +.slimlist { + padding-left: 20px; +} +.smallhr { + margin-top: 2px; + margin-bottom: 2px; + border-color: #333333; +} +.resultcolumn { + font-weight: bold; } .nobr { white-space: nowrap; diff --git a/openslides/motions/models.py b/openslides/motions/models.py index fdb36aebd..a410c28cd 100644 --- a/openslides/motions/models.py +++ b/openslides/motions/models.py @@ -763,6 +763,7 @@ class State(RESTModelMixin, models.Model): This behavior can be changed by the form and view, e. g. via the MotionDisableVersioningMixin. """ + # TODO: preferred_for = ChoiceField leave_old_version_active = models.BooleanField(default=False) """If true, new versions are not automaticly set active.""" diff --git a/openslides/motions/static/js/motions/motions.js b/openslides/motions/static/js/motions/motions.js index 0fa5f22a4..e4ba67e26 100644 --- a/openslides/motions/static/js/motions/motions.js +++ b/openslides/motions/static/js/motions/motions.js @@ -4,9 +4,78 @@ angular.module('OpenSlidesApp.motions', []) .factory('MotionPoll', [ 'DS', - function(DS) { + 'Config', + 'jsDataModel', + function(DS, Config, jsDataModel) { return DS.defineResource({ name: 'motions/motionpoll', + useClass: jsDataModel, + relations: { + belongsTo: { + 'motions/motion': { + localField: 'motion', + localKey: 'motion_id', + } + } + }, + methods: { + getYesPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITHOUT_INVALID" && this.votesvalid > 0) { + return "(" + Math.round(this.yes * 100 / this.votesvalid * 10) / 10 + " %)"; + } else if (config == "WITH_INVALID" && this.votescast > 0) { + return "(" + Math.round(this.yes * 100 / (this.votescast) * 10) / 10 + " %)"; + } else { + return null; + } + }, + getNoPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITHOUT_INVALID" && this.votesvalid > 0) { + return "(" + Math.round(this.no * 100 / this.votesvalid * 10) / 10 + " %)"; + } else if (config == "WITH_INVALID" && this.votescast > 0) { + return "(" + Math.round(this.no * 100 / (this.votescast) * 10) / 10 + " %)"; + } else { + return null; + } + }, + getAbstainPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITHOUT_INVALID" && this.votesvalid > 0) { + return "(" + Math.round(this.abstain * 100 / this.votesvalid * 10) / 10 + " %)"; + } else if (config == "WITH_INVALID" && this.votescast > 0) { + return "(" + Math.round(this.abstain * 100 / (this.votescast) * 10) / 10 + " %)"; + } else { + return null; + } + }, + getVotesValidPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITHOUT_INVALID") { + return "(100 %)"; + } else if (config == "WITH_INVALID") { + return "(" + Math.round(this.votesvalid * 100 / (this.votescast) * 10) / 10 + " %)"; + } else { + return null; + } + }, + getVotesInvalidPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITH_INVALID") { + return "(" + Math.round(this.votesinvalid * 100 / (this.votescast) * 10) / 10 + " %)"; + } else { + return null; + } + }, + getVotesCastPercent: function () { + var config = Config.get('motions_poll_100_percent_base').value; + if (config == "WITH_INVALID") { + return "(100 %)"; + } else { + return null; + } + } + } }); } ]) @@ -239,46 +308,112 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) }) }) -.controller('MotionListCtrl', function($scope, Motion, Category, User) { - Motion.bindAll({}, $scope, 'motions'); - Category.bindAll({}, $scope, 'categories'); - User.bindAll({}, $scope, 'users'); +.controller('MotionListCtrl', [ + '$scope', + '$state', + 'Motion', + 'Category', + 'User', + function($scope, $state, Motion, Category, User) { + Motion.bindAll({}, $scope, 'motions'); + Category.bindAll({}, $scope, 'categories'); + User.bindAll({}, $scope, 'users'); - // setup table sorting - $scope.sortColumn = 'identifier'; - $scope.filterPresent = ''; - $scope.reverse = false; - // function to sort by clicked column - $scope.toggleSort = function ( column ) { - if ( $scope.sortColumn === column ) { - $scope.reverse = !$scope.reverse; - } - $scope.sortColumn = column; - }; + // setup table sorting + $scope.sortColumn = 'identifier'; + $scope.filterPresent = ''; + $scope.reverse = false; + // function to sort by clicked column + $scope.toggleSort = function ( column ) { + if ( $scope.sortColumn === column ) { + $scope.reverse = !$scope.reverse; + } + $scope.sortColumn = column; + }; - // save changed motion - $scope.save = function (motion) { - Motion.save(motion); - }; - // delete selected motion - $scope.delete = function (motion) { - Motion.destroy(motion.id); - }; -}) + // hover edit actions + $scope.hoverIn = function () { + $scope.showEditActions = true; + }; + $scope.hoverOut = function () { + $scope.showEditActions = false; + }; + + // save changed motion + $scope.update = function (motion) { + // get (unchanged) values from latest version for update method + motion.title = motion.getTitle(-1); + motion.text = motion.getText(-1); + motion.reason = motion.getReason(-1); + Motion.save(motion).then( + function(success) { + motion.quickEdit = false; + $scope.alert.show = false; + }, + function(error){ + var message = ''; + for (var e in error.data) { + message += e + ': ' + error.data[e] + ' '; + } + $scope.alert = { type: 'danger', msg: message, show: true }; + }); + }; + + // *** delete mode functions *** + $scope.isDeleteMode = false; + // check all checkboxes + $scope.checkAll = function () { + angular.forEach($scope.motions, function (motion) { + motion.selected = $scope.selectedAll; + }); + }; + // uncheck all checkboxes if isDeleteMode is closed + $scope.uncheckAll = function () { + if (!$scope.isDeleteMode) { + $scope.selectedAll = false; + angular.forEach($scope.motions, function (motion) { + motion.selected = false; + }); + } + }; + // delete selected motions + $scope.delete = function () { + angular.forEach($scope.motions, function (motion) { + if (motion.selected) + Motion.destroy(motion.id); + }); + $scope.isDeleteMode = false; + $scope.uncheckAll(); + }; + // delete single motion + $scope.deleteSingleMotion = function (motion) { + Motion.destroy(motion.id); + }; + } +]) .controller('MotionDetailCtrl', [ '$scope', 'Motion', 'Category', + 'Workflow', 'User', 'motion', '$http', - function($scope, Motion, Category, User, motion, $http) { + function($scope, Motion, Category, Workflow, User, motion, $http) { Motion.bindOne(motion.id, $scope, 'motion'); Category.bindAll({}, $scope, 'categories'); + Workflow.bindAll({}, $scope, 'workflows'); User.bindAll({}, $scope, 'users'); Motion.loadRelations(motion); + $scope.alert = {}; // TODO: show alert in template + $scope.update_state = function (state_id) { + $http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id}); + } + $scope.reset_state = function (state_id) { + $http.put('/rest/motions/motion/' + motion.id + '/set_state/', {}); + } $scope.create_poll = function () { $http.post('/rest/motions/motion/' + motion.id + '/create_poll/', {}) .success(function(data){ @@ -288,10 +423,19 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) $scope.alert = { type: 'danger', msg: data.detail, show: true }; }); } - $scope.delete_poll = function (poll) { poll.DSDestroy(); } + $scope.update_poll = function (poll) { + poll.DSUpdate({ + motion_id: motion.id, + votes: {"Yes": poll.yes, "No": poll.no, "Abstain": poll.abstain}, + votesvalid: poll.votesvalid, + votesinvalid: poll.votesinvalid, + votescast: poll.votescast + }); + poll.isEditMode = false; + } } ]) diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index d9915a9f1..3efb17c2a 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -6,13 +6,6 @@ - - - {{ tag.name }} - -   - - {{ motion.agenda_item }} -Create Poll -
- {{ poll.id }} - delete -
-

Text

-
{{ motion.getText() }}
+

Reason

-
{{ motion.getReason() }}
+
@@ -61,11 +48,151 @@ {{ submitter.get_full_name() }}
+
+

Supporters

+
    +
  1. + {{ supporters.get_full_name() }} +
+
+ +

State

+ {{ motion.state.name | translate }} + +
+
+ +
+
+ +
+
+ +

Voting result

+
    +
  1. + Vote + + +
    +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    +
    + +
    + +
    + + +
    +
    +
    + +
    + + Yes: +
    +
    {{ poll.yes }} {{poll.getYesPercent()}}
    + +
    + + No: +
    +
    {{ poll.no }} {{poll.getNoPercent()}}
    + +
    + + Abstain: +
    +
    {{ poll.abstain }} {{poll.getAbstainPercent()}}
    +
    + +
    +
    + + Valid votes: +
    +
    {{ poll.votesvalid }} {{poll.getVotesValidPercent()}}
    +
    + +
    +
    + + Invalid votes: +
    +
    {{ poll.votesinvalid }} {{poll.getVotesInvalidPercent()}}
    +
    +
    + +
    +
    + + Votes cast: +
    +
    {{ poll.votescast }} {{poll.getVotesCastPercent()}}
    +
    +
    +
+ +

Category

{{ motion.category.name }} -

Voting result

- - +

Tags

+ + + {{ tag.name }} + +   +
diff --git a/openslides/motions/static/templates/motions/motion-form.html b/openslides/motions/static/templates/motions/motion-form.html index c89766836..07b64f0d2 100644 --- a/openslides/motions/static/templates/motions/motion-form.html +++ b/openslides/motions/static/templates/motions/motion-form.html @@ -20,7 +20,7 @@
- + {{ $item.get_full_name() }} @@ -31,7 +31,7 @@
- +
@@ -69,13 +69,10 @@ {{ file.title }} -
- + {{ $item.get_full_name() }} diff --git a/openslides/motions/static/templates/motions/motion-list.html b/openslides/motions/static/templates/motions/motion-list.html index d6d9db6ec..2fad69694 100644 --- a/openslides/motions/static/templates/motions/motion-list.html +++ b/openslides/motions/static/templates/motions/motion-list.html @@ -21,7 +21,24 @@
- +
+ +
+ + + +
+ + + + Delete selected motions + + +
- - - Agenda item - - + + + + + Identifier - + Title - - + Submitters - + Category - - Actions + + State + + - - {{ motion.identifier }} - - {{ motion.getTitle() }} - - -
- {{ submitter.get_full_name() }}
-
- - {{ motion.category.name }} - - - + + + - - - - - - - - + + + + + {{ motion.identifier }} + + {{ motion.getTitle() }} +
+ Edit | + QuickEdit | + Delete +
+ +
+ {{ submitter.get_full_name() }}
+
+ + {{ motion.category.name }} + + {{ motion.state.name | translate }} + + +

{{ motion.getTitle() }} – Quick Edit

+ + {{alert.msg}} + +
+
+ + +
+
+ + +
+
+
+
+ + + + {{ $item.get_full_name() }} + + +
+
+
+
+
+ + + + {{ $item.name }} + + + {{ tag.name }} + + +
+
+
+
+
+ + + + {{ $item.get_full_name() }} + + +
+
+
+
+
+
+ + +
+
+
+   + + Edit motion... +
diff --git a/openslides/users/static/templates/users/user-list.html b/openslides/users/static/templates/users/user-list.html index 63b8a4097..466e5543d 100644 --- a/openslides/users/static/templates/users/user-list.html +++ b/openslides/users/static/templates/users/user-list.html @@ -40,7 +40,7 @@
-
+
Date: Tue, 3 Nov 2015 10:03:44 +0100 Subject: [PATCH 4/5] Fixed motion states, handled workflow field. --- .../assignments/static/js/assignments/site.js | 2 +- openslides/core/static/js/core/base.js | 2 +- openslides/core/static/js/core/site.js | 2 +- openslides/motions/models.py | 5 +- openslides/motions/serializers.py | 27 ++++--- .../motions/static/js/motions/motions.js | 78 ++++++++++++++----- .../templates/motions/motion-detail.html | 6 +- tests/integration/motions/test_viewset.py | 19 +++-- 8 files changed, 97 insertions(+), 44 deletions(-) diff --git a/openslides/assignments/static/js/assignments/site.js b/openslides/assignments/static/js/assignments/site.js index a7e35c065..8759b9cf7 100644 --- a/openslides/assignments/static/js/assignments/site.js +++ b/openslides/assignments/static/js/assignments/site.js @@ -79,7 +79,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments']) .controller('AssignmentDetailCtrl', function($scope, Assignment, assignment) { Assignment.bindOne(assignment.id, $scope, 'assignment'); - Assignment.loadRelations(assignment); + Assignment.loadRelations(assignment, 'agenda_item'); }) .controller('AssignmentCreateCtrl', function($scope, $state, Assignment) { diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js index 84d6771f4..5c1053d36 100644 --- a/openslides/core/static/js/core/base.js +++ b/openslides/core/static/js/core/base.js @@ -111,7 +111,7 @@ angular.module('OpenSlidesApp.core', [ // Load the global data on startup .run([ 'loadGlobalData', - function(loadGlobalData, operator) { + function(loadGlobalData) { loadGlobalData(); } ]) diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js index 4aba97a13..014351ca8 100644 --- a/openslides/core/static/js/core/site.js +++ b/openslides/core/static/js/core/site.js @@ -646,7 +646,7 @@ angular.module('OpenSlidesApp.core.site', [ .controller('CustomslideDetailCtrl', function($scope, Customslide, customslide) { Customslide.bindOne(customslide.id, $scope, 'customslide'); - Customslide.loadRelations(customslide); + Customslide.loadRelations(customslide, 'agenda_item'); }) .controller('CustomslideCreateCtrl', function($scope, $state, Customslide) { diff --git a/openslides/motions/models.py b/openslides/motions/models.py index a410c28cd..9067f43b8 100644 --- a/openslides/motions/models.py +++ b/openslides/motions/models.py @@ -409,6 +409,7 @@ class Motion(RESTModelMixin, models.Model): """ Returns the id of the workflow of the motion. """ + # TODO: Rename to workflow_id return self.state.workflow.pk def set_state(self, state): @@ -442,7 +443,7 @@ class Motion(RESTModelMixin, models.Model): new_state = self.state.workflow.first_state else: new_state = (Workflow.objects.get(pk=config['motions_workflow']).first_state or - Workflow.objects.get(pk=config['motions_workflow']).state_set.all()[0]) + Workflow.objects.get(pk=config['motions_workflow']).states.all()[0]) self.set_state(new_state) def get_agenda_title(self): @@ -729,7 +730,7 @@ class State(RESTModelMixin, models.Model): action_word = models.CharField(max_length=255) """An alternative string to be used for a button to switch to this state.""" - workflow = models.ForeignKey('Workflow') + workflow = models.ForeignKey('Workflow', related_name='states') """A many-to-one relation to a workflow.""" next_states = models.ManyToManyField('self', symmetrical=False) diff --git a/openslides/motions/serializers.py b/openslides/motions/serializers.py index 67eddad0c..f9732fdac 100644 --- a/openslides/motions/serializers.py +++ b/openslides/motions/serializers.py @@ -1,7 +1,6 @@ from django.db import transaction from django.utils.translation import ugettext as _ -from openslides.core.config import config from openslides.utils.rest_api import ( CharField, DictField, @@ -58,19 +57,20 @@ class StateSerializer(ModelSerializer): 'versioning', 'leave_old_version_active', 'dont_set_identifier', - 'next_states',) + 'next_states', + 'workflow') class WorkflowSerializer(ModelSerializer): """ Serializer for motion.models.Workflow objects. """ - state_set = StateSerializer(many=True, read_only=True) + states = StateSerializer(many=True, read_only=True) first_state = PrimaryKeyRelatedField(read_only=True) class Meta: model = Workflow - fields = ('id', 'name', 'state_set', 'first_state',) + fields = ('id', 'name', 'states', 'first_state',) class MotionLogSerializer(ModelSerializer): @@ -180,11 +180,14 @@ class MotionSerializer(ModelSerializer): log_messages = MotionLogSerializer(many=True, read_only=True) polls = MotionPollSerializer(many=True, read_only=True) reason = CharField(allow_blank=True, required=False, write_only=True) - state = StateSerializer(read_only=True) text = CharField(write_only=True) title = CharField(max_length=255, write_only=True) versions = MotionVersionSerializer(many=True, read_only=True) - workflow = IntegerField(min_value=1, required=False, validators=[validate_workflow_field]) + workflow_id = IntegerField( + min_value=1, + required=False, + validators=[validate_workflow_field], + write_only=True) class Meta: model = Motion @@ -201,13 +204,13 @@ class MotionSerializer(ModelSerializer): 'submitters', 'supporters', 'state', - 'workflow', + 'workflow_id', 'tags', 'attachments', 'polls', 'agenda_item_id', 'log_messages',) - read_only_fields = ('parent',) # Some other fields are also read_only. See definitions above. + read_only_fields = ('parent', 'state') # Some other fields are also read_only. See definitions above. @transaction.atomic def create(self, validated_data): @@ -220,7 +223,7 @@ class MotionSerializer(ModelSerializer): motion.reason = validated_data.get('reason', '') motion.identifier = validated_data.get('identifier') motion.category = validated_data.get('category') - motion.reset_state(validated_data.get('workflow', int(config['motions_workflow']))) + motion.reset_state(validated_data.get('workflow_id')) motion.save() if validated_data.get('submitters'): motion.submitters.add(*validated_data['submitters']) @@ -242,9 +245,9 @@ class MotionSerializer(ModelSerializer): setattr(motion, key, validated_data[key]) # Workflow. - workflow = validated_data.get('workflow') - if workflow is not None and workflow != motion.workflow: - motion.reset_state(workflow) + workflow_id = validated_data.get('workflow_id') + if workflow_id is not None and workflow_id != motion.workflow: + motion.reset_state(workflow_id) # Decide if a new version is saved to the database. if (motion.state.versioning and diff --git a/openslides/motions/static/js/motions/motions.js b/openslides/motions/static/js/motions/motions.js index e4ba67e26..41d83f393 100644 --- a/openslides/motions/static/js/motions/motions.js +++ b/openslides/motions/static/js/motions/motions.js @@ -2,14 +2,58 @@ angular.module('OpenSlidesApp.motions', []) +.factory('WorkflowState', [ + 'DS', + function (DS) { + return DS.defineResource({ + name: 'motions/workflowstate', + methods: { + getNextStates: function () { + var states = []; + _.forEach(this.next_states_id, function (stateId) { + states.push(DS.get('motions/workflowstate', stateId)); + }) + return states; + } + } + }) + } +]) + +.factory('Workflow', [ + 'DS', + 'jsDataModel', + 'WorkflowState', + function (DS, jsDataModel, WorkflowState) { + return DS.defineResource({ + name: 'motions/workflow', + useClass: jsDataModel, + relations: { + hasMany: { + 'motions/workflowstate': { + localField: 'states', + foreignKey: 'workflow_id', + } + } + } + }) + } +]) + +// Load all MotionWorkflows at stateup +.run([ + 'Workflow', + function (Workflow) { + Workflow.findAll(); + } +]) + .factory('MotionPoll', [ 'DS', 'Config', - 'jsDataModel', - function(DS, Config, jsDataModel) { + function (DS, Config) { return DS.defineResource({ - name: 'motions/motionpoll', - useClass: jsDataModel, + name: 'motions/poll', relations: { belongsTo: { 'motions/motion': { @@ -149,10 +193,16 @@ angular.module('OpenSlidesApp.motions', []) localKeys: 'supporters_id', } ], - 'motions/motionpoll': { + 'motions/poll': { localField: 'polls', foreignKey: 'motion_id', } + }, + hasOne: { + 'motions/workflowstate': { + localField: 'state', + localKey: 'state_id', + } } } }); @@ -165,13 +215,7 @@ angular.module('OpenSlidesApp.motions', []) }); }]) -.factory('Workflow', ['DS', function(DS) { - return DS.defineResource({ - name: 'motions/workflow', - }); -}]) - -.run(['Motion', 'Category', 'Workflow', function(Motion, Category, Workflow) {}]); +.run(['Motion', 'Category', function(Motion, Category) {}]); angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) @@ -221,9 +265,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) categories: function(Category) { return Category.findAll(); }, - workflows: function(Workflow) { - return Workflow.findAll(); - }, tags: function(Tag) { return Tag.findAll(); }, @@ -262,9 +303,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) categories: function(Category) { return Category.findAll(); }, - workflows: function(Workflow) { - return Workflow.findAll(); - }, tags: function(Tag) { return Tag.findAll(); }, @@ -405,7 +443,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions']) Category.bindAll({}, $scope, 'categories'); Workflow.bindAll({}, $scope, 'workflows'); User.bindAll({}, $scope, 'users'); - Motion.loadRelations(motion); + Motion.loadRelations(motion, 'agenda_item'); + var state = motion.state; + state.getNextStates() $scope.alert = {}; // TODO: show alert in template $scope.update_state = function (state_id) { diff --git a/openslides/motions/static/templates/motions/motion-detail.html b/openslides/motions/static/templates/motions/motion-detail.html index 3efb17c2a..9cc830743 100644 --- a/openslides/motions/static/templates/motions/motion-detail.html +++ b/openslides/motions/static/templates/motions/motion-detail.html @@ -6,7 +6,11 @@ -{{ motion.agenda_item }} +agenda_id: {{ motion.agenda_item }} + +

state: {{ motion.state }} + +

next states: {{ motion.state.getNextStates() }}