Parent items for Agenda items (fixes #2213)

This commit is contained in:
Maximilian Krambach 2016-07-26 16:44:22 +02:00
parent e63c1438f5
commit 126cfb9000
7 changed files with 209 additions and 99 deletions

View File

@ -10,6 +10,7 @@ Version 2.1 (unreleased)
Agenda:
- Added button to remove all speakers from a list of speakers.
- Added option to create or edit agenda items as subitems of others.
Assignments:
- Remove unused assignment config to publish winner election results only.

View File

@ -21,6 +21,28 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users'])
}
])
.factory('AgendaUpdate',[
'Agenda',
function(Agenda) {
return {
saveChanges: function (item_id, changes) {
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',

View File

@ -129,7 +129,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
// open edit dialog
$scope.editDialog = function (item) {
$state.go(item.content_object.collection.replace('/','.')+'.detail.update',
{id: item.content_object.id});
{id: item.content_object.id});
};
// detail view of related item (content object)
$scope.open = function (item) {

View File

@ -36,16 +36,16 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
assignments: function(Assignment) {
return Assignment.findAll();
},
tags: function(Tag) {
return Tag.findAll();
},
items: function(Agenda) {
return Agenda.findAll().catch(
function () {
function() {
return null;
}
);
},
tags: function(Tag) {
return Tag.findAll();
},
phases: function(Assignment) {
return Assignment.getPhases();
}
@ -56,7 +56,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
resolve: {
assignment: function(Assignment, $stateParams) {
return Assignment.find($stateParams.id).then(function(assignment) {
return Assignment.loadRelations(assignment, 'agenda_item');
return assignment;
});
},
users: function(User) {
@ -65,6 +65,13 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
tags: function(Tag) {
return Tag.findAll();
},
items: function(Agenda) {
return Agenda.findAll().catch(
function() {
return null;
}
);
},
phases: function(Assignment) {
return Assignment.getPhases();
}
@ -75,8 +82,8 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
// (from assignment controller use AssignmentForm factory instead to open dialog in front
// of current view without redirect)
.state('assignments.assignment.detail.update', {
onEnter: ['$stateParams', '$state', 'ngDialog', 'Assignment',
function($stateParams, $state, ngDialog, Assignment) {
onEnter: ['$stateParams', '$state', 'ngDialog', 'Assignment', 'Agenda',
function($stateParams, $state, ngDialog, Assignment, Agenda) {
ngDialog.open({
template: 'static/templates/assignments/assignment-form.html',
controller: 'AssignmentUpdateCtrl',
@ -89,6 +96,12 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
return Assignment.loadRelations(assignment, 'agenda_item');
});
},
items: function(Agenda) {
return Agenda.findAll().catch(
function() {
return null;
});
},
},
preCloseCallback: function() {
$state.go('assignments.assignment.detail', {assignment: $stateParams.id});
@ -106,28 +119,32 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
'gettextCatalog',
'operator',
'Tag',
function (gettextCatalog, operator, Tag) {
'Assignment',
'Agenda',
'AgendaTree',
function (gettextCatalog, operator, Tag, Assignment, Agenda, AgendaTree) {
return {
// ngDialog for assignment form
getDialog: function (assignment) {
var resolve;
var resolve = {};
if (assignment) {
resolve = {
assignment: function() {
return assignment;
},
agenda_item: function(Assignment) {
return Assignment.loadRelations(assignment, 'agenda_item');
}
resolve.assignment = function () {
return assignment;
};
resolve.agenda_item = function () {
return Assignment.loadRelations(assignment, 'agenda_item');
};
}
resolve.items = function() {
return Agenda.getAll();
};
return {
template: 'static/templates/assignments/assignment-form.html',
controller: (assignment) ? 'AssignmentUpdateCtrl' : 'AssignmentCreateCtrl',
className: 'ngdialog-theme-default wide-form',
closeByEscape: false,
closeByDocument: false,
resolve: (resolve) ? resolve : null
resolve: resolve
};
},
// angular-formly fields for assignment form
@ -173,6 +190,17 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
},
hide: !operator.hasPerms('assignments.can_manage')
},
{
key: 'agenda_parent_item_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')
},
{
key: 'more',
type: 'checkbox',
@ -203,8 +231,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
'AssignmentForm',
'Assignment',
'Tag',
'Agenda',
'phases',
function($scope, ngDialog, AssignmentForm, Assignment, Tag, phases) {
function($scope, ngDialog, AssignmentForm, Assignment, Tag, Agenda, phases) {
Assignment.bindAll({}, $scope, 'assignments');
Tag.bindAll({}, $scope, 'tags');
$scope.phases = phases;
@ -257,7 +286,7 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
assignment.quickEdit = false;
$scope.alert.show = false;
},
function(error){
function(error) {
var message = '';
for (var e in error.data) {
message += e + ': ' + error.data[e] + ' ';
@ -317,7 +346,6 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
$scope.candidateSelectBox = {};
$scope.phases = phases;
$scope.alert = {};
// open edit dialog
$scope.openDialog = function (assignment) {
ngDialog.open(AssignmentForm.getDialog(assignment));
@ -471,27 +499,22 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
'Assignment',
'AssignmentForm',
'Agenda',
function($scope, Assignment, AssignmentForm, Agenda) {
'AgendaUpdate',
function($scope, Assignment, AssignmentForm, Agenda, AgendaUpdate) {
$scope.model = {};
// set default value for open posts form field
$scope.model.open_posts = 1;
// get all form fields
$scope.formFields = AssignmentForm.getFormFields();
// save assignment
$scope.save = function(assignment) {
Assignment.create(assignment).then(
function(success) {
// find related agenda item
Agenda.find(success.agenda_item_id).then(function(item) {
// check form element and set item type (AGENDA_ITEM = 1, HIDDEN_ITEM = 2)
var type = assignment.showAsAgendaItem ? 1 : 2;
// save only if agenda item type is modified
if (item.type != type) {
item.type = type;
Agenda.save(item);
}
});
// 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);
$scope.closeThisDialog();
}
);
@ -504,18 +527,22 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
'Assignment',
'AssignmentForm',
'Agenda',
'AgendaUpdate',
'assignment',
function($scope, Assignment, AssignmentForm, Agenda, assignment) {
function($scope, Assignment, AssignmentForm, Agenda, AgendaUpdate, assignment) {
$scope.alert = {};
// set initial values for form model by create deep copy of assignment object
// so list/detail view is not updated while editing
$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;
}
}
@ -526,13 +553,9 @@ angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
// save change assignment object on server
Assignment.save(assignment).then(
function(success) {
// check form element and set item type (AGENDA_ITEM = 1, HIDDEN_ITEM = 2)
var type = assignment.showAsAgendaItem ? 1 : 2;
// save only if agenda item type is modified
if (assignment.agenda_item.type != type) {
assignment.agenda_item.type = type;
Agenda.save(assignment.agenda_item);
}
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);
$scope.closeThisDialog();
},
function (error) {

View File

@ -479,6 +479,26 @@ angular.module('OpenSlidesApp.core', [
}
])
// filters the requesting object (id=selfid) from a list of input objects
.filter('notself', function() {
return function(input, selfid) {
var result;
if (selfid) {
result = [];
for (var key in input){
var obj = input[key];
if (selfid != obj.id) {
result.push(obj);
}
}
} else {
result = input;
}
return result;
};
})
// Make sure that the DS factories are loaded by making them a dependency
.run([
'ChatMessage',

View File

@ -647,6 +647,9 @@ angular.module('OpenSlidesApp.core.site', [
resolve: {
customslide: function(Customslide, $stateParams) {
return Customslide.find($stateParams.id);
},
items: function(Agenda) {
return Agenda.findAll();
}
}
})
@ -655,21 +658,26 @@ angular.module('OpenSlidesApp.core.site', [
// (from customslide controller use CustomSlideForm factory instead to open dialog in front
// of current view without redirect)
.state('core.customslide.detail.update', {
onEnter: ['$stateParams', '$state', 'ngDialog', 'Customslide',
function($stateParams, $state, ngDialog, Customslide) {
onEnter: ['$stateParams', '$state', 'ngDialog', 'Customslide', 'Agenda',
function($stateParams, $state, ngDialog, Customslide, Agenda) {
ngDialog.open({
template: 'static/templates/core/customslide-form.html',
controller: 'CustomslideUpdateCtrl',
className: 'ngdialog-theme-default wide-form',
resolve: {
customslide: function() {return Customslide.find($stateParams.id);}
customslide: function() {
return Customslide.find($stateParams.id);
},
items: function() {
return Agenda.findAll();
}
},
preCloseCallback: function() {
$state.go('core.customslide.detail', {customslide: $stateParams.id});
return true;
}
});
}]
}],
})
// tag
.state('core.tag', {
@ -979,7 +987,9 @@ angular.module('OpenSlidesApp.core.site', [
'gettextCatalog',
'Editor',
'Mediafile',
function (gettextCatalog, Editor, Mediafile) {
'Agenda',
'AgendaTree',
function (gettextCatalog, Editor, Mediafile, Agenda, AgendaTree) {
return {
// ngDialog for customslide form
getDialog: function (customslide) {
@ -1038,7 +1048,16 @@ angular.module('OpenSlidesApp.core.site', [
description: gettextCatalog.getString('If deactivated it appears as internal item on agenda.')
}
},
];
{
key: 'agenda_parent_item_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 ...')
}
}];
}
};
}
@ -1251,7 +1270,6 @@ angular.module('OpenSlidesApp.core.site', [
function($scope, ngDialog, CustomslideForm, Customslide, customslide) {
Customslide.bindOne(customslide.id, $scope, 'customslide');
Customslide.loadRelations(customslide, 'agenda_item');
// open edit dialog
$scope.openDialog = function (customslide) {
ngDialog.open(CustomslideForm.getDialog(customslide));
@ -1265,30 +1283,24 @@ angular.module('OpenSlidesApp.core.site', [
'Customslide',
'CustomslideForm',
'Agenda',
function($scope, $state, Customslide, CustomslideForm, Agenda) {
'AgendaUpdate',
function($scope, $state, Customslide, CustomslideForm, Agenda, AgendaUpdate) {
$scope.customslide = {};
$scope.model = {};
$scope.model.showAsAgendaItem = true;
// get all form fields
$scope.formFields = CustomslideForm.getFormFields();
// save form
$scope.save = function (customslide) {
Customslide.create(customslide).then(
function(success) {
// find related agenda item
Agenda.find(success.agenda_item_id).then(function(item) {
// check form element and set item type (AGENDA_ITEM = 1, HIDDEN_ITEM = 2)
var type = customslide.showAsAgendaItem ? 1 : 2;
// save only if agenda item type is modified
if (item.type != type) {
item.type = type;
Agenda.save(item);
}
});
$scope.closeThisDialog();
}
);
// 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: (customslide.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: customslide.agenda_parent_item_id}];
AgendaUpdate.saveChanges(success.agenda_item_id,changes);
});
$scope.closeThisDialog();
};
}
])
@ -1299,8 +1311,10 @@ angular.module('OpenSlidesApp.core.site', [
'Customslide',
'CustomslideForm',
'Agenda',
'AgendaUpdate',
'customslide',
function($scope, $state, Customslide, CustomslideForm, Agenda, customslide) {
function($scope, $state, Customslide, CustomslideForm, Agenda, AgendaUpdate, customslide) {
Customslide.loadRelations(customslide, 'agenda_item');
$scope.alert = {};
// set initial values for form model by create deep copy of customslide object
// so list/detail view is not updated while editing
@ -1311,25 +1325,22 @@ angular.module('OpenSlidesApp.core.site', [
if ($scope.formFields[i].key == "showAsAgendaItem") {
// get state from agenda item (hidden/internal or agenda item)
$scope.formFields[i].defaultValue = !customslide.agenda_item.is_hidden;
} else if($scope.formFields[i].key == "agenda_parent_item_id") {
$scope.formFields[i].defaultValue = customslide.agenda_item.parent_id;
}
}
// save form
$scope.save = function (customslide) {
// inject the changed customslide (copy) object back into DS store
Customslide.inject(customslide);
// save change customslide object on server
Customslide.save(customslide).then(
Customslide.create(customslide).then(
function(success) {
// save agenda specific stuff
var type = customslide.showAsAgendaItem ? 1 : 2;
if (customslide.agenda_item.type != type) {
customslide.agenda_item.type = type;
Agenda.save(customslide.agenda_item);
}
// 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: (customslide.showAsAgendaItem ? 1 : 2)},
{key: 'parent_id', value: customslide.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 customslide object from server
Customslide.refresh(customslide);

View File

@ -253,11 +253,16 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
return User.findAll().catch(
function () {
return null;
}
);
});
},
workflows: function(Workflow) {
return Workflow.findAll();
},
items: function(Agenda) {
return Agenda.findAll().catch(
function () {
return null;
});
}
}
})
@ -276,6 +281,13 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
}
);
},
items: function(Agenda) {
return Agenda.findAll().catch(
function () {
return null;
}
);
},
mediafiles: function(Mediafile) {
return Mediafile.findAll().catch(
function () {
@ -307,6 +319,13 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
return Motion.loadRelations(motion, 'agenda_item');
});
},
items: function(Agenda) {
return Agenda.findAll().catch(
function() {
return null;
}
);
}
},
preCloseCallback: function() {
$state.go('motions.motion.detail', {motion: $stateParams.id});
@ -384,7 +403,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'Tag',
'User',
'Workflow',
function (gettextCatalog, operator, Editor, Category, Config, Mediafile, Tag, User, Workflow) {
'Agenda',
'AgendaTree',
function (gettextCatalog, operator, Editor, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaTree) {
return {
// ngDialog for motion form
getDialog: function (motion) {
@ -482,6 +503,17 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
},
hide: !operator.hasPerms('motions.can_manage')
},
{
key: 'agenda_parent_item_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')
},
{
key: 'more',
type: 'checkbox',
@ -1015,7 +1047,8 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'User',
'Workflow',
'Agenda',
function($scope, gettext, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow, Agenda) {
'AgendaUpdate',
function($scope, gettext, Motion, MotionForm, Category, Config, Mediafile, Tag, User, Workflow, Agenda, AgendaUpdate) {
Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles');
Tag.bindAll({}, $scope, 'tags');
@ -1034,16 +1067,11 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
$scope.save = function (motion) {
Motion.create(motion).then(
function(success) {
// find related agenda item
Agenda.find(success.agenda_item_id).then(function(item) {
// check form element and set item type (AGENDA_ITEM = 1, HIDDEN_ITEM = 2)
var type = motion.showAsAgendaItem ? 1 : 2;
// save only if agenda item type is modified
if (item.type != type) {
item.type = type;
Agenda.save(item);
}
});
// 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);
$scope.closeThisDialog();
}
);
@ -1062,8 +1090,9 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
'User',
'Workflow',
'Agenda',
'AgendaUpdate',
'motion',
function($scope, Motion, Category, Config, Mediafile, MotionForm, Tag, User, Workflow, Agenda, motion) {
function($scope, Motion, Category, Config, Mediafile, MotionForm, Tag, User, Workflow, Agenda, AgendaUpdate, motion) {
Category.bindAll({}, $scope, 'categories');
Mediafile.bindAll({}, $scope, 'mediafiles');
Tag.bindAll({}, $scope, 'tags');
@ -1111,6 +1140,10 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
// 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;
}
}
// save motion
@ -1120,14 +1153,14 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
// save change motion object on server
Motion.save(motion, { method: 'PATCH' }).then(
function(success) {
// check form element and set item type (AGENDA_ITEM = 1, HIDDEN_ITEM = 2)
var type = motion.showAsAgendaItem ? 1 : 2;
// save only if agenda item type is modified
if (motion.agenda_item.type != type) {
motion.agenda_item.type = type;
Agenda.save(motion.agenda_item);
}
$scope.closeThisDialog();
Agenda.find(success.agenda_item_id).then(function(item) {
// 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);
$scope.closeThisDialog();
});
},
function (error) {
// save error: revert all changes by restore