Added new motion state flags to show an additional fields

... to extend state name and recommendation label.
Hide forState and forRecommendation fields in motion form and detail
view (comment section).
This commit is contained in:
Emanuel Schütze 2016-10-27 14:01:12 +02:00
parent b0ff8375a8
commit 78765ae9ed
11 changed files with 155 additions and 20 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.10.2 on 2016-10-27 12:06
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('motions', '0006_auto_20161017_2020'),
]
operations = [
migrations.AddField(
model_name='state',
name='show_recommendation_extension_field',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='state',
name='show_state_extension_field',
field=models.BooleanField(default=False),
),
]

View File

@ -1085,6 +1085,20 @@ class State(RESTModelMixin, models.Model):
this one, else it does. this one, else it does.
""" """
show_state_extension_field = models.BooleanField(default=False)
"""
If true, an additional input field (from motion comment) is visible
to extend the state name. The full state name is composed of the given
state name and the entered value of this input field.
"""
show_recommendation_extension_field = models.BooleanField(default=False)
"""
If true, an additional input field (from motion comment) is visible
to extend the recommendation label. The full recommendation string is
composed of the given recommendation label and the entered value of this input field.
"""
class Meta: class Meta:
default_permissions = () default_permissions = ()

View File

@ -71,6 +71,8 @@ class StateSerializer(ModelSerializer):
'versioning', 'versioning',
'leave_old_version_active', 'leave_old_version_active',
'dont_set_identifier', 'dont_set_identifier',
'show_state_extension_field',
'show_recommendation_extension_field',
'next_states', 'next_states',
'workflow') 'workflow')

View File

