Merge pull request #3341 from normanjaeckel/BetterAgenda
Supported server side setup of new agenda items (type and parent).
This commit is contained in:
commit
ec2fcbdd73
@ -12,6 +12,7 @@ Agenda:
|
||||
- Fixed wrong sorting of last speakers [#3193].
|
||||
- Fixed issue when sorting a new inserted speaker [#3210].
|
||||
- New permission for managing lists of speakers [#3366].
|
||||
- Fixed multiple request on creation of agenda related items [#3341].
|
||||
|
||||
Motions:
|
||||
- New export dialog [#3185].
|
||||
|
@ -14,16 +14,24 @@ def listen_to_related_object_post_save(sender, instance, created, **kwargs):
|
||||
Receiver function to create agenda items. It is connected to the signal
|
||||
django.db.models.signals.post_save during app loading.
|
||||
|
||||
Do not run caching and autoupdate if the instance as an attribute
|
||||
skip_autoupdate (regardless of its truthy or falsy conent).
|
||||
The agenda_item_update_information container may have fields like type,
|
||||
parent_id, comment, duration, weight or skip_autoupdate.
|
||||
|
||||
Do not run caching and autoupdate if the instance has a key
|
||||
skip_autoupdate in the agenda_item_update_information container.
|
||||
"""
|
||||
if hasattr(instance, 'get_agenda_title'):
|
||||
if created:
|
||||
attrs = {}
|
||||
for attr in ('type', 'parent_id', 'comment', 'duration', 'weight'):
|
||||
if instance.agenda_item_update_information.get(attr):
|
||||
attrs[attr] = instance.agenda_item_update_information.get(attr)
|
||||
Item.objects.create(content_object=instance, **attrs)
|
||||
|
||||
# If the object is created, the related_object has to be sent again.
|
||||
Item.objects.create(content_object=instance)
|
||||
if not hasattr(instance, 'skip_autoupdate'):
|
||||
if not instance.agenda_item_update_information.get('skip_autoupdate'):
|
||||
inform_changed_data(instance)
|
||||
elif not hasattr(instance, 'skip_autoupdate'):
|
||||
elif not instance.agenda_item_update_information.get('skip_autoupdate'):
|
||||
# If the object has changed, then also the agenda item has to be sent.
|
||||
inform_changed_data(instance.agenda_item)
|
||||
|
||||
|
@ -21,32 +21,6 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users'])
|
||||
}
|
||||
])
|
||||
|
||||
.factory('AgendaUpdate',[
|
||||
'Agenda',
|
||||
'operator',
|
||||
function(Agenda, operator) {
|
||||
return {
|
||||
saveChanges: function (item_id, changes) {
|
||||
// change agenda item only if user has the permission to do that
|
||||
if (operator.hasPerms('agenda.can_manage agenda.can_see_hidden_items')) {
|
||||
Agenda.find(item_id).then(function (item) {
|
||||
var something = false;
|
||||
_.each(changes, function(change) {
|
||||
if (change.value !== item[change.key]) {
|
||||
item[change.key] = change.value;
|
||||
something = true;
|
||||
}
|
||||
});
|
||||
if (something === true) {
|
||||
Agenda.save(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
])
|
||||
|
||||
.factory('Agenda', [
|
||||
'$http',
|
||||
'DS',
|
||||
|
@ -330,6 +330,11 @@ class Assignment(RESTModelMixin, models.Model):
|
||||
vote_results_dict[candidate].append(votes)
|
||||
return vote_results_dict
|
||||
|
||||
"""
|
||||
Container for runtime information for agenda app (on create or update of this instance).
|
||||
"""
|
||||
agenda_item_update_information = {} # type: Dict[str, Any]
|
||||
|
||||
def get_agenda_title(self):
|
||||
return str(self)
|
||||
|
||||
|
@ -194,6 +194,8 @@ class AssignmentFullSerializer(ModelSerializer):
|
||||
"""
|
||||
assignment_related_users = AssignmentRelatedUserSerializer(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:
|
||||
model = Assignment
|
||||
@ -207,6 +209,8 @@ class AssignmentFullSerializer(ModelSerializer):
|
||||
'poll_description_default',
|
||||
'polls',
|
||||
'agenda_item_id',
|
||||
'agenda_type',
|
||||
'agenda_parent_id',
|
||||
'tags',)
|
||||
validators = (posts_validator,)
|
||||
|
||||
@ -215,6 +219,19 @@ class AssignmentFullSerializer(ModelSerializer):
|
||||
data['description'] = validate_html(data['description'])
|
||||
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):
|
||||
"""
|
||||
|
@ -155,21 +155,21 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Default comment on the ballot paper')
|
||||
}
|
||||
},
|
||||
{
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated the election appears as internal item on agenda.')
|
||||
},
|
||||
hide: !(operator.hasPerms('assignments.can_manage') && operator.hasPerms('agenda.can_manage'))
|
||||
}];
|
||||
|
||||
// parent item
|
||||
// show as agenda item + parent item
|
||||
if (isCreateForm) {
|
||||
formFields.push({
|
||||
key: 'agenda_parent_item_id',
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated the election appears as internal item on agenda.')
|
||||
},
|
||||
hide: !(operator.hasPerms('assignments.can_manage') && operator.hasPerms('agenda.can_manage'))
|
||||
});
|
||||
formFields.push({
|
||||
key: 'agenda_parent_id',
|
||||
type: 'select-single',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Parent item'),
|
||||
@ -627,9 +627,8 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
'Assignment',
|
||||
'AssignmentForm',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'ErrorMessage',
|
||||
function($scope, $state, Assignment, AssignmentForm, Agenda, AgendaUpdate, ErrorMessage) {
|
||||
function($scope, $state, Assignment, AssignmentForm, Agenda, ErrorMessage) {
|
||||
$scope.model = {};
|
||||
// set default value for open posts form field
|
||||
$scope.model.open_posts = 1;
|
||||
@ -637,13 +636,10 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
$scope.formFields = AssignmentForm.getFormFields(true);
|
||||
// save assignment
|
||||
$scope.save = function(assignment, gotoDetailView) {
|
||||
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(
|
||||
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) {
|
||||
$state.go('assignments.assignment.detail', {id: success.id});
|
||||
}
|
||||
@ -663,10 +659,9 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
'Assignment',
|
||||
'AssignmentForm',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'assignmentId',
|
||||
'ErrorMessage',
|
||||
function($scope, $state, Assignment, AssignmentForm, Agenda, AgendaUpdate, assignmentId, ErrorMessage) {
|
||||
function($scope, $state, Assignment, AssignmentForm, Agenda, assignmentId, ErrorMessage) {
|
||||
var assignment = Assignment.get(assignmentId);
|
||||
$scope.alert = {};
|
||||
// set initial values for form model by create deep copy of assignment object
|
||||
@ -674,26 +669,14 @@ angular.module('OpenSlidesApp.assignments.site', [
|
||||
$scope.model = angular.copy(assignment);
|
||||
// get all form fields
|
||||
$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;
|
||||
} else if($scope.formFields[i].key == 'agenda_parent_item_id') {
|
||||
$scope.formFields[i].defaultValue = agenda_item.parent_id;
|
||||
}
|
||||
}
|
||||
|
||||
// save assignment
|
||||
$scope.save = function (assignment, gotoDetailView) {
|
||||
// inject the changed assignment (copy) object back into DS store
|
||||
Assignment.inject(assignment);
|
||||
// save change assignment object on server
|
||||
// save changed assignment object on server
|
||||
Assignment.save(assignment).then(
|
||||
function(success) {
|
||||
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) {
|
||||
$state.go('assignments.assignment.detail', {id: success.id});
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
from typing import Any, Dict # noqa
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.core.exceptions import ImproperlyConfigured, ValidationError
|
||||
@ -628,6 +630,11 @@ class Motion(RESTModelMixin, models.Model):
|
||||
if self.recommendation is not None:
|
||||
self.set_state(self.recommendation)
|
||||
|
||||
"""
|
||||
Container for runtime information for agenda app (on create or update of this instance).
|
||||
"""
|
||||
agenda_item_update_information = {} # type: Dict[str, Any]
|
||||
|
||||
def get_agenda_title(self):
|
||||
"""
|
||||
Return a simple title string for the agenda.
|
||||
@ -895,6 +902,11 @@ class MotionBlock(RESTModelMixin, models.Model):
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs) # type: ignore
|
||||
|
||||
"""
|
||||
Container for runtime information for agenda app (on create or update of this instance).
|
||||
"""
|
||||
agenda_item_update_information = {} # type: Dict[str, Any]
|
||||
|
||||
@property
|
||||
def agenda_item(self):
|
||||
"""
|
||||
|
@ -49,9 +49,25 @@ class MotionBlockSerializer(ModelSerializer):
|
||||
"""
|
||||
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:
|
||||
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):
|
||||
@ -288,6 +304,8 @@ class MotionSerializer(ModelSerializer):
|
||||
required=False,
|
||||
validators=[validate_workflow_field],
|
||||
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:
|
||||
model = Motion
|
||||
@ -314,6 +332,8 @@ class MotionSerializer(ModelSerializer):
|
||||
'attachments',
|
||||
'polls',
|
||||
'agenda_item_id',
|
||||
'agenda_type',
|
||||
'agenda_parent_id',
|
||||
'log_messages',)
|
||||
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):
|
||||
"""
|
||||
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.title = validated_data['title']
|
||||
@ -344,6 +367,8 @@ class MotionSerializer(ModelSerializer):
|
||||
motion.comments = validated_data.get('comments')
|
||||
motion.parent = validated_data.get('parent')
|
||||
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()
|
||||
if validated_data.get('submitters'):
|
||||
motion.submitters.add(*validated_data['submitters'])
|
||||
|
@ -69,8 +69,8 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
|
||||
};
|
||||
},
|
||||
// Get angular-formly fields.
|
||||
getFormFields: function () {
|
||||
return [
|
||||
getFormFields: function (isCreateForm) {
|
||||
var formFields = [
|
||||
{
|
||||
key: 'title',
|
||||
type: 'input',
|
||||
@ -78,7 +78,11 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
|
||||
label: gettextCatalog.getString('Title')
|
||||
}
|
||||
},
|
||||
{
|
||||
];
|
||||
|
||||
// show as agenda item + parent item
|
||||
if (isCreateForm) {
|
||||
formFields.push({
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
@ -86,18 +90,21 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
|
||||
description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.')
|
||||
},
|
||||
hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage'))
|
||||
},
|
||||
{
|
||||
key: 'agenda_parent_item_id',
|
||||
});
|
||||
formFields.push({
|
||||
key: 'agenda_parent_id',
|
||||
type: 'select-single',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Parent item'),
|
||||
options: AgendaTree.getFlatTree(Agenda.getAll()),
|
||||
ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id',
|
||||
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',
|
||||
'MotionBlock',
|
||||
'MotionBlockForm',
|
||||
'AgendaUpdate',
|
||||
function($scope, MotionBlock, MotionBlockForm, AgendaUpdate) {
|
||||
function($scope, MotionBlock, MotionBlockForm) {
|
||||
// Prepare form.
|
||||
$scope.model = {};
|
||||
$scope.model.showAsAgendaItem = true;
|
||||
|
||||
// Get all form fields.
|
||||
$scope.formFields = MotionBlockForm.getFormFields();
|
||||
$scope.formFields = MotionBlockForm.getFormFields(true);
|
||||
|
||||
// Save form.
|
||||
$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(
|
||||
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();
|
||||
},
|
||||
function (error) {
|
||||
@ -227,11 +230,8 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
|
||||
'$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');
|
||||
function($scope, $state, MotionBlock, MotionBlockForm, motionBlockId) {
|
||||
$scope.alert = {};
|
||||
|
||||
// Prepare form. Set initial values by creating a deep copy of
|
||||
@ -241,23 +241,14 @@ angular.module('OpenSlidesApp.motions.motionBlock', [])
|
||||
|
||||
// Get all form fields.
|
||||
$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.
|
||||
$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(
|
||||
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();
|
||||
},
|
||||
function (error) {
|
||||
|
@ -376,21 +376,21 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
description: gettextCatalog.getString("Don't create a new version.")
|
||||
},
|
||||
hide: true
|
||||
},
|
||||
{
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated the motion appears as internal item on agenda.')
|
||||
},
|
||||
hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage'))
|
||||
}];
|
||||
|
||||
// parent item
|
||||
// show as agenda item + parent item
|
||||
if (isCreateForm) {
|
||||
formFields.push({
|
||||
key: 'agenda_parent_item_id',
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated the motion appears as internal item on agenda.')
|
||||
},
|
||||
hide: !(operator.hasPerms('motions.can_manage') && operator.hasPerms('agenda.can_manage'))
|
||||
});
|
||||
formFields.push({
|
||||
key: 'agenda_parent_id',
|
||||
type: 'select-single',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Parent item'),
|
||||
@ -1890,10 +1890,9 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
'User',
|
||||
'Workflow',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'ErrorMessage',
|
||||
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');
|
||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||
Tag.bindAll({}, $scope, 'tags');
|
||||
@ -1927,13 +1926,10 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
|
||||
// save motion
|
||||
$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(
|
||||
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) {
|
||||
$state.go('motions.motion.detail', {id: success.id});
|
||||
}
|
||||
@ -1959,13 +1955,12 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
'User',
|
||||
'Workflow',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'motionId',
|
||||
'operator',
|
||||
'ErrorMessage',
|
||||
'EditingWarning',
|
||||
function($scope, $state, Motion, Category, Config, Mediafile, MotionForm,
|
||||
Tag, User, Workflow, Agenda, AgendaUpdate, motionId, operator, ErrorMessage,
|
||||
Tag, User, Workflow, Agenda, motionId, operator, ErrorMessage,
|
||||
EditingWarning) {
|
||||
Category.bindAll({}, $scope, 'categories');
|
||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||
@ -2007,18 +2002,10 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
$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") {
|
||||
// get saved workflow id from state
|
||||
$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
|
||||
@ -2029,14 +2016,9 @@ angular.module('OpenSlidesApp.motions.site', [
|
||||
$scope.save = function (motion, gotoDetailView) {
|
||||
// inject the changed motion (copy) object back into DS store
|
||||
Motion.inject(motion);
|
||||
// save change motion object on server
|
||||
// save changed motion object on server
|
||||
Motion.save(motion).then(
|
||||
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) {
|
||||
$state.go('motions.motion.detail', {id: success.id});
|
||||
}
|
||||
|
@ -596,7 +596,8 @@ class CategoryViewSet(ModelViewSet):
|
||||
# Remove old identifiers
|
||||
for motion in motions:
|
||||
motion.identifier = None
|
||||
motion.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)
|
||||
|
||||
# Set new identifers and change identifiers of amendments.
|
||||
@ -615,7 +616,8 @@ class CategoryViewSet(ModelViewSet):
|
||||
obj['new_identifier'],
|
||||
child.identifier,
|
||||
count=1)
|
||||
child.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)
|
||||
instances.append(child)
|
||||
instances.append(child.agenda_item)
|
||||
|
@ -1,3 +1,5 @@
|
||||
from typing import Any, Dict # noqa
|
||||
|
||||
from django.contrib.contenttypes.fields import GenericRelation
|
||||
from django.db import models
|
||||
|
||||
@ -54,6 +56,11 @@ class Topic(RESTModelMixin, models.Model):
|
||||
id=self.pk)
|
||||
return super().delete(skip_autoupdate=skip_autoupdate, *args, **kwargs) # type: ignore
|
||||
|
||||
"""
|
||||
Container for runtime information for agenda app (on create or update of this instance).
|
||||
"""
|
||||
agenda_item_update_information = {} # type: Dict[str, Any]
|
||||
|
||||
@property
|
||||
def agenda_item(self):
|
||||
"""
|
||||
|
@ -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 .models import Topic
|
||||
@ -8,11 +8,49 @@ class TopicSerializer(ModelSerializer):
|
||||
"""
|
||||
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:
|
||||
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):
|
||||
if 'text' in data:
|
||||
data['text'] = validate_html(data['text'])
|
||||
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
|
||||
|
@ -116,28 +116,28 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
}
|
||||
});
|
||||
}
|
||||
// show as agenda item
|
||||
formFields.push({
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.')
|
||||
},
|
||||
hide: !operator.hasPerms('agenda.can_manage')
|
||||
});
|
||||
|
||||
// parent item
|
||||
// show as agenda item + parent item
|
||||
if (isCreateForm) {
|
||||
formFields.push({
|
||||
key: 'agenda_parent_item_id',
|
||||
key: 'showAsAgendaItem',
|
||||
type: 'checkbox',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Show as agenda item'),
|
||||
description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.')
|
||||
},
|
||||
hide: !operator.hasPerms('agenda.can_manage')
|
||||
});
|
||||
formFields.push({
|
||||
key: 'agenda_parent_id',
|
||||
type: 'select-single',
|
||||
templateOptions: {
|
||||
label: gettextCatalog.getString('Parent item'),
|
||||
options: AgendaTree.getFlatTree(Agenda.getAll()),
|
||||
ngOptions: 'item.id as item.getListViewTitle() for item in to.options | notself : model.agenda_item_id',
|
||||
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',
|
||||
'TopicForm',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'ErrorMessage',
|
||||
function($scope, $state, Topic, TopicForm, Agenda, AgendaUpdate, ErrorMessage) {
|
||||
function($scope, $state, Topic, TopicForm, Agenda, ErrorMessage) {
|
||||
$scope.topic = {};
|
||||
$scope.model = {};
|
||||
$scope.model.showAsAgendaItem = true;
|
||||
@ -196,13 +195,10 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
$scope.formFields = TopicForm.getFormFields(true);
|
||||
// save form
|
||||
$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(
|
||||
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();
|
||||
}, function (error) {
|
||||
$scope.alert = ErrorMessage.forAlert(error);
|
||||
@ -218,10 +214,9 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
'Topic',
|
||||
'TopicForm',
|
||||
'Agenda',
|
||||
'AgendaUpdate',
|
||||
'topicId',
|
||||
'ErrorMessage',
|
||||
function($scope, $state, Topic, TopicForm, Agenda, AgendaUpdate, topicId, ErrorMessage) {
|
||||
function($scope, $state, Topic, TopicForm, Agenda, topicId, ErrorMessage) {
|
||||
var topic = Topic.get(topicId);
|
||||
$scope.alert = {};
|
||||
// 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);
|
||||
// get all form fields
|
||||
$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
|
||||
$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) {
|
||||
// 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();
|
||||
}, function (error) {
|
||||
},
|
||||
function (error) {
|
||||
// save error: revert all changes by restore
|
||||
// (refresh) original topic object from server
|
||||
Topic.refresh(topic);
|
||||
@ -265,8 +252,7 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
'Topic',
|
||||
'HumanTimeConverter',
|
||||
'TopicsCsvExample',
|
||||
'AgendaUpdate',
|
||||
function($scope, gettext, Agenda, Topic, HumanTimeConverter, TopicsCsvExample, AgendaUpdate) {
|
||||
function($scope, gettext, Agenda, Topic, HumanTimeConverter, TopicsCsvExample) {
|
||||
// Big TODO: Change wording from "item" to "topic".
|
||||
// import from textarea
|
||||
$scope.importByLine = function () {
|
||||
@ -275,12 +261,11 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
$scope.importcounter = 0;
|
||||
$scope.titleItems.forEach(function(title, index) {
|
||||
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
|
||||
Topic.create(item).then(
|
||||
function(success) {
|
||||
var changes = [{key: 'type', value: 1},
|
||||
{key: 'weight', value: 1000 + index}];
|
||||
AgendaUpdate.saveChanges(success.agenda_item_id, changes);
|
||||
$scope.importcounter++;
|
||||
}
|
||||
);
|
||||
@ -363,13 +348,12 @@ angular.module('OpenSlidesApp.topics.site', ['OpenSlidesApp.topics', 'OpenSlides
|
||||
$scope.csvImporting = true;
|
||||
angular.forEach($scope.items, function (item) {
|
||||
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(
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user