Reorganize the current list of speakers. Fixes #2669 and find()-calls.

This commit is contained in:
FinnStutzenstein 2017-02-03 11:54:36 +01:00
parent 45c3da22f9
commit 3c36cd5757
14 changed files with 330 additions and 237 deletions

View File

@ -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.

View File

@ -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'])
# 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'

View File

@ -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;
}
var isProjected = this.isProjectedWithOverlayStatus();
_.forEach(isProjected, function (mapping) {
$http.post('/rest/core/projector/' + mapping.projectorId + '/deactivate_elements/',
[mapping.uuid]
);
});
$http.post('/rest/core/projector/' + isProjectedId + '/deactivate_elements/',
[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);
}
// Activate, if the projector_id is a new projector.
if (isProjectedId != projectorId) {
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: 'agenda/current-list-of-speakers-overlay',stable: true}]);
[{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;
},
};
}
])

View File

@ -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);
};
}
])

View File

@ -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);
}
});
}
};
}
])

View File

@ -7,41 +7,59 @@
</a>
<!-- manage list of speakers -->
<button os-perms="agenda.can_manage"
ng-disabled="!currentListOfSpeakersItem()"
ng-click="goToListOfSpeakers()"
class="btn btn-sm btn-default">
<i class="fa fa-microphone"></i>
<translate>Manage list</translate>
</button>
<!-- project -->
<div os-perms="core.can_manage_projector" class="btn-group" uib-dropdown
uib-tooltip="{{ 'Projector' | translate }} {{ isCurrentLoSProjected()[0] || '' }}"
tooltip-enable="isCurrentLoSProjected().length">
<!-- Current list of speakers projector button -->
<div class="btn-group button" uib-dropdown
uib-tooltip="{{ 'Project the current list of speakers' | translate }}"
os-perms="core.can_manage_projector">
<button type="button" class="btn btn-default btn-sm"
title="{{ 'Project current list of speakers' | translate }}"
ng-click="projectCurrentLoS(defaultProjectorId)"
ng-class="{ 'btn-primary': isCurrentLoSProjected().length && inArray(isCurrentLoSProjected(), defaultProjectorId) }">
ng-click="currentListOfSpeakers.project(listOfSpeakersDefaultProjectorId, currentListOfSpeakersAsOverlay)"
ng-class="{ 'btn-primary': currentListOfSpeakers.isProjected().length && inArray(currentListOfSpeakers.isProjected(), listOfSpeakersDefaultProjectorId)}">
<i class="fa fa-video-camera"></i>
<translate>Current list of speakers</translate>
</button>
<button type="button" class="btn btn-default btn-sm" uib-dropdown-toggle
ng-class="{ 'btn-primary': isCurrentLoSProjected().length && !inArray(isCurrentLoSProjected(), defaultProjectorId) }">
<button type="button" class="btn btn-default btn-sm"
ng-if="projectors.length > 1"
uib-dropdown-toggle
ng-class="{ 'btn-primary': currentListOfSpeakers.isProjected().length && !inArray(currentListOfSpeakers.isProjected(), listOfSpeakersDefaultProjectorId)}">
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="split-button">
<ul class="dropdown-menu" role="menu" aria-labelledby="split-button" ng-if="projectors.length > 1">
<li role="menuitem">
<a href="" ng-click="setOverlay(false); $event.stopPropagation();">
<i class="fa" ng-class="currentListOfSpeakersAsOverlay ? 'fa-circle-o' : 'fa-check-circle-o'"></i>
<translate>Project as slide</translate>
</a>
</li>
<li role="menuitem">
<a href="" ng-click="setOverlay(true); $event.stopPropagation();">
<i class="fa" ng-class="currentListOfSpeakersAsOverlay ? 'fa-check-circle-o' : 'fa-circle-o'"></i>
<translate>Project as overlay</translate>
</a>
</li>
<li class="divider"></li>
<li role="menuitem" ng-repeat="projector in projectors | orderBy:'id'">
<a href="" ng-click="projectCurrentLoS(projector.id)"
ng-class="{ 'projected': inArray(isCurrentLoSProjected(), projector.id) }">
<i class="fa fa-video-camera" ng-show="inArray(isCurrentLoSProjected(), projector.id) "></i>
<a href="" ng-click="currentListOfSpeakers.project(projector.id, currentListOfSpeakersAsOverlay)"
ng-class="{ 'projected': inArray(currentListOfSpeakers.isProjected(), projector.id) }">
<i class="fa fa-video-camera" ng-show="inArray(currentListOfSpeakers.isProjected(), projector.id)"></i>
{{ projector.name | translate }}
<span ng-if="projector.id == defaultProjectorId">(<translate>Default</translate>)</span>
<span ng-if="projector.id == listOfSpeakersDefaultProjectorId">(<translate>Default</translate>)</span>
</a>
</li>
</ul>
</div>
</div>
<h1 translate>Current list of speakers</h1>
<h2> {{ AgendaItem.getTitle() }}
<span class="slimlabel label label-danger ng-scope" style="" ng-if="AgendaItem.speaker_list_closed" translate>
<h2> {{ agendaItem.getTitle() }}
<span class="slimlabel label label-danger ng-scope" style="" ng-if="agendaItem.speaker_list_closed" translate>
Closed
</span>
</h2>
@ -50,16 +68,16 @@
<div class="details">
<!-- Last speakers -->
<p ng-repeat="speaker in lastSpeakers = (AgendaItem.speakers | filter: {end_time: '!!', begin_time: '!!'}) |
<p ng-repeat="speaker in lastSpeakers = (agendaItem.speakers | filter: {end_time: '!!', begin_time: '!!'}) |
limitTo: config('agenda_show_last_speakers') : (lastSpeakers.length - config('agenda_show_last_speakers'))" class="lastSpeakers">
{{ speaker.user.get_full_name() }}
<!-- Current speaker -->
<p ng-repeat="speaker in currentspeakers = (AgendaItem.speakers| filter: {end_time: null, begin_time: '!!'})"
<p ng-repeat="speaker in currentspeakers = (agendaItem.speakers| filter: {end_time: null, begin_time: '!!'})"
class="currentSpeaker">
<i class="fa fa-microphone fa-lg"></i> {{ speaker.user.get_full_name() }}
<!-- Next speakers -->
<ol class="nextSpeakers">
<li ng-repeat="speaker in AgendaItem.speakers | filter: {begin_time: null} | orderBy:'weight'">
<li ng-repeat="speaker in agendaItem.speakers | filter: {begin_time: null} | orderBy:'weight'">
{{ speaker.user.get_full_name() }}
</ol>
</div>

