Enhance setup of new agenda items for topics, motions and motion blocks.

This commit is contained in:
Norman Jäckel 2018-01-20 09:54:46 +01:00 committed by Emanuel Schütze
parent f7de741e10
commit 4aad16db4f
12 changed files with 175 additions and 145 deletions

View File

@ -15,18 +15,17 @@ def listen_to_related_object_post_save(sender, instance, created, **kwargs):
django.db.models.signals.post_save during app loading. django.db.models.signals.post_save during app loading.
The agenda_item_update_information container may have fields like type, The agenda_item_update_information container may have fields like type,
parent or skip_autoupdate. parent_id, comment, duration, weight or skip_autoupdate.
Do not run caching and autoupdate if the instance as a key Do not run caching and autoupdate if the instance has a key
skip_autoupdate in the agenda_item_update_information container. skip_autoupdate in the agenda_item_update_information container.
""" """
if hasattr(instance, 'get_agenda_title'): if hasattr(instance, 'get_agenda_title'):
if created: if created:
attrs = {} attrs = {}
if instance.agenda_item_update_information.get('type'): for attr in ('type', 'parent_id', 'comment', 'duration', 'weight'):
attrs['type'] = instance.agenda_item_update_information.get('type') if instance.agenda_item_update_information.get(attr):
if instance.agenda_item_update_information.get('parent'): attrs[attr] = instance.agenda_item_update_information.get(attr)
attrs['parent'] = instance.agenda_item_update_information.get('parent')
Item.objects.create(content_object=instance, **attrs) Item.objects.create(content_object=instance, **attrs)
# If the object is created, the related_object has to be sent again. # If the object is created, the related_object has to be sent again.

View File

@ -333,7 +333,7 @@ class Assignment(RESTModelMixin, models.Model):
""" """
Container for runtime information for agenda app (on create or update of this instance). Container for runtime information for agenda app (on create or update of this instance).
""" """
agenda_item_update_information = {} agenda_item_update_information = {} # type: Dict[str, Any]
def get_agenda_title(self): def get_agenda_title(self):
return str(self) return str(self)

View File

@ -194,6 +194,8 @@ class AssignmentFullSerializer(ModelSerializer):
""" """
assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True) assignment_related_users = AssignmentRelatedUserSerializer(many=True, read_only=True)
polls = AssignmentAllPollSerializer(many=True, read_only=True) polls = AssignmentAllPollSerializer(many=True, read_only=True)
agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=2)
agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1)
class Meta: class Meta:
model = Assignment model = Assignment
@ -207,6 +209,8 @@ class AssignmentFullSerializer(ModelSerializer):
'poll_description_default', 'poll_description_default',
'polls', 'polls',
'agenda_item_id', 'agenda_item_id',
'agenda_type',
'agenda_parent_id',
'tags',) 'tags',)
validators = (posts_validator,) validators = (posts_validator,)
@ -215,6 +219,19 @@ class AssignmentFullSerializer(ModelSerializer):
data['description'] = validate_html(data['description']) data['description'] = validate_html(data['description'])
return data return data
def create(self, validated_data):
"""
Customized create method. Set information about related agenda item
into agenda_item_update_information container.
"""
agenda_type = validated_data.pop('agenda_type', None)
agenda_parent_id = validated_data.pop('agenda_parent_id', None)
assignment = Assignment(**validated_data)
assignment.agenda_item_update_information['type'] = agenda_type
assignment.agenda_item_update_information['parent_id'] = agenda_parent_id
assignment.save()
return assignment
class AssignmentShortSerializer(AssignmentFullSerializer): class AssignmentShortSerializer(AssignmentFullSerializer):
""" """

View File

