Small changes for multiple projector feature.
This commit is contained in:
parent
e6b9b21d41
commit
8427ffd816
@ -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.
|
||||
|
@ -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/',
|
||||
|
@ -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')
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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(
|
||||
|
@ -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:
|
||||
// <projector-button model="motion" default-projector.id="defPrId" additional-id="2"
|
||||
// content="{{ 'project' | translate }}"></projector-button>
|
||||
//
|
||||
// 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: <projector-button model="motion" default-projector.id="defPrId"
|
||||
* additional-id="2" content="{{ 'project' | translate }}"></projector-button>
|
||||
* 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) {
|
||||
|
@ -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,18 +74,16 @@ 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
|
||||
$scope.recalculateIframe = function () {
|
||||
@ -164,7 +163,6 @@ 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;
|
||||
@ -176,7 +174,6 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
||||
$scope.blank = $scope.projector.blank;
|
||||
}
|
||||
}
|
||||
|
||||
if ($scope.broadcast > 0) {
|
||||
// get elements and blank from broadcast projector
|
||||
$scope.broadcastDeregister = $scope.$watch(function () {
|
||||
@ -192,7 +189,6 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
if ($scope.broadcastDeregister) {
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -56,7 +56,7 @@
|
||||
<li ng-repeat="projectiondefault in projectiondefaults | orderBy:'id'"
|
||||
ng-click="setProjectionDefault(projector, projectiondefault)">
|
||||
<i class="fa fa-check" ng-if="projectiondefault.projector_id === projector.id"></i>
|
||||
<translate>{{ projectiondefault.display_name }}</translate>
|
||||
{{ projectiondefault.display_name | translate }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
@ -20,7 +20,7 @@
|
||||
ng-class="{ 'projected': (model.isProjected(additionalId) == projector.id) }">
|
||||
<i class="fa fa-video-camera" ng-show="model.isProjected(additionalId) == projector.id"></i>
|
||||
{{ projector.name }}
|
||||
<span ng-if="defaultProjectorId == projector.id">(<translate>Standard</translate>)</span>
|
||||
<span ng-if="defaultProjectorId == projector.id">(<translate>Default</translate>)</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
@ -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:
|
||||
|
@ -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,9 +177,6 @@ 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);
|
||||
|
@ -196,7 +196,7 @@
|
||||
<td ng-show="!isDeleteMode"
|
||||
os-perms="core.can_manage_projector">
|
||||
<div class="btn-group" style="min-width:54px;" uib-dropdown
|
||||
ng-if="mediafile.mediafile.is_presentable"
|
||||
ng-if="mediafile.is_presentable"
|
||||
uib-tooltip="{{ 'Projektor' | translate }} {{ mediafile.isProjected() }}"
|
||||
tooltip-enable="mediafile.isProjected() > 0">
|
||||
<button type="button" class="btn btn-default btn-sm"
|
||||
|
@ -79,9 +79,7 @@ angular.module('OpenSlidesApp.motions.projector', ['OpenSlidesApp.motions'])
|
||||
}
|
||||
};
|
||||
|
||||
Motion.find(id).then(function (motion) {
|
||||
$scope.motion = motion;
|
||||
});
|
||||
Motion.bindOne(id, $scope, 'motion');
|
||||
User.bindAll({}, $scope, 'users');
|
||||
|
||||
}
|
||||
|
@ -1217,7 +1217,6 @@ angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions', 'OpenSlid
|
||||
$scope.$watch(function () {
|
||||
return Projector.lastModified();
|
||||
}, function () {
|
||||
console.log(ProjectionDefault.getAll());
|
||||
$scope.defaultProjectorId = ProjectionDefault.filter({name: 'motions'})[0].projector_id;
|
||||
});
|
||||
$scope.version = motion.active_version;
|
||||
|
@ -64,7 +64,7 @@ def ws_add_projector(message, projector_id):
|
||||
"""
|
||||
user = message.user
|
||||
# user is the django anonymous user. We have our own.
|
||||
if user.is_anonymous:
|
||||
if user.is_anonymous and config['general_systen_enable_anonymous']:
|
||||
user = AnonymousUser()
|
||||
|
||||
if not user.has_perm('core.can_see_projector'):
|
||||
@ -140,12 +140,9 @@ def send_data(message):
|
||||
|
||||
broadcast_id = config['projector_broadcast']
|
||||
if broadcast_id > 0:
|
||||
projectors = Projector.objects.all() # also the broadcasted projector should get data
|
||||
broadcast_projector = Projector.objects.get(pk=broadcast_id)
|
||||
|
||||
projectors = Projector.objects.all() # Also the broadcasted projector should get his data
|
||||
send_all = True
|
||||
# The data from the broadcasted projector
|
||||
broadcast_projector_data = get_projector_element_data(broadcast_projector)
|
||||
broadcast_projector_data = get_projector_element_data(Projector.objects.get(pk=broadcast_id))
|
||||
else:
|
||||
broadcast_projector_data = None
|
||||
|
||||
|
@ -14,7 +14,6 @@ from rest_framework.routers import DefaultRouter
|
||||
from rest_framework.serializers import ModelSerializer as _ModelSerializer
|
||||
from rest_framework.serializers import ( # noqa
|
||||
MANY_RELATION_KWARGS,
|
||||
BooleanField,
|
||||
CharField,
|
||||
DictField,
|
||||
Field,
|
||||
|
@ -26,22 +26,12 @@ class ProjectorAPI(TestCase):
|
||||
|
||||
response = self.client.get(reverse('projector-detail', args=['1']))
|
||||
content = json.loads(response.content.decode())
|
||||
del content['projectiondefaults']
|
||||
|
||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||
self.assertEqual(content, {
|
||||
'id': 1,
|
||||
'elements': {
|
||||
self.assertEqual(content['elements'], {
|
||||
'aae4a07b26534cfb9af4232f361dce73':
|
||||
{'id': topic.id,
|
||||
'uuid': 'aae4a07b26534cfb9af4232f361dce73',
|
||||
'name': 'topics/topic'}},
|
||||
'scale': 0,
|
||||
'scroll': 0,
|
||||
'name': 'Defaultprojector',
|
||||
'blank': False,
|
||||
'width': 1024,
|
||||
'height': 768})
|
||||
'name': 'topics/topic'}})
|
||||
|
||||
def test_invalid_slide_on_default_projector(self):
|
||||
self.client.login(username='admin', password='admin')
|
||||
|
Loading…
Reference in New Issue
Block a user