View File

@ -0,0 +1,30 @@
<div id="speakerbox">
<h3 translate>List of speakers</h3>
<!-- Last speakers -->
<p ng-repeat="speaker in lastSpeakers = (agendaItem.speakers
| filter: {end_time: '!!', begin_time: '!!'})
| limitTo: config('agenda_show_last_speakers') : (lastSpeakers.length - config('agenda_show_last_speakers'))"
class="lastSpeakers">
{{ speaker.user.get_full_name() }}
</p>
<!-- Current speaker -->
<p ng-repeat="speaker in agendaItem.speakers | filter: {end_time: null, begin_time: '!!'} "
class="currentSpeaker">
<i class="fa fa-microphone fa-lg"></i> {{ speaker.user.get_full_name() }}
</p>
<!-- Next speakers -->
<ol class="nextSpeakers">
<li ng-repeat="speaker in nextSpeakers = (agendaItem.speakers
| filter: {begin_time: null})
| orderBy:'weight'
| limitTo: 3">
{{ speaker.user.get_full_name() }}
</li>
</ol>
<p ng-if="nextSpeakers.length > 3" class="lastSpeakers">
<i>+ {{ nextSpeakers.length - 3 }}</i>
</p>
</div>

View File

@ -0,0 +1,33 @@
<div class="content scrollcontent">
<div class="title">
<h1 translate>Current list of speakers</h1>
<h2> {{ agendaItem.getTitle() }}
<span class="slimlabel label label-danger ng-scope"
ng-if="agendaItem.speaker_list_closed" translate>Closed</span>
</h2>
</div>
<!-- Last speakers -->
<p ng-repeat="speaker in lastSpeakers = (agendaItem.speakers
| filter: {end_time: '!!', begin_time: '!!'})
| limitTo: config('agenda_show_last_speakers') : (lastSpeakers.length - config('agenda_show_last_speakers'))"
class="lastSpeakers">
{{ speaker.user.get_full_name() }}
</p>
<!-- Current speaker -->
<p ng-repeat="speaker in currentspeakers = (agendaItem.speakers
| filter: {end_time: null, begin_time: '!!'})"
class="currentSpeaker">
<i class="fa fa-microphone fa-lg"></i> {{ speaker.user.get_full_name() }}
</p>
<!-- Next speakers -->
<ol class="nextSpeakers">
<li ng-repeat="speaker in agendaItem.speakers
| filter: {begin_time: null}
| orderBy:'weight'">
{{ speaker.user.get_full_name() }}
</li>
</ol>
</div>

View File