@ -147,11 +147,12 @@ angular.module('OpenSlidesApp.motions', [
'MotionComment', 'MotionComment',
'jsDataModel', 'jsDataModel',
'gettext', 'gettext',
'gettextCatalog',
'operator', 'operator',
'Config', 'Config',
'lineNumberingService', 'lineNumberingService',
'diffService', 'diffService',
function(DS, MotionPoll, MotionChangeRecommendation, MotionComment, jsDataModel, gettext, operator, Config, function(DS, MotionPoll, MotionChangeRecommendation, MotionComment, jsDataModel, gettext, gettextCatalog, operator, Config,
lineNumberingService, diffService) { lineNumberingService, diffService) {
var name = 'motions/motion'; var name = 'motions/motion';
return DS.defineResource({ return DS.defineResource({
@ -288,6 +289,38 @@ angular.module('OpenSlidesApp.motions', [
getReason: function (versionId) { getReason: function (versionId) {
return this.getVersion(versionId).reason; return this.getVersion(versionId).reason;
}, },
// full state name - optional with custom state name extension
// depended by state and provided by a custom comment field
getStateName: function () {
var additionalName = '';
if (this.state.show_state_extension_field) {
// check motion comment fields for flag 'forState'
var fields = Config.get('motions_comments').value;
var index = _.findIndex(fields, ['forState', true]);
if (index > -1) {
additionalName = ' ' + this.comments[index];
}
}
return gettextCatalog.getString(this.state.name) + additionalName;
},
// full recommendation string - optional with custom recommendationextension
// depended by state and provided by a custom comment field
getRecommendationName: function () {
var recommendation = '';
var additionalName = '';
if (this.recommendation) {
recommendation = gettextCatalog.getString(this.recommendation.recommendation_label);
if (this.recommendation.show_recommendation_extension_field) {
// check motion comment fields for flag 'forRecommendation'
var fields = Config.get('motions_comments').value;
var index = _.findIndex(fields, ['forRecommendation', true]);
if (index > -1) {
additionalName = ' ' + this.comments[index];
}
}
}
return recommendation + additionalName;
},
// link name which is shown in search result // link name which is shown in search result
getSearchResultName: function () { getSearchResultName: function () {
return this.getTitle(); return this.getTitle();
@ -450,7 +483,7 @@ angular.module('OpenSlidesApp.motions', [
getFormFields: function () { getFormFields: function () {
var fields = Config.get('motions_comments').value; var fields = Config.get('motions_comments').value;
return _.map( return _.map(
fields, _.filter(fields, function(element) { return !element.forState && !element.forRecommendation; }),
function (field) { function (field) {
return { return {
key: 'comment ' + field.name, key: 'comment ' + field.name,
@ -484,7 +517,16 @@ angular.module('OpenSlidesApp.motions', [
for (var i = 0; i < fields.length; i++) { for (var i = 0; i < fields.length; i++) {
motion.comments.push(motion['comment ' + fields[i].name] || ''); motion.comments.push(motion['comment ' + fields[i].name] || '');
} }
} },
getFieldNameForFlag: function (flag) {
var fields = Config.get('motions_comments').value;
var fieldName = '';
var index = _.findIndex(fields, [flag, true]);
if (index > -1) {
fieldName = fields[index].name;
}
return fieldName;
},
}; };
} }
]) ])

View File

@ -82,7 +82,7 @@ angular.module('OpenSlidesApp.motions.DOCX', [])
}).join(', '), }).join(', '),
signature_translation: signature_translation, signature_translation: signature_translation,
status_translation: status_translation, status_translation: status_translation,
status: gettextCatalog.getString(motion.state.name), status: motion.getStateName(),
text: html2docx(motion.getText()), text: html2docx(motion.getText()),
reason_translation: motion.getReason().length === 0 ? '' : reason_translation, reason_translation: motion.getReason().length === 0 ? '' : reason_translation,
reason: html2docx(motion.getReason()), reason: html2docx(motion.getReason()),

View File

@ -42,7 +42,7 @@ angular.module('OpenSlidesApp.motions.pdf', ['OpenSlidesApp.core.pdf'])
// Generate text of signment // Generate text of signment
var signment = function() { var signment = function() {
var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:'); var label = converter.createElement("text", gettextCatalog.getString('Submitter') + ':\nStatus:');
var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n'+gettextCatalog.getString(motion.state.name)); var state = converter.createElement("text", User.get(motion.submitters_id[0]).full_name + '\n' + motion.getStateName());
state.width = "70%"; state.width = "70%";
label.width = "30%"; label.width = "30%";
label.bold = true; label.bold = true;

View File

@ -1013,6 +1013,7 @@ angular.module('OpenSlidesApp.motions.site', [
'MotionChangeRecommendation', 'MotionChangeRecommendation',
'MotionPDFExport', 'MotionPDFExport',
'Motion', 'Motion',
'MotionComment',
'Category', 'Category',
'Mediafile', 'Mediafile',
'Tag', 'Tag',
@ -1026,9 +1027,8 @@ angular.module('OpenSlidesApp.motions.site', [
'ProjectionDefault', 'ProjectionDefault',
function($scope, $http, operator, ngDialog, MotionForm, function($scope, $http, operator, ngDialog, MotionForm,
ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, MotionPDFExport, ChangeRecommmendationCreate, ChangeRecommmendationView, MotionChangeRecommendation, MotionPDFExport,
Motion, Category, Mediafile, Tag, User, Workflow, Config, motion, MotionInlineEditing, gettextCatalog, Motion, MotionComment, Category, Mediafile, Tag, User, Workflow, Config, motion, MotionInlineEditing, gettextCatalog,
Projector, ProjectionDefault) { Projector, ProjectionDefault) {
Motion.bindOne(motion.id, $scope, 'motion');
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles'); Mediafile.bindAll({}, $scope, 'mediafiles');
Tag.bindAll({}, $scope, 'tags'); Tag.bindAll({}, $scope, 'tags');
@ -1041,10 +1041,17 @@ angular.module('OpenSlidesApp.motions.site', [
}, function () { }, function () {
$scope.defaultProjectorId = ProjectionDefault.filter({name: 'motions'})[0].projector_id; $scope.defaultProjectorId = ProjectionDefault.filter({name: 'motions'})[0].projector_id;
}); });
$scope.$watch(function () {
return Motion.lastModified(motion.id);
}, function () {
$scope.motion = Motion.get(motion.id);
MotionComment.populateFields($scope.motion);
});
$scope.commentsFields = Config.get('motions_comments').value;
$scope.commentFieldForState = MotionComment.getFieldNameForFlag('forState');
$scope.commentFieldForRecommendation = MotionComment.getFieldNameForFlag('forRecommendation');
$scope.version = motion.active_version; $scope.version = motion.active_version;
$scope.isCollapsed = true; $scope.isCollapsed = true;
$scope.commentsFields = Config.get('motions_comments').value;
$scope.lineNumberMode = Config.get('motions_default_line_numbering').value; $scope.lineNumberMode = Config.get('motions_default_line_numbering').value;
$scope.setLineNumberMode = function(mode) { $scope.setLineNumberMode = function(mode) {
$scope.lineNumberMode = mode; $scope.lineNumberMode = mode;
@ -1119,6 +1126,26 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.reset_state = function () { $scope.reset_state = function () {
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {}); $http.put('/rest/motions/motion/' + motion.id + '/set_state/', {});
}; };
// save additional state field
$scope.saveAdditionalStateField = function (stateExtension) {
if (stateExtension) {
motion["comment " + $scope.commentFieldForState] = stateExtension;
motion.title = motion.getTitle(-1);
motion.text = motion.getText(-1);
motion.reason = motion.getReason(-1);
Motion.save(motion);
}
};
// save additional recommendation field
$scope.saveAdditionalRecommendationField = function (recommendationExtension) {
if (recommendationExtension) {
motion["comment " + $scope.commentFieldForRecommendation] = recommendationExtension;
motion.title = motion.getTitle(-1);
motion.text = motion.getText(-1);
motion.reason = motion.getReason(-1);
Motion.save(motion);
}
};
// update recommendation // update recommendation
$scope.updateRecommendation = function (recommendation_id) { $scope.updateRecommendation = function (recommendation_id) {
$http.put('/rest/motions/motion/' + motion.id + '/set_recommendation/', {'recommendation': recommendation_id}); $http.put('/rest/motions/motion/' + motion.id + '/set_recommendation/', {'recommendation': recommendation_id});
@ -1183,7 +1210,7 @@ angular.module('OpenSlidesApp.motions.site', [
isAllowed = operator.hasPerms('motions.can_see_and_manage_comments') || _.find( isAllowed = operator.hasPerms('motions.can_see_and_manage_comments') || _.find(
$scope.commentsFields, $scope.commentsFields,
function(field) { function(field) {
return field.public; return field.public && !field.forState && !field.forRecommendation;
} }
); );
} }

View File

@ -65,11 +65,11 @@
</div> </div>
<td> <td>
<div class="label" ng-class="'label-'+motion.state.css_class"> <div class="label" ng-class="'label-'+motion.state.css_class">
{{ motion.state.name | translate }} {{ motion.getStateName() }}
</div> </div>
<td> <td>
<div class="label" ng-class="'label-'+motion.recommendation.css_class"> <div class="label" ng-class="'label-'+motion.recommendation.css_class">
{{ motion.recommendation.recommendation_label | translate }} {{ motion.getRecommendationName() }}
</div> </div>
</table> </table>
</div> </div>

View File

@ -115,7 +115,19 @@
</span> </span>
</div> </div>
<div class="label" ng-class="'label-'+motion.state.css_class"> <div class="label" ng-class="'label-'+motion.state.css_class">
{{ motion.state.name | translate }} {{ motion.getStateName() }}
</div>
<div os-perms="motions.can_manage" class="input-group spacer"
ng-show="motion.state.show_state_extension_field">
<label class="sr-only" for="stateExtensionField">{{ commentFieldForState }}</label>
<input type="text" ng-model="stateExtension"
id="stateNameExtensionField" class="form-control input-sm"
placeholder="{{ commentFieldForState }}">
<span class="input-group-btn">
<button ng-click="saveAdditionalStateField(stateExtension)" class="btn btn-default btn-sm">
<i class="fa fa-check"></i>
</button>
</span>
</div> </div>
<!-- Recommendation --> <!-- Recommendation -->
@ -142,7 +154,19 @@
</span> </span>
</div> </div>
<div class="label" ng-class="'label-'+motion.recommendation.css_class"> <div class="label" ng-class="'label-'+motion.recommendation.css_class">
{{ motion.recommendation.recommendation_label | translate }} {{ motion.getRecommendationName() }}
</div>
<div class="input-group spacer"
ng-show="motion.recommendation.show_recommendation_extension_field">
<label class="sr-only" for="recommendationExtensionField">{{ commentFieldForRecommendation }}</label>
<input type="text" ng-model="recommendationExtension"
id="recommendationExtensionField" class="form-control input-sm"
placeholder="{{ commentFieldForRecommendation }}">
<span class="input-group-btn">
<button ng-click="saveAdditionalRecommendationField(recommendationExtension)" class="btn btn-default btn-sm">
<i class="fa fa-check"></i>
</button>
</span>
</div> </div>
</div> </div>
@ -405,9 +429,10 @@
</div> </div>
<div class="details" ng-if="isAllowedToSeeCommentField()"> <div class="details" ng-if="isAllowedToSeeCommentField()">
<h3>Motion Comments</h3> <h3 translate>Comments</h3>
<div ng-repeat="field in commentsFields"> <div ng-repeat="field in commentsFields">
<div ng-if="field.public || operator.hasPerms('motions.can_see_and_manage_comments')"> <div ng-if="(field.public && !field.forState && !field.forRecommendation) ||
(operator.hasPerms('motions.can_see_and_manage_comments') && !field.forState && !field.forRecommendation)">
<h4> <h4>
{{ field.name }} {{ field.name }}
<span ng-if="!field.public" class="label label-warning" translate>internal</span> <span ng-if="!field.public" class="label label-warning" translate>internal</span>

View File

@ -389,7 +389,7 @@
<i class="fa fa-paperclip" ng-if="motion.attachments_id.length > 0"></i> <i class="fa fa-paperclip" ng-if="motion.attachments_id.length > 0"></i>
<span style="padding: 5px;" ng-mouseover="motion.stateHover=true" ng-mouseleave="motion.stateHover=false"> <span style="padding: 5px;" ng-mouseover="motion.stateHover=true" ng-mouseleave="motion.stateHover=false">
<span class="label" ng-class="'label-'+motion.state.css_class"> <span class="label" ng-class="'label-'+motion.state.css_class">
{{ motion.state.name | translate }} {{ motion.getStateName() }}
</span> </span>
<span os-perms="motions.can_manage" ng-class="{'hiddenDiv': !motion.stateHover}" uib-dropdown> <span os-perms="motions.can_manage" ng-class="{'hiddenDiv': !motion.stateHover}" uib-dropdown>
<i class="fa fa-cog pointer" uib-dropdown-toggle id="state-dropdown{{ motion.id }}"></i> <i class="fa fa-cog pointer" uib-dropdown-toggle id="state-dropdown{{ motion.id }}"></i>

View File

@ -3,7 +3,7 @@
<div id="sidebox"> <div id="sidebox">
<!-- State --> <!-- State -->
<h3 translate>State</h3> <h3 translate>State</h3>
{{ motion.state.name | translate }} {{ motion.getStateName() }}
<!-- Submitters --> <!-- Submitters -->
<h3 ng-if="motion.submitters.length > 0" translate>Submitters</h3> <h3 ng-if="motion.submitters.length > 0" translate>Submitters</h3>