@ -157,7 +157,7 @@ angular.module('OpenSlidesApp.assignments.site', [
} }
}]; }];
// parent item // show as agenda item + parent item
if (isCreateForm) { if (isCreateForm) {
formFields.push({ formFields.push({
key: 'showAsAgendaItem', key: 'showAsAgendaItem',
@ -627,9 +627,8 @@ angular.module('OpenSlidesApp.assignments.site', [
'Assignment', 'Assignment',
'AssignmentForm', 'AssignmentForm',
'Agenda', 'Agenda',
//'AgendaUpdate',
'ErrorMessage', 'ErrorMessage',
function($scope, $state, Assignment, AssignmentForm, Agenda,/* AgendaUpdate,*/ ErrorMessage) { function($scope, $state, Assignment, AssignmentForm, Agenda, ErrorMessage) {
$scope.model = {}; $scope.model = {};
// set default value for open posts form field // set default value for open posts form field
$scope.model.open_posts = 1; $scope.model.open_posts = 1;
@ -638,13 +637,9 @@ angular.module('OpenSlidesApp.assignments.site', [
// save assignment // save assignment
$scope.save = function(assignment, gotoDetailView) { $scope.save = function(assignment, gotoDetailView) {
assignment.agenda_type = assignment.showAsAgendaItem ? 1 : 2; assignment.agenda_type = assignment.showAsAgendaItem ? 1 : 2;
// The attribute assignment.agenda_parent_id is set by the form, see form definition.
Assignment.create(assignment).then( Assignment.create(assignment).then(
function (success) { function (success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
/*var changes = [{key: 'type', value: (assignment.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: assignment.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);*/
if (gotoDetailView) { if (gotoDetailView) {
$state.go('assignments.assignment.detail', {id: success.id}); $state.go('assignments.assignment.detail', {id: success.id});
} }
@ -674,20 +669,12 @@ angular.module('OpenSlidesApp.assignments.site', [
$scope.model = angular.copy(assignment); $scope.model = angular.copy(assignment);
// get all form fields // get all form fields
$scope.formFields = AssignmentForm.getFormFields(); $scope.formFields = AssignmentForm.getFormFields();
var agenda_item = Agenda.get(assignment.agenda_item_id);
for (var i = 0; i < $scope.formFields.length; i++) {
if ($scope.formFields[i].key == "showAsAgendaItem") {
// get state from agenda item (hidden/internal or agenda item)
$scope.formFields[i].defaultValue = !assignment.agenda_item.is_hidden;
}
}
// save assignment // save assignment
$scope.save = function (assignment, gotoDetailView) { $scope.save = function (assignment, gotoDetailView) {
assignment.agenda_type = assignment.showAsAgendaItem ? 1 : 2;
// inject the changed assignment (copy) object back into DS store // inject the changed assignment (copy) object back into DS store
Assignment.inject(assignment); Assignment.inject(assignment);
// save change assignment object on server // save changed assignment object on server
Assignment.save(assignment).then( Assignment.save(assignment).then(
function(success) { function(success) {
if (gotoDetailView) { if (gotoDetailView) {

View File

@ -1,3 +1,5 @@
from typing import Any, Dict # noqa
from django.conf import settings from django.conf import settings
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.exceptions import ImproperlyConfigured, ValidationError
@ -631,7 +633,7 @@ class Motion(RESTModelMixin, models.Model):
""" """
Container for runtime information for agenda app (on create or update of this instance). Container for runtime information for agenda app (on create or update of this instance).
""" """
agenda_item_update_information = {} agenda_item_update_information = {} # type: Dict[str, Any]
def get_agenda_title(self): def get_agenda_title(self):
""" """
@ -903,7 +905,7 @@ class MotionBlock(RESTModelMixin, models.Model):
""" """
Container for runtime information for agenda app (on create or update of this instance). Container for runtime information for agenda app (on create or update of this instance).
""" """
agenda_item_update_information = {} agenda_item_update_information = {} # type: Dict[str, Any]
@property @property
def agenda_item(self): def agenda_item(self):

View File

@ -49,9 +49,25 @@ class MotionBlockSerializer(ModelSerializer):
""" """
Serializer for motion.models.Category objects. Serializer for motion.models.Category objects.
""" """
agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=2)
agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1)
class Meta: class Meta:
model = MotionBlock model = MotionBlock
fields = ('id', 'title', 'agenda_item_id',) fields = ('id', 'title', 'agenda_item_id', 'agenda_type', 'agenda_parent_id',)
def create(self, validated_data):
"""
Customized create method. Set information about related agenda item
into agenda_item_update_information container.
"""
agenda_type = validated_data.pop('agenda_type', None)
agenda_parent_id = validated_data.pop('agenda_parent_id', None)
motion_block = MotionBlock(**validated_data)
motion_block.agenda_item_update_information['type'] = agenda_type
motion_block.agenda_item_update_information['parent_id'] = agenda_parent_id
motion_block.save()
return motion_block
class StateSerializer(ModelSerializer): class StateSerializer(ModelSerializer):
@ -288,6 +304,8 @@ class MotionSerializer(ModelSerializer):
required=False, required=False,
validators=[validate_workflow_field], validators=[validate_workflow_field],
write_only=True) write_only=True)
agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=2)
agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1)
class Meta: class Meta:
model = Motion model = Motion
@ -314,6 +332,8 @@ class MotionSerializer(ModelSerializer):
'attachments', 'attachments',
'polls', 'polls',
'agenda_item_id', 'agenda_item_id',
'agenda_type',
'agenda_parent_id',
'log_messages',) 'log_messages',)
read_only_fields = ('state', 'recommendation',) # Some other fields are also read_only. See definitions above. read_only_fields = ('state', 'recommendation',) # Some other fields are also read_only. See definitions above.
@ -332,6 +352,9 @@ class MotionSerializer(ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
""" """
Customized method to create a new motion from some data. Customized method to create a new motion from some data.
Set also information about related agenda item into
agenda_item_update_information container.
""" """
motion = Motion() motion = Motion()
motion.title = validated_data['title'] motion.title = validated_data['title']
@ -344,6 +367,8 @@ class MotionSerializer(ModelSerializer):
motion.comments = validated_data.get('comments') motion.comments = validated_data.get('comments')
motion.parent = validated_data.get('parent') motion.parent = validated_data.get('parent')
motion.reset_state(validated_data.get('workflow_id')) motion.reset_state(validated_data.get('workflow_id'))
motion.agenda_item_update_information['type'] = validated_data.get('agenda_type')
motion.agenda_item_update_information['parent_id'] = validated_data.get('agenda_parent_id')
motion.save() motion.save()
if validated_data.get('submitters'): if validated_data.get('submitters'):
motion.submitters.add(*validated_data['submitters']) motion.submitters.add(*validated_data['submitters'])

View File

@ -69,8 +69,8 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
}; };
}, },
// Get angular-formly fields. // Get angular-formly fields.
getFormFields: function () { getFormFields: function (isCreateForm) {
return [ var formFields = [
{ {
key: 'title', key: 'title',
type: 'input', type: 'input',
@ -78,7 +78,11 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
label: gettextCatalog.getString('Title') label: gettextCatalog.getString('Title')
} }
}, },
{ ];
// show as agenda item + parent item
if (isCreateForm) {
formFields.push({
key: 'showAsAgendaItem', key: 'showAsAgendaItem',
type: 'checkbox', type: 'checkbox',
templateOptions: { templateOptions: {
@ -86,18 +90,21 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.') description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.')
}, },
hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage')) hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage'))
}, });
{ formFields.push({
key: 'agenda_parent_item_id', key: 'agenda_parent_id',
type: 'select-single', type: 'select-single',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Parent item'), label: gettextCatalog.getString('Parent item'),
options: AgendaTree.getFlatTree(Agenda.getAll()), options: AgendaTree.getFlatTree(Agenda.getAll()),
ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id',
placeholder: gettextCatalog.getString('Select a parent item ...') placeholder: gettextCatalog.getString('Select a parent item ...')
},
hide: !operator.hasPerms('agenda.can_manage')
});
} }
}
]; return formFields;
} }
}; };
} }
@ -190,24 +197,20 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
'$scope', '$scope',
'MotionBlock', 'MotionBlock',
'MotionBlockForm', 'MotionBlockForm',
'AgendaUpdate', function($scope, MotionBlock, MotionBlockForm) {
function($scope, MotionBlock, MotionBlockForm, AgendaUpdate) {
// Prepare form. // Prepare form.
$scope.model = {}; $scope.model = {};
$scope.model.showAsAgendaItem = true; $scope.model.showAsAgendaItem = true;
// Get all form fields. // Get all form fields.
$scope.formFields = MotionBlockForm.getFormFields(); $scope.formFields = MotionBlockForm.getFormFields(true);
// Save form. // Save form.
$scope.save = function (motionBlock) { $scope.save = function (motionBlock) {
motionBlock.agenda_type = motionBlock.showAsAgendaItem ? 1 : 2;
// The attribute motionBlock.agenda_parent_id is set by the form, see form definition.
MotionBlock.create(motionBlock).then( MotionBlock.create(motionBlock).then(
function (success) { function (success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (motionBlock.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: motionBlock.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
$scope.closeThisDialog(); $scope.closeThisDialog();
}, },
function (error) { function (error) {
@ -230,8 +233,6 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
'AgendaUpdate', 'AgendaUpdate',
'motionBlockId', 'motionBlockId',
function($scope, $state, MotionBlock, MotionBlockForm, AgendaUpdate, motionBlockId) { function($scope, $state, MotionBlock, MotionBlockForm, AgendaUpdate, motionBlockId) {
// TODO: Check #2486 and remove some agenda related code.
//MotionBlock.loadRelations(motionBlock, 'agenda_item');
$scope.alert = {}; $scope.alert = {};
// Prepare form. Set initial values by creating a deep copy of // Prepare form. Set initial values by creating a deep copy of
@ -241,23 +242,14 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
// Get all form fields. // Get all form fields.
$scope.formFields = MotionBlockForm.getFormFields(); $scope.formFields = MotionBlockForm.getFormFields();
for (var i = 0; i < $scope.formFields.length; i++) {
if ($scope.formFields[i].key == 'showAsAgendaItem') {
// Get state from agenda item (hidden/internal or agenda item).
$scope.formFields[i].defaultValue = !motionBlock.agenda_item.is_hidden;
} else if ($scope.formFields[i].key == 'agenda_parent_item_id') {
$scope.formFields[i].defaultValue = motionBlock.agenda_item.parent_id;
}
}
// Save form. // Save form.
$scope.save = function (motionBlock) { $scope.save = function (motionBlock) {
// inject the changed motionBlock (copy) object back into DS store
MotionBlock.inject(motionBlock);
// save changed motionBlock object on server
MotionBlock.create(motionBlock).then( MotionBlock.create(motionBlock).then(
function (success) { function (success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (motionBlock.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: motionBlock.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
$scope.closeThisDialog(); $scope.closeThisDialog();
}, },
function (error) { function (error) {

View File

@ -376,8 +376,11 @@ angular.module('OpenSlidesApp.motions.site', [
description: gettextCatalog.getString("Don't create a new version.") description: gettextCatalog.getString("Don't create a new version.")
}, },
hide: true hide: true
}, }];
{
// show as agenda item + parent item
if (isCreateForm) {
formFields.push({
key: 'showAsAgendaItem', key: 'showAsAgendaItem',
type: 'checkbox', type: 'checkbox',
templateOptions: { templateOptions: {
@ -385,12 +388,9 @@ angular.module('OpenSlidesApp.motions.site', [
description: gettextCatalog.getString('If deactivated the motion appears as internal item on agenda.') description: gettextCatalog.getString('If deactivated the motion appears as internal item on agenda.')
}, },
hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage')) hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage'))
}]; });
// parent item
if (isCreateForm) {
formFields.push({ formFields.push({
key: 'agenda_parent_item_id', key: 'agenda_parent_id',
type: 'select-single', type: 'select-single',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Parent item'), label: gettextCatalog.getString('Parent item'),
@ -1890,10 +1890,9 @@ angular.module('OpenSlidesApp.motions.site', [
'User', 'User',
'Workflow', 'Workflow',
'Agenda', 'Agenda',
'AgendaUpdate',
'ErrorMessage', 'ErrorMessage',
function($scope, $state, gettext, gettextCatalog, operator, Motion, MotionForm, function($scope, $state, gettext, gettextCatalog, operator, Motion, MotionForm,
Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaUpdate, ErrorMessage) { Category, Config, Mediafile, Tag, User, Workflow, Agenda, ErrorMessage) {
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles'); Mediafile.bindAll({}, $scope, 'mediafiles');
Tag.bindAll({}, $scope, 'tags'); Tag.bindAll({}, $scope, 'tags');
@ -1927,13 +1926,10 @@ angular.module('OpenSlidesApp.motions.site', [
// save motion // save motion
$scope.save = function (motion, gotoDetailView) { $scope.save = function (motion, gotoDetailView) {
motion.agenda_type = motion.showAsAgendaItem ? 1 : 2;
// The attribute motion.agenda_parent_id is set by the form, see form definition.
Motion.create(motion).then( Motion.create(motion).then(
function(success) { function(success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: motion.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
if (isAmendment || gotoDetailView) { if (isAmendment || gotoDetailView) {
$state.go('motions.motion.detail', {id: success.id}); $state.go('motions.motion.detail', {id: success.id});
} }
@ -1959,13 +1955,12 @@ angular.module('OpenSlidesApp.motions.site', [
'User', 'User',
'Workflow', 'Workflow',
'Agenda', 'Agenda',
'AgendaUpdate',
'motionId', 'motionId',
'operator', 'operator',
'ErrorMessage', 'ErrorMessage',
'EditingWarning', 'EditingWarning',
function($scope, $state, Motion, Category, Config, Mediafile, MotionForm, function($scope, $state, Motion, Category, Config, Mediafile, MotionForm,
Tag, User, Workflow, Agenda, AgendaUpdate, motionId, operator, ErrorMessage, Tag, User, Workflow, Agenda, motionId, operator, ErrorMessage,
EditingWarning) { EditingWarning) {
Category.bindAll({}, $scope, 'categories'); Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles'); Mediafile.bindAll({}, $scope, 'mediafiles');
@ -2007,18 +2002,10 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.formFields[i].hide = false; $scope.formFields[i].hide = false;
} }
} }
if ($scope.formFields[i].key == "showAsAgendaItem" && motion.agenda_item) {
// get state from agenda item (hidden/internal or agenda item)
$scope.formFields[i].defaultValue = !motion.agenda_item.is_hidden;
}
if ($scope.formFields[i].key == "workflow_id") { if ($scope.formFields[i].key == "workflow_id") {
// get saved workflow id from state // get saved workflow id from state
$scope.formFields[i].defaultValue = motion.state.workflow_id; $scope.formFields[i].defaultValue = motion.state.workflow_id;
} }
if ($scope.formFields[i].key == "agenda_parent_item_id") {
// get current parent_id of the agenda item
$scope.formFields[i].defaultValue = motion.agenda_item.parent_id;
}
} }
// Displaying a warning, if other users edit this motion too // Displaying a warning, if other users edit this motion too
@ -2029,14 +2016,9 @@ angular.module('OpenSlidesApp.motions.site', [
$scope.save = function (motion, gotoDetailView) { $scope.save = function (motion, gotoDetailView) {
// inject the changed motion (copy) object back into DS store // inject the changed motion (copy) object back into DS store
Motion.inject(motion); Motion.inject(motion);
// save change motion object on server // save changed motion object on server
Motion.save(motion).then( Motion.save(motion).then(
function(success) { function(success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (motion.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: motion.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
if (gotoDetailView) { if (gotoDetailView) {
$state.go('motions.motion.detail', {id: success.id}); $state.go('motions.motion.detail', {id: success.id});
} }

View File

@ -596,7 +596,8 @@ class CategoryViewSet(ModelViewSet):
# Remove old identifiers # Remove old identifiers
for motion in motions: for motion in motions:
motion.identifier = None motion.identifier = None
motion.agenda_item_update_information['skip_autoupdate'] = True # This line is to skip agenda item autoupdate. See agenda/signals.py. # This line is to skip agenda item autoupdate. See agenda/signals.py.
motion.agenda_item_update_information['skip_autoupdate'] = True
motion.save(skip_autoupdate=True) motion.save(skip_autoupdate=True)
# Set new identifers and change identifiers of amendments. # Set new identifers and change identifiers of amendments.
@ -615,7 +616,8 @@ class CategoryViewSet(ModelViewSet):
obj['new_identifier'], obj['new_identifier'],
child.identifier, child.identifier,
count=1) count=1)
child.agenda_item_update_information['skip_autoupdate'] = True # This line is to skip agenda item autoupdate. See agenda/signals.py. # This line is to skip agenda item autoupdate. See agenda/signals.py.
child.agenda_item_update_information['skip_autoupdate'] = True
child.save(skip_autoupdate=True) child.save(skip_autoupdate=True)
instances.append(child) instances.append(child)
instances.append(child.agenda_item) instances.append(child.agenda_item)

View File

@ -1,3 +1,5 @@
from typing import Any, Dict # noqa
from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.fields import GenericRelation
from django.db import models from django.db import models
@ -57,7 +59,7 @@ class Topic(RESTModelMixin, models.Model):
""" """
Container for runtime information for agenda app (on create or update of this instance). Container for runtime information for agenda app (on create or update of this instance).
""" """
agenda_item_update_information = {} agenda_item_update_information = {} # type: Dict[str, Any]
@property @property
def agenda_item(self): def agenda_item(self):

View File

@ -1,4 +1,4 @@
from openslides.utils.rest_api import ModelSerializer from openslides.utils.rest_api import CharField, IntegerField, ModelSerializer
from openslides.utils.validate import validate_html from openslides.utils.validate import validate_html
from .models import Topic from .models import Topic
@ -8,11 +8,49 @@ class TopicSerializer(ModelSerializer):
""" """
Serializer for core.models.Topic objects. Serializer for core.models.Topic objects.
""" """
agenda_type = IntegerField(write_only=True, required=False, min_value=1, max_value=2)
agenda_parent_id = IntegerField(write_only=True, required=False, min_value=1)
agenda_comment = CharField(write_only=True, required=False, allow_blank=True)
agenda_duration = IntegerField(write_only=True, required=False, min_value=1)
agenda_weight = IntegerField(write_only=True, required=False, min_value=1)
class Meta: class Meta:
model = Topic model = Topic
fields = ('id', 'title', 'text', 'attachments', 'agenda_item_id') fields = (
'id',
'title',
'text',
'attachments',
'agenda_item_id',
'agenda_type',
'agenda_parent_id',
'agenda_comment',
'agenda_duration',
'agenda_weight',
)
def validate(self, data): def validate(self, data):
if 'text' in data: if 'text' in data:
data['text'] = validate_html(data['text']) data['text'] = validate_html(data['text'])
return data return data
def create(self, validated_data):
"""
Customized create method. Set information about related agenda item
into agenda_item_update_information container.
"""
agenda_type = validated_data.pop('agenda_type', None)
agenda_parent_id = validated_data.pop('agenda_parent_id', None)
agenda_comment = validated_data.pop('agenda_comment', None)
agenda_duration = validated_data.pop('agenda_duration', None)
agenda_weight = validated_data.pop('agenda_weight', None)
attachments = validated_data.pop('attachments', [])
topic = Topic(**validated_data)
topic.agenda_item_update_information['type'] = agenda_type
topic.agenda_item_update_information['parent_id'] = agenda_parent_id
topic.agenda_item_update_information['comment'] = agenda_comment
topic.agenda_item_update_information['duration'] = agenda_duration
topic.agenda_item_update_information['weight'] = agenda_weight
topic.save()
topic.attachments.add(*attachments)
return topic

View File

@ -116,7 +116,9 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
} }
}); });
} }
// show as agenda item
// show as agenda item + parent item
if (isCreateForm) {
formFields.push({ formFields.push({
key: 'showAsAgendaItem', key: 'showAsAgendaItem',
type: 'checkbox', type: 'checkbox',
@ -126,18 +128,16 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
}, },
hide: !operator.hasPerms('agenda.can_manage') hide: !operator.hasPerms('agenda.can_manage')
}); });
// parent item
if (isCreateForm) {
formFields.push({ formFields.push({
key: 'agenda_parent_item_id', key: 'agenda_parent_id',
type: 'select-single', type: 'select-single',
templateOptions: { templateOptions: {
label: gettextCatalog.getString('Parent item'), label: gettextCatalog.getString('Parent item'),
options: AgendaTree.getFlatTree(Agenda.getAll()), options: AgendaTree.getFlatTree(Agenda.getAll()),
ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id', ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id',
placeholder: gettextCatalog.getString('Select a parent item ...') placeholder: gettextCatalog.getString('Select a parent item ...')
} },
hide: !operator.hasPerms('agenda.can_manage')
}); });
} }
@ -186,9 +186,8 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
'Topic', 'Topic',
'TopicForm', 'TopicForm',
'Agenda', 'Agenda',
'AgendaUpdate',
'ErrorMessage', 'ErrorMessage',
function($scope, $state, Topic, TopicForm, Agenda, AgendaUpdate, ErrorMessage) { function($scope, $state, Topic, TopicForm, Agenda, ErrorMessage) {
$scope.topic = {}; $scope.topic = {};
$scope.model = {}; $scope.model = {};
$scope.model.showAsAgendaItem = true; $scope.model.showAsAgendaItem = true;
@ -196,13 +195,10 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
$scope.formFields = TopicForm.getFormFields(true); $scope.formFields = TopicForm.getFormFields(true);
// save form // save form
$scope.save = function (topic) { $scope.save = function (topic) {
topic.agenda_type = topic.showAsAgendaItem ? 1 : 2;
// The attribute topic.agenda_parent_id is set by the form, see form definition.
Topic.create(topic).then( Topic.create(topic).then(
function (success) { function (success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (topic.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: topic.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
$scope.closeThisDialog(); $scope.closeThisDialog();
}, function (error) { }, function (error) {
$scope.alert = ErrorMessage.forAlert(error); $scope.alert = ErrorMessage.forAlert(error);
@ -218,10 +214,9 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
'Topic', 'Topic',
'TopicForm', 'TopicForm',
'Agenda', 'Agenda',
'AgendaUpdate',
'topicId', 'topicId',
'ErrorMessage', 'ErrorMessage',
function($scope, $state, Topic, TopicForm, Agenda, AgendaUpdate, topicId, ErrorMessage) { function($scope, $state, Topic, TopicForm, Agenda, topicId, ErrorMessage) {
var topic = Topic.get(topicId); var topic = Topic.get(topicId);
$scope.alert = {}; $scope.alert = {};
// set initial values for form model by create deep copy of topic object // set initial values for form model by create deep copy of topic object
@ -229,25 +224,17 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
$scope.model = angular.copy(topic); $scope.model = angular.copy(topic);
// get all form fields // get all form fields
$scope.formFields = TopicForm.getFormFields(); $scope.formFields = TopicForm.getFormFields();
for (var i = 0; i < $scope.formFields.length; i++) {
if ($scope.formFields[i].key == "showAsAgendaItem") {
// get state from agenda item (hidden/internal or agenda item)
$scope.formFields[i].defaultValue = !topic.agenda_item.is_hidden;
} else if ($scope.formFields[i].key == "agenda_parent_item_id") {
$scope.formFields[i].defaultValue = topic.agenda_item.parent_id;
}
}
// save form // save form
$scope.save = function (topic) { $scope.save = function (topic) {
Topic.create(topic).then( // inject the changed topic (copy) object back into DS store
Topic.inject(topic);
// save changed topic object on server
Topic.save(topic).then(
function(success) { function(success) {
// type: Value 1 means a non hidden agenda item, value 2 means a hidden agenda item,
// see openslides.agenda.models.Item.ITEM_TYPE.
var changes = [{key: 'type', value: (topic.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: topic.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
$scope.closeThisDialog(); $scope.closeThisDialog();
}, function (error) { },
function (error) {
// save error: revert all changes by restore // save error: revert all changes by restore
// (refresh) original topic object from server // (refresh) original topic object from server
Topic.refresh(topic); Topic.refresh(topic);
@ -265,8 +252,7 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
'Topic', 'Topic',
'HumanTimeConverter', 'HumanTimeConverter',
'TopicsCsvExample', 'TopicsCsvExample',
'AgendaUpdate', function($scope, gettext, Agenda, Topic, HumanTimeConverter, TopicsCsvExample) {
function($scope, gettext, Agenda, Topic, HumanTimeConverter, TopicsCsvExample, AgendaUpdate) {
// Big TODO: Change wording from "item" to "topic". // Big TODO: Change wording from "item" to "topic".
// import from textarea // import from textarea
$scope.importByLine = function () { $scope.importByLine = function () {
@ -275,12 +261,11 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
$scope.importcounter = 0; $scope.importcounter = 0;
$scope.titleItems.forEach(function(title, index) { $scope.titleItems.forEach(function(title, index) {
var item = {title: title}; var item = {title: title};
item.agenda_type = 1; // The new topic is not hidden.
item.agenda_weight = 1000 + index;
// TODO: create all items in bulk mode // TODO: create all items in bulk mode
Topic.create(item).then( Topic.create(item).then(
function(success) { function(success) {
var changes = [{key: 'type', value: 1},
{key: 'weight', value: 1000 + index}];
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
$scope.importcounter++; $scope.importcounter++;
} }
); );
@ -363,13 +348,12 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
$scope.csvImporting = true; $scope.csvImporting = true;
angular.forEach($scope.items, function (item) { angular.forEach($scope.items, function (item) {
if (item.selected && !item.importerror) { if (item.selected && !item.importerror) {
item.agenda_type = item.type;
item.agenda_comment = item.comment;
item.agenda_duration = item.duration;
item.agenda_weight = item.weight;
Topic.create(item).then( Topic.create(item).then(
function(success) { function(success) {
var changes = [{key: 'duration', value: item.duration},
{key: 'comment', value: item.comment},
{key: 'type', value: item.type},
{key: 'weight', value: item.weight}];
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
item.imported = true; item.imported = true;
} }
); );