@ -1,19 +0,0 @@
<div ng-controller="SlideCurrentListOfSpeakersCtrl">
<div ng-if="agendaItem.speakers && agendaItem.speakers.length">
<div id="speakerbox">
<h3 translate>List of speakers</h3>
<p ng-repeat="speaker in lastSpeakers = (agendaItem.speakers | filter: {end_time: '!!', begin_time: '!!'}) |
limitTo: config('agenda_show_last_speakers') : (lastSpeakers.length - config('agenda_show_last_speakers'))"
class="lastSpeakers">
{{ speaker.user.get_full_name() }}
<p ng-repeat="speaker in agendaItem.speakers | filter: {end_time: null, begin_time: '!!'} "
class="currentSpeaker">
<i class="fa fa-microphone fa-lg"></i> {{ speaker.user.get_full_name() }}
<ol class="nextSpeakers">
<li ng-repeat="speaker in nextSpeakers = (agendaItem.speakers | filter: {begin_time: null}) | orderBy:'weight' | limitTo: 3">
{{ speaker.user.get_full_name() }}
</ol>
<p ng-if="nextSpeakers.length > 3" class="lastSpeakers">
<i>+ {{ nextSpeakers.length - 3 }}</i>
</div>
</div>

View File

@ -1,23 +1,8 @@
<div ng-controller="SlideCurrentListOfSpeakersCtrl" class="content scrollcontent">
<div class="title">
<h1 translate>Current list of speakers</h1>
<h2> {{ agendaItem.getTitle() }}
<span class="slimlabel label label-danger ng-scope" style=""
ng-if="agendaItem.speaker_list_closed" translate>Closed</span>
</h2>
</div>
<div ng-controller="SlideCurrentListOfSpeakersCtrl">
<ng-include src="'static/templates/agenda/partial-slide-current-list-of-speakers.html'"
ng-if="!overlay"></ng-include>
<ng-include src="'static/templates/agenda/partial-slide-current-list-of-speakers-overlay.html'"
ng-if="overlay"></ng-include>
<!-- Last speakers -->
<p ng-repeat="speaker in lastSpeakers = (agendaItem.speakers | filter: {end_time: '!!', begin_time: '!!'}) |
limitTo: config('agenda_show_last_speakers') : (lastSpeakers.length - config('agenda_show_last_speakers'))" class="lastSpeakers">
{{ speaker.user.get_full_name() }}
<!-- Current speaker -->
<p ng-repeat="speaker in currentspeakers = (agendaItem.speakers| filter: {end_time: null, begin_time: '!!'})"
class="currentSpeaker">
<i class="fa fa-microphone fa-lg"></i> {{ speaker.user.get_full_name() }}
<!-- Next speakers -->
<ol class="nextSpeakers">
<li ng-repeat="speaker in agendaItem.speakers | filter: {begin_time: null} | orderBy:'weight'">
{{ speaker.user.get_full_name() }}
</ol>
</div>

View File

