From 8427ffd816543aa0dd1365194760886c540142cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Norman=20J=C3=A4ckel?= Date: Thu, 29 Sep 2016 15:32:58 +0200 Subject: [PATCH] Small changes for multiple projector feature. --- CHANGELOG | 2 +- openslides/agenda/static/js/agenda/site.js | 13 ++-- .../core/migrations/0006_multiprojector.py | 2 +- openslides/core/models.py | 13 ++-- openslides/core/serializers.py | 4 +- openslides/core/signals.py | 18 ++--- openslides/core/static/js/core/base.js | 46 +++++++------ openslides/core/static/js/core/projector.js | 68 +++++++++---------- openslides/core/static/js/core/site.js | 8 +-- .../templates/core/manage-projectors.html | 2 +- .../static/templates/projector-button.html | 2 +- openslides/core/views.py | 23 +++---- .../mediafiles/static/js/mediafiles/site.js | 7 +- .../templates/mediafiles/mediafile-list.html | 2 +- .../motions/static/js/motions/projector.js | 4 +- openslides/motions/static/js/motions/site.js | 1 - openslides/utils/autoupdate.py | 9 +-- openslides/utils/rest_api.py | 1 - tests/integration/core/test_views.py | 20 ++---- 19 files changed, 112 insertions(+), 133 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 8fe5f6e73..17146e2e6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -20,7 +20,7 @@ Core: - Added support for big assemblies with lots of users. - Added HTML support for messages on the projector. - Moved custom slides to own app "topics". Renamed it to "Topic". -- Added support for multiple projectors +- Added support for multiple projectors. Motions: - Added origin field. diff --git a/openslides/agenda/static/js/agenda/site.js b/openslides/agenda/static/js/agenda/site.js index 1b34573a3..7ca0ee866 100644 --- a/openslides/agenda/static/js/agenda/site.js +++ b/openslides/agenda/static/js/agenda/site.js @@ -251,7 +251,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) var isAgendaProjectedId = $scope.isAgendaProjected($scope.mainListTree); if (isAgendaProjectedId > 0) { // Deactivate - $http.post('/rest/core/projector/' + isAgendaProjectedId + '/prune_elements/', []); + $http.post('/rest/core/projector/' + isAgendaProjectedId + '/clear_elements/'); } if (isAgendaProjectedId != projectorId) { $http.post('/rest/core/projector/' + projectorId + '/prune_elements/', @@ -332,7 +332,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) if (projectiondefaultItem) { $scope.defaultProjectorItemId = projectiondefaultItem.projector_id; } - var projectiondefaultListOfSpeakers = ProjectionDefault.filter({name: 'list_of_speakers'})[0]; + var projectiondefaultListOfSpeakers = ProjectionDefault.filter({name: 'agenda_list_of_speakers'})[0]; if (projectiondefaultListOfSpeakers) { $scope.defaultProjectorListOfSpeakersId = projectiondefaultListOfSpeakers.projector_id; } @@ -515,16 +515,11 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) }, function() { $scope.projectors = Projector.getAll(); $scope.updateCurrentListOfSpeakers(); - }); - $scope.$watch(function () { - return Projector.lastModified(); - }, function () { - var projectiondefault = ProjectionDefault.filter({name: 'current_list_of_speakers'})[0]; + var projectiondefault = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0]; if (projectiondefault) { $scope.defaultProjectorId = projectiondefault.projector_id; } }); - $scope.updateCurrentListOfSpeakers = function () { var itemPromise = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference); if (itemPromise) { @@ -539,7 +534,7 @@ angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda']) var isCurrentLoSProjectedId = $scope.isCurrentLoSProjected($scope.mainListTree); if (isCurrentLoSProjectedId > 0) { // Deactivate - $http.post('/rest/core/projector/' + isCurrentLoSProjectedId + '/prune_elements/', []); + $http.post('/rest/core/projector/' + isCurrentLoSProjectedId + '/clear_elements/'); } if (isCurrentLoSProjectedId != projectorId) { $http.post('/rest/core/projector/' + projectorId + '/prune_elements/', diff --git a/openslides/core/migrations/0006_multiprojector.py b/openslides/core/migrations/0006_multiprojector.py index 592695384..638ceb839 100644 --- a/openslides/core/migrations/0006_multiprojector.py +++ b/openslides/core/migrations/0006_multiprojector.py @@ -13,7 +13,7 @@ def name_default_projector(apps, schema_editor): Set the name of the default projector to 'Defaultprojector' """ Projector = apps.get_model('core', 'Projector') - Projector.objects.filter(pk=1).update(name='Defaultprojector') + Projector.objects.filter(pk=1).update(name='Default projector') class Migration(migrations.Migration): diff --git a/openslides/core/models.py b/openslides/core/models.py index 77bba0286..953496b42 100644 --- a/openslides/core/models.py +++ b/openslides/core/models.py @@ -179,18 +179,19 @@ class Projector(RESTModelMixin, models.Model): class ProjectionDefault(RESTModelMixin, models.Model): """ - Model for the ProjectionDefaults like Motion, Agenda, List of speakers,... - The name is the technical name like 'topics', 'motions'. For apps the name should - be the app name to get keep the ProjectionDefault for apps generic. But it is - possible to give some special name like 'list_of_speakers'. - The display_name is the shown name on the front end for the user. + Model for the projection defaults like motions, agenda, list of + speakers and thelike. The name is the technical name like 'topics' or + 'motions'. For apps the name should be the app name to get keep the + ProjectionDefault for apps generic. But it is possible to give some + special name like 'list_of_speakers'. The display_name is the shown + name on the front end for the user. """ name = models.CharField(max_length=256) display_name = models.CharField(max_length=256) projector = models.ForeignKey( - 'Projector', + Projector, on_delete=models.CASCADE, related_name='projectiondefaults') diff --git a/openslides/core/serializers.py b/openslides/core/serializers.py index aeb0482b7..a3c45dd89 100644 --- a/openslides/core/serializers.py +++ b/openslides/core/serializers.py @@ -40,8 +40,8 @@ class ProjectorSerializer(ModelSerializer): class Meta: model = Projector - fields = ('id', 'config', 'elements', 'scale', 'scroll', 'name', 'blank', 'width', 'height', 'projectiondefaults') - read_only_fields = ('scale', 'scroll', 'blank', 'width', 'height', 'projectiondefaults') + fields = ('id', 'config', 'elements', 'scale', 'scroll', 'name', 'blank', 'width', 'height', 'projectiondefaults', ) + read_only_fields = ('scale', 'scroll', 'blank', 'width', 'height', ) class TagSerializer(ModelSerializer): diff --git a/openslides/core/signals.py b/openslides/core/signals.py index 7ec363245..92609c323 100644 --- a/openslides/core/signals.py +++ b/openslides/core/signals.py @@ -27,18 +27,20 @@ def delete_django_app_permissions(sender, **kwargs): def create_builtin_projection_defaults(**kwargs): """ Creates the builtin defaults: - - agenda_all_items, agenda_item + - agenda_all_items, agenda_list_of_speakers, agenda_current_list_of_speakers + - topics - assignments - mediafiles - motion - users - - list_of_speakers - - current_list_of_speakers - """ - # Check whether ProjectionDefaults exists + These strings have to be used in the controllers where you want to + define a projector button. Use the string to get the id of the + responsible projector and pass this id to the projector button directive. + """ + # Check whether ProjectionDefault objects exist. if ProjectionDefault.objects.all().exists(): - # Do completely nothing if the defaults are already in the database. + # Do completely nothing if some defaults are already in the database. return default_projector = Projector.objects.get(pk=1) @@ -52,11 +54,11 @@ def create_builtin_projection_defaults(**kwargs): display_name='Topics', projector=default_projector) ProjectionDefault.objects.create( - name='list_of_speakers', + name='agenda_list_of_speakers', display_name='List of speakers', projector=default_projector) ProjectionDefault.objects.create( - name='current_list_of_speakers', + name='agenda_current_list_of_speakers', display_name='Current list of speakers', projector=default_projector) ProjectionDefault.objects.create( diff --git a/openslides/core/static/js/core/base.js b/openslides/core/static/js/core/base.js index a9c09c88c..0a66870a1 100644 --- a/openslides/core/static/js/core/base.js +++ b/openslides/core/static/js/core/base.js @@ -192,10 +192,12 @@ angular.module('OpenSlidesApp.core', [ autoupdate.onMessage(function(json) { // TODO: when MODEL.find() is called after this // a new request is fired. This could be a bug in DS - // TODO: If you don't have the permission to see a projector, the - // variable json is a string with an error message. Therefor - // the next line fails. - var dataList = JSON.parse(json); + var dataList = []; + try { + dataList = JSON.parse(json); + } catch(err) { + console.error(json); + } _.forEach(dataList, function(data) { console.log("Received object: " + data.collection + ", " + data.id); var instance = DS.get(data.collection, data.id); @@ -313,15 +315,17 @@ angular.module('OpenSlidesApp.core', [ } ]) -// This places a Projectorbutton in the document. Example: -// -// -// This button references to model (in this case 'motion'). Also a defaultProjectionId has to -// be given. In the Exable its a scope variable. The next two parameters are additional: -// - additional-id: Then the model.project and model.isProjected will be called whith this -// argument (ex.: model.project(2)) -// - content: A not trusted text placed behind the projector symbol. +/* + * This places a projector button in the document. + * + * Example: + * This button references to model (in this example 'motion'). Also a defaultProjectionId + * has to be given. In the example it's a scope variable. The next two parameters are additional: + * - additional-id: Then the model.project and model.isProjected will be called with + * this argument (e. g.: model.project(2)) + * - content: A text placed behind the projector symbol. + */ .directive('projectorButton', [ 'Projector', function (Projector) { @@ -370,7 +374,7 @@ angular.module('OpenSlidesApp.core', [ // if this object is already projected on projectorId, delete this element from this projector var isProjectedId = this.isProjected(); if (isProjectedId > 0) { - $http.post('/rest/core/projector/' + isProjectedId + '/prune_elements/'); + $http.post('/rest/core/projector/' + isProjectedId + '/clear_elements/'); } // if it was the same projector before, just delete it but not show again if (isProjectedId != projectorId) { @@ -483,7 +487,7 @@ angular.module('OpenSlidesApp.core', [ }, getStateForCurrentSlide: function () { var return_dict; - $.each(this.elements, function(key, value) { + angular.forEach(this.elements, function(key, value) { if (value.name == 'agenda/list-of-speakers') { return_dict = { 'state': 'agenda.item.detail', @@ -592,11 +596,13 @@ angular.module('OpenSlidesApp.core', [ } ]) -// This filter filters all items in array. If the filterArray is empty, the array is passed. -// The filterArray contains numbers of the multiselect: [1, 3, 4]. -// Then, all items in array are passed, if the item_id (get with id_function) matches one of the -// ids in filterArray. id_function could also return a list of ids. Example: -// Item 1 has two tags with ids [1, 4]. filterArray = [3, 4] --> match +/* + * This filter filters all items in an array. If the filterArray is empty, the + * array is passed. The filterArray contains numbers of the multiselect, e. g. [1, 3, 4]. + * Then, all items in the array are passed, if the item_id (get with id_function) matches + * one of the ids in filterArray. id_function could also return a list of ids. Example: + * Item 1 has two tags with ids [1, 4]. filterArray == [3, 4] --> match + */ .filter('SelectMultipleFilter', [ function () { return function (array, filterArray, idFunction) { diff --git a/openslides/core/static/js/core/projector.js b/openslides/core/static/js/core/projector.js index c0acd41d2..dbaaed9a3 100644 --- a/openslides/core/static/js/core/projector.js +++ b/openslides/core/static/js/core/projector.js @@ -60,10 +60,11 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) .controller('ProjectorContainerCtrl', [ '$scope', '$location', + 'gettext', 'loadGlobalData', 'Projector', 'ProjectorID', - function($scope, $location, loadGlobalData, Projector, ProjectorID) { + function($scope, $location, gettext, loadGlobalData, Projector, ProjectorID) { loadGlobalData(); $scope.projector_id = ProjectorID(); @@ -73,17 +74,15 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) $scope.$watch(function () { return Projector.lastModified($scope.projector_id); }, function () { - Projector.find($scope.projector_id).then(function (projector) { + var projector = Projector.get($scope.projector_id) + if (projector) { + $scope.error = ''; $scope.projectorWidth = projector.width; $scope.projectorHeight = projector.height; $scope.recalculateIframe(); - }, function (error) { - if (error.status == 404) { - $scope.error = 'Projector not found.'; - } else if (error.status == 403) { - $scope.error = 'You have to login to see the projector.'; - } - }); + } else { + $scope.error = gettext('Can not open the projector.'); + } }); // recalculate the actual Iframesize and scale @@ -164,34 +163,31 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core']) $scope.$watch(function () { return Config.lastModified('projector_broadcast'); }, function () { - Config.findAll().then(function () { - var bc = Config.get('projector_broadcast').value; - if ($scope.broadcast != bc) { - $scope.broadcast = bc; - if ($scope.broadcastDeregister) { - // revert to original $scope.projector - $scope.broadcastDeregister(); - $scope.broadcastDeregister = null; - setElements($scope.projector); - $scope.blank = $scope.projector.blank; + var bc = Config.get('projector_broadcast').value; + if ($scope.broadcast != bc) { + $scope.broadcast = bc; + if ($scope.broadcastDeregister) { + // revert to original $scope.projector + $scope.broadcastDeregister(); + $scope.broadcastDeregister = null; + setElements($scope.projector); + $scope.blank = $scope.projector.blank; + } + } + if ($scope.broadcast > 0) { + // get elements and blank from broadcast projector + $scope.broadcastDeregister = $scope.$watch(function () { + return Projector.lastModified($scope.broadcast); + }, function () { + if ($scope.broadcast > 0) { + // var broadcast_projector = Projector.get($scope.broadcast); + Projector.find($scope.broadcast).then(function (broadcast_projector) { + setElements(broadcast_projector); + $scope.blank = broadcast_projector.blank; + }); } - } - - if ($scope.broadcast > 0) { - // get elements and blank from broadcast projector - $scope.broadcastDeregister = $scope.$watch(function () { - return Projector.lastModified($scope.broadcast); - }, function () { - if ($scope.broadcast > 0) { - // var broadcast_projector = Projector.get($scope.broadcast); - Projector.find($scope.broadcast).then(function (broadcast_projector) { - setElements(broadcast_projector); - $scope.blank = broadcast_projector.blank; - }); - } - }); - } - }); + }); + } }); $scope.$on('$destroy', function() { diff --git a/openslides/core/static/js/core/site.js b/openslides/core/static/js/core/site.js index 825cfdaba..beb1694cf 100644 --- a/openslides/core/static/js/core/site.js +++ b/openslides/core/static/js/core/site.js @@ -1205,7 +1205,7 @@ angular.module('OpenSlidesApp.core.site', [ }); }; - // Get all message and countdown data from the defaultprojector (id=1) + // Get all message and countdown data from the defaultprojector (id=1) var rebuildAllElements = function () { $scope.countdowns = []; $scope.messages = []; @@ -1246,7 +1246,7 @@ angular.module('OpenSlidesApp.core.site', [ // stop ALL interval timer cancelIntervalTimers(); - + rebuildAllElements(); }); $scope.$on('$destroy', function() { @@ -1424,7 +1424,7 @@ angular.module('OpenSlidesApp.core.site', [ $http.post('/rest/core/projector/' + projector.id + '/update_elements/', data); } }); - }); + }); }; $scope.isProjected = function (element) { var projectorIds = []; @@ -1616,7 +1616,7 @@ angular.module('OpenSlidesApp.core.site', [ }; $scope.removeIdentifierMessages = function () { Projector.getAll().forEach(function (projector) { - $.each(projector.elements, function (uuid, value) { + angular.forEach(projector.elements, function (uuid, value) { if (value.name == 'core/message' && value.type == 'identify') { $http.post('/rest/core/projector/' + projector.id + '/deactivate_elements/', [uuid]); } diff --git a/openslides/core/static/templates/core/manage-projectors.html b/openslides/core/static/templates/core/manage-projectors.html index 508611299..fe115510e 100644 --- a/openslides/core/static/templates/core/manage-projectors.html +++ b/openslides/core/static/templates/core/manage-projectors.html @@ -56,7 +56,7 @@
  • - {{ projectiondefault.display_name }} + {{ projectiondefault.display_name | translate }}
  • diff --git a/openslides/core/static/templates/projector-button.html b/openslides/core/static/templates/projector-button.html index e90dec1c3..49415430e 100644 --- a/openslides/core/static/templates/projector-button.html +++ b/openslides/core/static/templates/projector-button.html @@ -20,7 +20,7 @@ ng-class="{ 'projected': (model.isProjected(additionalId) == projector.id) }"> {{ projector.name }} - (Standard) + (Default) diff --git a/openslides/core/views.py b/openslides/core/views.py index 9228b035f..e015f90dd 100644 --- a/openslides/core/views.py +++ b/openslides/core/views.py @@ -18,7 +18,6 @@ from django.utils.timezone import now from openslides import __version__ as version from openslides.utils import views as utils_views -from openslides.utils.autoupdate import inform_changed_data from openslides.utils.plugins import ( get_plugin_description, get_plugin_verbose_name, @@ -190,23 +189,25 @@ class ProjectorViewSet(ModelViewSet): result = self.get_access_permissions().check_permissions(self.request.user) elif self.action == 'metadata': result = self.request.user.has_perm('core.can_see_projector') - elif self.action in ('activate_elements', 'prune_elements', 'update_elements', - 'deactivate_elements', 'clear_elements', 'control_view', - 'set_resolution', 'set_scroll', 'control_blank', 'destroy', - 'create', 'update', 'broadcast', 'set_projectiondefault'): + elif self.action in ( + 'create', 'update', 'partial_update', 'destroy', + 'activate_elements', 'prune_elements', 'update_elements', 'deactivate_elements', 'clear_elements', + 'control_view', 'set_resolution', 'set_scroll', 'control_blank', 'broadcast', + 'set_projectiondefault', + ): result = (self.request.user.has_perm('core.can_see_projector') and self.request.user.has_perm('core.can_manage_projector')) else: result = False return result - # Assign all projectionDefaults from this projector to the default projector (pk=1) + # Assign all ProjectionDefault objects from this projector to the default projector (pk=1). def destroy(self, *args, **kwargs): projector_instance = self.get_object() - for a in ProjectionDefault.objects.all(): - if a.projector.id == projector_instance.id: - a.projector = Projector.objects.get(pk=1) - a.save() + for projection_default in ProjectionDefault.objects.all(): + if projection_default.projector.id == projector_instance.id: + projection_default.projector_id = 1 + projection_default.save() return super(ProjectorViewSet, self).destroy(*args, **kwargs) @detail_route(methods=['post']) @@ -486,8 +487,6 @@ class ProjectorViewSet(ModelViewSet): """ if config['projector_broadcast'] == 0: config['projector_broadcast'] = pk - projector_instance = self.get_object() - inform_changed_data(projector_instance) message = "Setting projector {id} as broadcast projector was successful.".format( id=pk) else: diff --git a/openslides/mediafiles/static/js/mediafiles/site.js b/openslides/mediafiles/static/js/mediafiles/site.js index 093fcfbe3..b2d90fad2 100644 --- a/openslides/mediafiles/static/js/mediafiles/site.js +++ b/openslides/mediafiles/static/js/mediafiles/site.js @@ -158,7 +158,7 @@ angular.module('OpenSlidesApp.mediafiles.site', ['ngFileUpload', 'OpenSlidesApp. $scope.showMediafile = function (projectorId, mediafile) { var isProjectedId = mediafile.isProjected(); if (isProjectedId > 0) { - $http.post('/rest/core/projector/' + isProjectedId + '/prune_elements/', []); + $http.post('/rest/core/projector/' + isProjectedId + '/clear_elements/'); } if (isProjectedId != projectorId) { var postUrl = '/rest/core/projector/' + projectorId + '/prune_elements/'; @@ -177,15 +177,12 @@ angular.module('OpenSlidesApp.mediafiles.site', ['ngFileUpload', 'OpenSlidesApp. } }; - // To avoid some kind of 60,000000000001% in template - $scope.round = function (val) {return Math.round(val);}; - var sendMediafileCommand = function (mediafile, data) { var updateData = _.extend({}, mediafile); _.extend(updateData, data); var postData = {}; postData[mediafile.uuid] = updateData; - + // Find Projector where the mediafile is projected $scope.projectors.forEach(function (projector) { if (_.find(projector.elements, function (e) {return e.uuid == mediafile.uuid;})) { diff --git a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html b/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html index 683e5ddb8..b3850dff5 100644 --- a/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html +++ b/openslides/mediafiles/static/templates/mediafiles/mediafile-list.html @@ -196,7 +196,7 @@