diff --git a/CHANGELOG b/CHANGELOG index f75403a58..eabffa841 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -12,6 +12,8 @@ Agenda: - Added button to remove all speakers from a list of speakers. - Added option to create or edit agenda items as subitems of others. - Fixed security issue: Comments were shown for unprivileged users. +- Added option to choose whether to show the current list of speakers slide + as a slide or an overlay. Core: - Added support for multiple projectors. diff --git a/openslides/agenda/projector.py b/openslides/agenda/projector.py index db1b10e52..84b8813f6 100644 --- a/openslides/agenda/projector.py +++ b/openslides/agenda/projector.py @@ -73,13 +73,19 @@ class ListOfSpeakersSlide(ProjectorElement): return {'agenda_item_id': self.config_entry.get('id')} -class CurrentListOfSpeakersMetaClass(ProjectorElement): +class CurrentListOfSpeakersSlide(ProjectorElement): """ - Main class for the list of speaker slides. + Slide for the current list of speakers. + """ + name = 'agenda/current-list-of-speakers' - """ def get_requirements(self, config_entry): - items = self.get_agenda_items(config['projector_currentListOfSpeakers_reference']) + # The query mechanism on client needs the referenced projector. + reference_projector = Projector.objects.get( + pk=config['projector_currentListOfSpeakers_reference']) + yield reference_projector + + items = self.get_agenda_items(reference_projector) for item in items: yield item for speaker in item.speakers.filter(end_time=None): @@ -90,8 +96,7 @@ class CurrentListOfSpeakersMetaClass(ProjectorElement): # Yield last speakers yield speaker.user - def get_agenda_items(self, projector_id): - projector = Projector.objects.get(pk=projector_id) + def get_agenda_items(self, projector): for element in projector.elements.values(): agenda_item_id = element.get('agenda_item_id') if agenda_item_id is not None: @@ -99,26 +104,23 @@ class CurrentListOfSpeakersMetaClass(ProjectorElement): def get_collection_elements_required_for_this(self, collection_element, config_entry): output = super().get_collection_elements_required_for_this(collection_element, config_entry) - # Full update if agenda_item changes because then we may have new - # candidates and therefor need new users. - items = self.get_agenda_items(config['projector_currentListOfSpeakers_reference']) - for item in items: - if collection_element == CollectionElement.from_values(item.get_collection_string(), item.pk): - output.extend(self.get_requirements_as_collection_elements(config_entry)) - break + # Full update if agenda_item or referenced projector changes because + # then we may have new candidates and therefor need new users. + reference_projector = Projector.objects.get( + pk=config['projector_currentListOfSpeakers_reference']) + is_reference_projector = collection_element == CollectionElement.from_values( + reference_projector.get_collection_string(), + reference_projector.pk) + is_config = ( + collection_element.collection_string == 'core/config' and + collection_element.information.get('changed_config') == 'projector_currentListOfSpeakers_reference') + + if is_reference_projector or is_config: + output.extend(self.get_requirements_as_collection_elements(config_entry)) + else: + items = self.get_agenda_items(reference_projector) + for item in items: + if collection_element == CollectionElement.from_values(item.get_collection_string(), item.pk): + output.extend(self.get_requirements_as_collection_elements(config_entry)) + break return output - - -class CurrentListOfSpeakersSlide(CurrentListOfSpeakersMetaClass): - """ - Slide for the current list of speakers. - """ - name = 'agenda/current-list-of-speakers' - - -class CurrentListOfSpeakersOverlaySlide(CurrentListOfSpeakersMetaClass): - """ - List of speakers overlay. - Subclass of ListOfSpeakers - """ - name = 'agenda/current-list-of-speakers-overlay' diff --git a/openslides/agenda/static/js/agenda/base.js b/openslides/agenda/static/js/agenda/base.js index e6ff3793b..a01c7b150 100644 --- a/openslides/agenda/static/js/agenda/base.js +++ b/openslides/agenda/static/js/agenda/base.js @@ -289,101 +289,73 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users']) } ]) -// TODO: Remove all find() calls from the projector logic. It is also used on the site so this has to be -// changed with the refactoring of the site autoupdate. .factory('CurrentListOfSpeakersItem', [ 'Projector', - 'Assignment', // TODO: Remove this after refactoring of data loading on start. - 'Topic', // TODO: Remove this after refactoring of data loading on start. - 'Motion', // TODO: Remove this after refactoring of data loading on start. - 'MotionBlock', // TODO: Remove this after refactoring of data loading on start. 'Agenda', - function (Projector, Assignment, Topic, Motion, MotionBlock, Agenda) { + function (Projector, Agenda) { return { getItem: function (projectorId) { - var elementPromise; - return Projector.find(projectorId).then(function (projector) { - // scan all elements + var projector = Projector.get(projectorId), item; + if (projector) { _.forEach(projector.elements, function(element) { - switch(element.name) { - case 'motions/motion': - elementPromise = Motion.find(element.id).then(function(motion) { - return Motion.loadRelations(motion, 'agenda_item').then(function() { - return motion.agenda_item; - }); - }); - break; - case 'motions/motion-block': - elementPromise = MotionBlock.find(element.id).then(function(motionBlock) { - return MotionBlock.loadRelations(motionBlock, 'agenda_item').then(function() { - return motionBlock.agenda_item; - }); - }); - break; - case 'topics/topic': - elementPromise = Topic.find(element.id).then(function(topic) { - return Topic.loadRelations(topic, 'agenda_item').then(function() { - return topic.agenda_item; - }); - }); - break; - case 'assignments/assignment': - elementPromise = Assignment.find(element.id).then(function(assignment) { - return Assignment.loadRelations(assignment, 'agenda_item').then(function() { - return assignment.agenda_item; - }); - }); - break; - case 'agenda/list-of-speakers': - elementPromise = Agenda.find(element.id).then(function(item) { - return item; - }); + if (element.agenda_item_id) { + item = Agenda.get(element.agenda_item_id); } }); - return elementPromise; - }); + } + return item; } }; } ]) -.factory('ListOfSpeakersOverlay', [ +.factory('CurrentListOfSpeakersSlide', [ '$http', 'Projector', - 'gettextCatalog', - 'gettext', - function($http, Projector, gettextCatalog, gettext) { - var name = 'agenda/current-list-of-speakers-overlay'; + function($http, Projector) { + var name = 'agenda/current-list-of-speakers'; return { - name: name, - verboseName: gettext('List of speakers overlay'), project: function (projectorId, overlay) { - var isProjectedId = this.isProjected(overlay); - if (isProjectedId.length > 0) { - // Deactivate - var projector = Projector.get(isProjectedId[0]); - var uuid; - _.forEach(projector.elements, function (element) { - if (element.name == 'agenda/current-list-of-speakers-overlay') { - uuid = element.uuid; - } - }); - $http.post('/rest/core/projector/' + isProjectedId + '/deactivate_elements/', - [uuid]); - } - // Activate, if the projector_id is a new projector. - if (isProjectedId != projectorId) { - return $http.post( - '/rest/core/projector/' + projectorId + '/activate_elements/', - [{name: 'agenda/current-list-of-speakers-overlay',stable: true}]); + var isProjected = this.isProjectedWithOverlayStatus(); + _.forEach(isProjected, function (mapping) { + $http.post('/rest/core/projector/' + mapping.projectorId + '/deactivate_elements/', + [mapping.uuid] + ); + }); + + // The slide was projected, if the id matches. If the overlay is given, also + // the overlay is checked + var wasProjectedBefore = _.some(isProjected, function (mapping) { + var value = (mapping.projectorId === projectorId); + if (overlay !== undefined) { + value = value && (mapping.overlay === overlay); + } + return value; + }); + overlay = overlay || false; // set overlay if it wasn't defined + + if (!wasProjectedBefore) { + var activate = function () { + return $http.post( + '/rest/core/projector/' + projectorId + '/activate_elements/', + [{name: name, + stable: overlay, // if this is an overlay, it should not be + // removed by changing the main content + overlay: overlay}] + ); + }; + if (!overlay) { + // clear all elements on this projector, because we are _not_ using the overlay. + $http.post('/rest/core/projector/' + projectorId + '/clear_elements/').then(activate); + } else { + activate(); + } } }, isProjected: function () { // Returns the ids of all projectors with an agenda-item element. Else return an empty list. var predicate = function (element) { - var value; - value = element.name == 'agenda/current-list-of-speakers-overlay'; - return value; + return element.name === name; }; var isProjectedIds = []; Projector.getAll().forEach(function (projector) { @@ -392,7 +364,23 @@ angular.module('OpenSlidesApp.agenda', ['OpenSlidesApp.users']) } }); return isProjectedIds; - } + }, + // Returns a list of mappings between pojector id, overlay and uuid. + isProjectedWithOverlayStatus: function () { + var mapping = []; + _.forEach(Projector.getAll(), function (projector) { + _.forEach(projector.elements, function (element, uuid) { + if (element.name === name) { + mapping.push({ + projectorId: projector.id, + uuid: uuid, + overlay: element.overlay || false, + }); + } + }); + }); + return mapping; + }, }; } ]) diff --git a/openslides/agenda/static/js/agenda/projector.js b/openslides/agenda/static/js/agenda/projector.js index 83dccbea7..d2d010691 100644 --- a/openslides/agenda/static/js/agenda/projector.js +++ b/openslides/agenda/static/js/agenda/projector.js @@ -16,9 +16,6 @@ angular.module('OpenSlidesApp.agenda.projector', ['OpenSlidesApp.agenda']) slidesProvider.registerSlide('agenda/current-list-of-speakers', { template: 'static/templates/agenda/slide-current-list-of-speakers.html', }); - slidesProvider.registerSlide('agenda/current-list-of-speakers-overlay', { - template: 'static/templates/agenda/slide-current-list-of-speakers-overlay.html', - }); } ]) @@ -27,7 +24,9 @@ angular.module('OpenSlidesApp.agenda.projector', ['OpenSlidesApp.agenda']) 'Agenda', 'CurrentListOfSpeakersItem', 'Config', - function ($scope, Agenda, CurrentListOfSpeakersItem, Config) { + 'Projector', + function ($scope, Agenda, CurrentListOfSpeakersItem, Config, Projector) { + $scope.overlay = $scope.element.overlay; // Watch for changes in the current list of speakers reference $scope.$watch(function () { return Config.lastModified('projector_currentListOfSpeakers_reference'); @@ -35,6 +34,12 @@ angular.module('OpenSlidesApp.agenda.projector', ['OpenSlidesApp.agenda']) $scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference'); $scope.updateCurrentListOfSpeakers(); }); + // Watch for changes in the referenced projector + $scope.$watch(function () { + return Projector.lastModified($scope.currentListOfSpeakersReference); + }, function () { + $scope.updateCurrentListOfSpeakers(); + }); // Watch for changes in the current item. $scope.$watch(function () { return Agenda.lastModified(); @@ -42,12 +47,7 @@ angular.module('OpenSlidesApp.agenda.projector', ['OpenSlidesApp.agenda']) $scope.updateCurrentListOfSpeakers(); }); $scope.updateCurrentListOfSpeakers = function () { - var itemPromise = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); - if (itemPromise) { - itemPromise.then(function(item) { - $scope.agendaItem = item; - }); - } + $scope.agendaItem = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); }; } ]) diff --git a/openslides/agenda/static/js/agenda/site.js b/openslides/agenda/static/js/agenda/site.js index 6754e062e..24cd5135f 100644 --- a/openslides/agenda/static/js/agenda/site.js +++ b/openslides/agenda/static/js/agenda/site.js @@ -55,7 +55,7 @@ angular.module('OpenSlidesApp.agenda.site', [ }) .state('agenda.current-list-of-speakers', { url: '/speakers', - controller: 'ListOfSpeakersViewCtrl', + controller: 'CurrentListOfSpeakersViewCtrl', data: { title: gettext('Current list of speakers'), }, @@ -588,7 +588,7 @@ angular.module('OpenSlidesApp.agenda.site', [ } ]) -.controller('ListOfSpeakersViewCtrl', [ +.controller('CurrentListOfSpeakersViewCtrl', [ '$scope', '$state', '$http', @@ -596,7 +596,9 @@ angular.module('OpenSlidesApp.agenda.site', [ 'ProjectionDefault', 'Config', 'CurrentListOfSpeakersItem', - function($scope, $state, $http, Projector, ProjectionDefault, Config, CurrentListOfSpeakersItem) { + 'CurrentListOfSpeakersSlide', + function($scope, $state, $http, Projector, ProjectionDefault, Config, CurrentListOfSpeakersItem, + CurrentListOfSpeakersSlide) { // Watch for changes in the current list of speakers reference $scope.$watch(function () { return Config.lastModified('projector_currentListOfSpeakers_reference'); @@ -608,52 +610,44 @@ angular.module('OpenSlidesApp.agenda.site', [ return Projector.lastModified(); }, function() { $scope.projectors = Projector.getAll(); - $scope.updateCurrentListOfSpeakers(); - var projectiondefault = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0]; - if (projectiondefault) { - $scope.defaultProjectorId = projectiondefault.projector_id; + if ($scope.projectors.length === 1) { + $scope.currentListOfSpeakersAsOverlay = true; } + $scope.updateCurrentListOfSpeakers(); + $scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id; }); $scope.updateCurrentListOfSpeakers = function () { - var itemPromise = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); - if (itemPromise) { - itemPromise.then(function(item) { - $scope.AgendaItem = item; - }); - } - }; - // Project current list of speakers - // same logic as in core/base.js - $scope.projectCurrentLoS = function (projectorId) { - var isCurrentLoSProjectedIds = $scope.isCurrentLoSProjected($scope.mainListTree); - _.forEach(isCurrentLoSProjectedIds, function (id) { - $http.post('/rest/core/projector/' + id + '/clear_elements/'); - }); - if (_.indexOf(isCurrentLoSProjectedIds, projectorId) == -1) { - $http.post('/rest/core/projector/' + projectorId + '/prune_elements/', - [{name: 'agenda/current-list-of-speakers'}]); - } - }; - // same logic as in core/base.js - $scope.isCurrentLoSProjected = function () { - // Returns the ids of all projectors with an element with the name - // 'agenda/current-list-of-speakers'. Elsewise returns an empty list. - var projectorIds = []; - $scope.projectors.forEach(function (projector) { - var key = _.findKey(projector.elements, function (element) { - return element.name == 'agenda/current-list-of-speakers'; - }); - if (typeof key === 'string') { - projectorIds.push(projector.id); - } - }); - return projectorIds; + $scope.agendaItem = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); }; // go to the list of speakers (management) of the currently // displayed projector slide $scope.goToListOfSpeakers = function() { - $state.go('agenda.item.detail', {id: $scope.AgendaItem.id}); + if ($scope.agendaItem) { + $state.go('agenda.item.detail', {id: $scope.agendaItem.id}); + } + }; + $scope.currentListOfSpeakers = CurrentListOfSpeakersSlide; + // Set the current overlay status + if ($scope.currentListOfSpeakers.isProjected().length) { + var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); + $scope.currentListOfSpeakersAsOverlay = isProjected[0].overlay; + } else { + $scope.currentListOfSpeakersAsOverlay = true; + } + $scope.currentListOfSpeakersItem = function () { + return CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); + }; + $scope.setOverlay = function (overlay) { + $scope.currentListOfSpeakersAsOverlay = overlay; + var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus(); + if (isProjected.length) { + _.forEach(isProjected, function (mapping) { + if (mapping.overlay != overlay) { // change the overlay if it is different + $scope.currentListOfSpeakers.project(mapping.projectorId, overlay); + } + }); + } }; } ]) diff --git a/openslides/agenda/static/templates/agenda/current-list-of-speakers.html b/openslides/agenda/static/templates/agenda/current-list-of-speakers.html index 04407a116..4fd6cb2b6 100644 --- a/openslides/agenda/static/templates/agenda/current-list-of-speakers.html +++ b/openslides/agenda/static/templates/agenda/current-list-of-speakers.html @@ -7,41 +7,59 @@ - -
{{ speaker.user.get_full_name() }} -
{{ speaker.user.get_full_name() }}
+ {{ speaker.user.get_full_name() }} +
+ + ++ {{ speaker.user.get_full_name() }} +
+ + ++ + {{ nextSpeakers.length - 3 }} +
++ {{ speaker.user.get_full_name() }} +
+ + ++ {{ speaker.user.get_full_name() }} +
+ + +- {{ speaker.user.get_full_name() }} -
- {{ speaker.user.get_full_name() }} -
- + {{ nextSpeakers.length - 3 }} -
- {{ speaker.user.get_full_name() }} - -
- {{ speaker.user.get_full_name() }} - -