@ -230,9 +230,10 @@ h3 {
position: absolute;
background: #d3d3d3;
border-radius: 7px;
border: 1px solid;
padding: 3px 7px 10px 19px;
border: 1px solid #999;
padding: 0px 7px 10px 19px;
z-index: 99;
box-shadow: 3px 3px 10px 1px rgba(0,0,0,0.5);
}
ul, ol {
margin: 0 0 10px 2em;

View File

@ -712,7 +712,8 @@ angular.module('OpenSlidesApp.core', [
value.name != 'agenda/item-list' &&
value.name != 'core/clock' &&
value.name != 'core/countdown' &&
value.name != 'core/message' ) {
value.name != 'core/message' &&
value.name != 'agenda/current-list-of-speakers' ) {
return_dict = {
'state': value.name.replace('/', '.')+'.detail.update',
'param': {id: value.id}

View File

@ -1036,7 +1036,7 @@ angular.module('OpenSlidesApp.core.site', [
'Config',
'Projector',
'CurrentListOfSpeakersItem',
'ListOfSpeakersOverlay',
'CurrentListOfSpeakersSlide',
'ProjectionDefault',
'ProjectorMessage',
'Countdown',
@ -1044,7 +1044,7 @@ angular.module('OpenSlidesApp.core.site', [
'ngDialog',
'ProjectorMessageForm',
function($scope, $http, $interval, $state, $q, $filter, Config, Projector, CurrentListOfSpeakersItem,
ListOfSpeakersOverlay, ProjectionDefault, ProjectorMessage, Countdown, gettextCatalog,
CurrentListOfSpeakersSlide, ProjectionDefault, ProjectorMessage, Countdown, gettextCatalog,
ngDialog, ProjectorMessageForm) {
ProjectorMessage.bindAll({}, $scope, 'messages');
@ -1078,7 +1078,6 @@ angular.module('OpenSlidesApp.core.site', [
cancelIntervalTimers();
});
$scope.listofspeakers = ListOfSpeakersOverlay;
$scope.$watch(function () {
return Projector.lastModified();
}, function () {
@ -1086,20 +1085,14 @@ angular.module('OpenSlidesApp.core.site', [
if (!$scope.active_projector) {
$scope.changeProjector($filter('orderBy')($scope.projectors, 'id')[0]);
}
if ($scope.projectors.length === 1) {
$scope.currentListOfSpeakersAsOverlay = true;
}
$scope.messageDefaultProjectorId = ProjectionDefault.filter({name: 'messages'})[0].projector_id;
$scope.countdownDefaultProjectorId = ProjectionDefault.filter({name: 'countdowns'})[0].projector_id;
$scope.getDefaultOverlayProjector();
$scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id;
});
// gets the default projector where the current list of speakers overlay will be displayed
$scope.getDefaultOverlayProjector = function () {
var projectiondefault = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0];
if (projectiondefault) {
$scope.listofSpeakersDefaultProjectorId = projectiondefault.projector_id;
} else {
$scope.listOfSpeakersDefaultProjectorId = 1;
}
};
// watch for changes in projector_broadcast and currentListOfSpeakersReference
var last_broadcast;
$scope.$watch(function () {
@ -1172,11 +1165,35 @@ angular.module('OpenSlidesApp.core.site', [
ProjectorMessage.destroy(message.id);
};
/* Current list of speakers */
$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;
}
// go to the list of speakers(management) of the currently displayed list of speakers reference slide
$scope.goToListOfSpeakers = function() {
CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference).then(function (success) {
$state.go('agenda.item.detail', {id: success.id});
var item = $scope.currentListOfSpeakersItem();
if (item) {
$state.go('agenda.item.detail', {id: item.id});
}
};
$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);
}
});
}
};
}
])

View File

@ -268,14 +268,55 @@
<h4 translate>List of speakers</h4>
</a>
<div uib-collapse="!isSpeakerList" ng-cloak>
<projector-button model="listofspeakers" default-projector-id="listOfSpeakersDefaultProjectorId">
</projector-button>
<div class="btn-group" os-perms="agenda.can_manage">
<a ng-click="goToListOfSpeakers()" class="btn btn-default btn-sm"
title="{{ 'Manage current list of speakers' | translate}}">
<i class="fa fa-microphone"></i>
<!-- Current list of speakers projector button -->
<div class="btn-group button" uib-dropdown
uib-tooltip="{{ 'Project the current list of speakers' | translate }}"
os-perms="core.can_manage_projector">
<button type="button" class="btn btn-default btn-sm"
title="{{ 'Project current list of speakers' | translate }}"
ng-click="currentListOfSpeakers.project(listOfSpeakersDefaultProjectorId, currentListOfSpeakersAsOverlay)"
ng-class="{ 'btn-primary': currentListOfSpeakers.isProjected().length && inArray(currentListOfSpeakers.isProjected(), listOfSpeakersDefaultProjectorId)}">
<i class="fa fa-video-camera"></i>
<translate>Current list of speakers</translate>
</button>
<button type="button" class="btn btn-default btn-sm slimDropDown"
ng-if="projectors.length > 1"
uib-dropdown-toggle
ng-class="{ 'btn-primary': currentListOfSpeakers.isProjected().length && !inArray(currentListOfSpeakers.isProjected(), listOfSpeakersDefaultProjectorId)}">
<span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" aria-labelledby="split-button" ng-if="projectors.length > 1">
<li role="menuitem">
<a href="" ng-click="setOverlay(false); $event.stopPropagation();">
<i class="fa" ng-class="currentListOfSpeakersAsOverlay ? 'fa-circle-o' : 'fa-check-circle-o'"></i>
<translate>Project as slide</translate>
</a>
</li>
<li role="menuitem">
<a href="" ng-click="setOverlay(true); $event.stopPropagation();">
<i class="fa" ng-class="currentListOfSpeakersAsOverlay ? 'fa-check-circle-o' : 'fa-circle-o'"></i>
<translate>Project as overlay</translate>
</a>
</li>
<li class="divider"></li>
<li role="menuitem" ng-repeat="projector in projectors | orderBy:'id'">
<a href="" ng-click="currentListOfSpeakers.project(projector.id, currentListOfSpeakersAsOverlay)"
ng-class="{ 'projected': inArray(currentListOfSpeakers.isProjected(), projector.id) }">
<i class="fa fa-video-camera" ng-show="inArray(currentListOfSpeakers.isProjected(), projector.id)"></i>
{{ projector.name | translate }}
<span ng-if="projector.id == listOfSpeakersDefaultProjectorId">(<translate>Default</translate>)</span>
</a>
</li>
</ul>
</div>
<button os-perms="agenda.can_manage"
ng-disabled="!currentListOfSpeakersItem()"
ng-click="goToListOfSpeakers()" class="btn btn-default btn-sm"
uib-tooltip="{{ 'Manage the current list of speakers' | translate}}">
<i class="fa fa-microphone"></i>
</button>
</div>
</div>
</div><!-- end div ProjectorControlCtrl -->