Merge pull request #2291 from normanjaeckel/MotionAmendment
Reimplemented amendments for motions.
This commit is contained in:
commit
08c6fa11b4
@ -103,26 +103,32 @@ def get_config_variables():
|
|||||||
subgroup='General')
|
subgroup='General')
|
||||||
|
|
||||||
# Amendments
|
# Amendments
|
||||||
# Amendments currently not implemented. (TODO: Implement it like in OpenSlides 1.7.)
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_amendments_enabled',
|
name='motions_amendments_enabled',
|
||||||
default_value=False,
|
default_value=False,
|
||||||
input_type='boolean',
|
input_type='boolean',
|
||||||
label='Activate amendments',
|
label='Activate amendments',
|
||||||
hidden=True,
|
|
||||||
weight=335,
|
weight=335,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Amendments')
|
subgroup='Amendments')
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
name='motions_amendments_prefix',
|
name='motions_amendments_prefix',
|
||||||
default_value='A',
|
default_value='-',
|
||||||
label='Prefix for the identifier for amendments',
|
label='Prefix for the identifier for amendments',
|
||||||
hidden=True,
|
|
||||||
weight=340,
|
weight=340,
|
||||||
group='Motions',
|
group='Motions',
|
||||||
subgroup='Amendments')
|
subgroup='Amendments')
|
||||||
|
|
||||||
|
yield ConfigVariable(
|
||||||
|
name='motions_amendments_apply_title_text',
|
||||||
|
default_value=False,
|
||||||
|
input_type='boolean',
|
||||||
|
label='Apply title and text for new amendments',
|
||||||
|
weight=342,
|
||||||
|
group='Motions',
|
||||||
|
subgroup='Amendments')
|
||||||
|
|
||||||
# Supporters
|
# Supporters
|
||||||
|
|
||||||
yield ConfigVariable(
|
yield ConfigVariable(
|
||||||
|
@ -243,7 +243,7 @@ class MotionSerializer(ModelSerializer):
|
|||||||
'polls',
|
'polls',
|
||||||
'agenda_item_id',
|
'agenda_item_id',
|
||||||
'log_messages',)
|
'log_messages',)
|
||||||
read_only_fields = ('parent', 'state') # Some other fields are also read_only. See definitions above.
|
read_only_fields = ('state',) # Some other fields are also read_only. See definitions above.
|
||||||
|
|
||||||
@transaction.atomic
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -257,6 +257,7 @@ class MotionSerializer(ModelSerializer):
|
|||||||
motion.identifier = validated_data.get('identifier')
|
motion.identifier = validated_data.get('identifier')
|
||||||
motion.category = validated_data.get('category')
|
motion.category = validated_data.get('category')
|
||||||
motion.origin = validated_data.get('origin', '')
|
motion.origin = validated_data.get('origin', '')
|
||||||
|
motion.parent = validated_data.get('parent')
|
||||||
motion.reset_state(validated_data.get('workflow_id'))
|
motion.reset_state(validated_data.get('workflow_id'))
|
||||||
motion.save()
|
motion.save()
|
||||||
if validated_data.get('submitters'):
|
if validated_data.get('submitters'):
|
||||||
|
@ -272,6 +272,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
motion: function(Motion, $stateParams) {
|
motion: function(Motion, $stateParams) {
|
||||||
return Motion.find($stateParams.id);
|
return Motion.find($stateParams.id);
|
||||||
},
|
},
|
||||||
|
motions: function(Motion) {
|
||||||
|
return Motion.findAll();
|
||||||
|
},
|
||||||
categories: function(Category) {
|
categories: function(Category) {
|
||||||
return Category.findAll();
|
return Category.findAll();
|
||||||
},
|
},
|
||||||
@ -819,7 +822,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'diffService',
|
'diffService',
|
||||||
function($scope, $http, $timeout, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, Editor,
|
function($scope, $http, $timeout, ngDialog, MotionForm, Motion, Category, Mediafile, Tag, User, Workflow, Editor,
|
||||||
Config,motion, SingleMotionContentProvider, MotionContentProvider, PdfMakeConverter,
|
Config, motion, SingleMotionContentProvider, MotionContentProvider, PdfMakeConverter,
|
||||||
PdfMakeDocumentProvider, gettextCatalog, diffService) {
|
PdfMakeDocumentProvider, gettextCatalog, diffService) {
|
||||||
Motion.bindOne(motion.id, $scope, 'motion');
|
Motion.bindOne(motion.id, $scope, 'motion');
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
@ -832,6 +835,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
$scope.isCollapsed = true;
|
$scope.isCollapsed = true;
|
||||||
$scope.lineNumberMode = Config.get('motions_default_line_numbering').value;
|
$scope.lineNumberMode = Config.get('motions_default_line_numbering').value;
|
||||||
$scope.lineBrokenText = motion.getTextWithLineBreaks($scope.version);
|
$scope.lineBrokenText = motion.getTextWithLineBreaks($scope.version);
|
||||||
|
if (motion.parent_id) {
|
||||||
|
Motion.bindOne(motion.parent_id, $scope, 'parent');
|
||||||
|
}
|
||||||
|
$scope.amendments = Motion.filter({parent_id: motion.id});
|
||||||
|
|
||||||
$scope.makePDF = function(){
|
$scope.makePDF = function(){
|
||||||
var content = motion.getText($scope.version) + motion.getReason($scope.version),
|
var content = motion.getText($scope.version) + motion.getReason($scope.version),
|
||||||
@ -875,6 +882,15 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
$scope.unsupport = function () {
|
$scope.unsupport = function () {
|
||||||
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
$http.delete('/rest/motions/motion/' + motion.id + '/support/');
|
||||||
};
|
};
|
||||||
|
// open dialog for new amendment
|
||||||
|
$scope.newAmendment = function () {
|
||||||
|
var dialog = MotionForm.getDialog();
|
||||||
|
if (dialog.scope === undefined) {
|
||||||
|
dialog.scope = {};
|
||||||
|
}
|
||||||
|
dialog.scope = $scope;
|
||||||
|
ngDialog.open(dialog);
|
||||||
|
};
|
||||||
// update state
|
// update state
|
||||||
$scope.updateState = function (state_id) {
|
$scope.updateState = function (state_id) {
|
||||||
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
$http.put('/rest/motions/motion/' + motion.id + '/set_state/', {'state': state_id});
|
||||||
@ -1038,6 +1054,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
|
|
||||||
.controller('MotionCreateCtrl', [
|
.controller('MotionCreateCtrl', [
|
||||||
'$scope',
|
'$scope',
|
||||||
|
'$state',
|
||||||
'gettext',
|
'gettext',
|
||||||
'Motion',
|
'Motion',
|
||||||
'MotionForm',
|
'MotionForm',
|
||||||
@ -1049,7 +1066,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
'Workflow',
|
'Workflow',
|
||||||
'Agenda',
|
'Agenda',
|
||||||
'AgendaUpdate',
|
'AgendaUpdate',
|
||||||
function($scope, gettext, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaUpdate) {
|
function($scope, $state, gettext, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaUpdate) {
|
||||||
Category.bindAll({}, $scope, 'categories');
|
Category.bindAll({}, $scope, 'categories');
|
||||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||||
Tag.bindAll({}, $scope, 'tags');
|
Tag.bindAll({}, $scope, 'tags');
|
||||||
@ -1057,13 +1074,27 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
Workflow.bindAll({}, $scope, 'workflows');
|
Workflow.bindAll({}, $scope, 'workflows');
|
||||||
|
|
||||||
$scope.model = {};
|
$scope.model = {};
|
||||||
// set default values for create form
|
|
||||||
|
// Check whether this is a new amendment.
|
||||||
|
var isAmendment = $scope.$parent.motion && $scope.$parent.motion.id;
|
||||||
|
|
||||||
|
// Set default values for create form
|
||||||
// ... set preamble config value as text
|
// ... set preamble config value as text
|
||||||
$scope.model.text = Config.get('motions_preamble').value;
|
$scope.model.text = Config.get('motions_preamble').value;
|
||||||
|
// ... for amendments add parent_id
|
||||||
|
if (isAmendment) {
|
||||||
|
if (Config.get('motions_amendments_apply_title_text').value) {
|
||||||
|
$scope.model.title = $scope.$parent.motion.getTitle();
|
||||||
|
$scope.model.text = $scope.$parent.motion.getText();
|
||||||
|
}
|
||||||
|
$scope.model.parent_id = $scope.$parent.motion.id;
|
||||||
|
Motion.bindOne($scope.model.parent_id, $scope, 'parent');
|
||||||
|
}
|
||||||
// ... preselect default workflow
|
// ... preselect default workflow
|
||||||
$scope.model.workflow_id = Config.get('motions_workflow').value;
|
$scope.model.workflow_id = Config.get('motions_workflow').value;
|
||||||
// get all form fields
|
// get all form fields
|
||||||
$scope.formFields = MotionForm.getFormFields();
|
$scope.formFields = MotionForm.getFormFields();
|
||||||
|
|
||||||
// save motion
|
// save motion
|
||||||
$scope.save = function (motion) {
|
$scope.save = function (motion) {
|
||||||
Motion.create(motion).then(
|
Motion.create(motion).then(
|
||||||
@ -1073,6 +1104,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
|
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
|
||||||
{key: 'parent_id', value: motion.agenda_parent_item_id}];
|
{key: 'parent_id', value: motion.agenda_parent_item_id}];
|
||||||
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
|
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
|
||||||
|
if (isAmendment) {
|
||||||
|
$state.go('motions.motion.detail', {id: success.id});
|
||||||
|
}
|
||||||
$scope.closeThisDialog();
|
$scope.closeThisDialog();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -1346,7 +1380,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
var importedUsers = [];
|
var importedUsers = [];
|
||||||
var importedCategories = [];
|
var importedCategories = [];
|
||||||
// collect users and categories
|
// collect users and categories
|
||||||
angular.forEach($scope.motions, function (motion){
|
angular.forEach($scope.motions, function (motion) {
|
||||||
if (!motion.importerror) {
|
if (!motion.importerror) {
|
||||||
// collect user if not exists
|
// collect user if not exists
|
||||||
if (!motion.submitters_id && motion.submitter) {
|
if (!motion.submitters_id && motion.submitter) {
|
||||||
@ -1630,8 +1664,7 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
|||||||
gettext('Amendments');
|
gettext('Amendments');
|
||||||
gettext('Activate amendments');
|
gettext('Activate amendments');
|
||||||
gettext('Prefix for the identifier for amendments');
|
gettext('Prefix for the identifier for amendments');
|
||||||
/// Prefix for the identifier for amendments
|
gettext('Apply title and text for new amendments');
|
||||||
gettext('A');
|
|
||||||
|
|
||||||
// subgroup Suppoerters
|
// subgroup Suppoerters
|
||||||
gettext('Supporters');
|
gettext('Supporters');
|
||||||
|
@ -31,6 +31,10 @@
|
|||||||
<h1>{{ motion.agenda_item.getTitle() || motion.getTitle() }}</h1>
|
<h1>{{ motion.agenda_item.getTitle() || motion.getTitle() }}</h1>
|
||||||
<h2>
|
<h2>
|
||||||
<translate>Motion</translate> {{ motion.identifier }}
|
<translate>Motion</translate> {{ motion.identifier }}
|
||||||
|
<span ng-if="parent">
|
||||||
|
(<translate>Amendment of motion</translate>
|
||||||
|
<a ui-sref="motions.motion.detail({id: parent.id})">{{ parent.identifier || parent.getTitle() }}</a>)
|
||||||
|
</span>
|
||||||
<span ng-if="motion.versions.length > 1" >| Version {{ motion.getVersion(version).version_number }}</span>
|
<span ng-if="motion.versions.length > 1" >| Version {{ motion.getVersion(version).version_number }}</span>
|
||||||
<span ng-if="motion.active_version != version" class="label label-warning">
|
<span ng-if="motion.active_version != version" class="label label-warning">
|
||||||
<i class="fa fa-exclamation-triangle"></i>
|
<i class="fa fa-exclamation-triangle"></i>
|
||||||
@ -56,7 +60,7 @@
|
|||||||
<!-- submitters -->
|
<!-- submitters -->
|
||||||
<h3 translate>Submitters</h3>
|
<h3 translate>Submitters</h3>
|
||||||
<div ng-repeat="submitter in motion.submitters">
|
<div ng-repeat="submitter in motion.submitters">
|
||||||
{{ submitter.get_full_name() }}<br>
|
{{ submitter.get_full_name() }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- supporters -->
|
<!-- supporters -->
|
||||||
@ -77,6 +81,19 @@
|
|||||||
<translate>Unsupport motion</translate>
|
<translate>Unsupport motion</translate>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Amendments -->
|
||||||
|
<div ng-if="config('motions_amendments_enabled')">
|
||||||
|
<h3 translate>Amendments</h3>
|
||||||
|
<div ng-repeat="amendment in amendments">
|
||||||
|
<a ui-sref="motions.motion.detail({id: amendment.id})">
|
||||||
|
<translate>Motion</translate> {{ amendment.identifier || amendment.getTitle() }}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<button os-perms="motions.can_create" ng-click="newAmendment()" class="btn btn-default btn-sm">
|
||||||
|
<i class="fa fa-plus"></i>
|
||||||
|
<translate>New amendment</translate>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-4">
|
<div class="col-md-4">
|
||||||
<!-- Category -->
|
<!-- Category -->
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
<h1 ng-if="model.id" translate>Edit motion</h1>
|
<h1 ng-if="model.id" translate>Edit motion</h1>
|
||||||
<h1 ng-if="!model.id" translate>New motion</h1>
|
<h1 ng-if="!model.id && !parent" translate>New motion</h1>
|
||||||
|
<h1 ng-if="parent"><translate>New amendment of motion</translate> {{ parent.identifier || parent.getTitle() }}</h1>
|
||||||
|
|
||||||
<uib-alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
<uib-alert ng-show="alert.show" type="{{ alert.type }}" ng-click="alert={}" close="alert={}">
|
||||||
{{ alert.msg }}
|
{{ alert.msg }}
|
||||||
|
@ -171,7 +171,7 @@ class ModelTest(TestCase):
|
|||||||
|
|
||||||
motion.set_identifier()
|
motion.set_identifier()
|
||||||
|
|
||||||
self.assertEqual(motion.identifier, 'Parent identifier A 1')
|
self.assertEqual(motion.identifier, 'Parent identifier - 1')
|
||||||
|
|
||||||
def test_set_identifier_second_amendment(self):
|
def test_set_identifier_second_amendment(self):
|
||||||
"""
|
"""
|
||||||
@ -186,7 +186,7 @@ class ModelTest(TestCase):
|
|||||||
|
|
||||||
motion.set_identifier()
|
motion.set_identifier()
|
||||||
|
|
||||||
self.assertEqual(motion.identifier, 'Parent identifier A 2')
|
self.assertEqual(motion.identifier, 'Parent identifier - 2')
|
||||||
|
|
||||||
|
|
||||||
class ConfigTest(TestCase):
|
class ConfigTest(TestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user