Manage view for Current list of speakers (closes #3007)

This commit is contained in:
FinnStutzenstein 2017-02-24 11:38:17 +01:00 committed by Emanuel Schütze
parent f69781b88b
commit c093796a17
5 changed files with 215 additions and 217 deletions

View File

@ -14,6 +14,7 @@ Agenda:
- 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.
- Manage speakers on the current list of speakers view.
Core:
- Added support for multiple projectors.
@ -28,7 +29,6 @@ Core:
- Used Roboto font in all templates.
- Added HTML support for messages on the projector.
- Moved custom slides to own app "topics". Renamed it to "Topic".
- Added an overlay for the current list of speakers.
- Added button to clear the chatbox.
- Switched editor back from TinyMCE to CKEditor which provides a
better copy/paste support from MS Word.

View File

@ -389,22 +389,30 @@ angular.module('OpenSlidesApp.agenda.site', [
.controller('ItemDetailCtrl', [
'$scope',
'$filter',
'$http',
'$state',
'operator',
'Agenda',
'User',
'itemId',
'Projector',
'ProjectionDefault',
function ($scope, $filter, $http, $state, operator, Agenda, User, itemId, Projector, ProjectionDefault) {
var item = Agenda.get(itemId);
Agenda.bindOne(item.id, $scope, 'item');
User.bindAll({}, $scope, 'users');
function ($scope, $filter, Agenda, itemId, Projector, ProjectionDefault) {
$scope.alert = {};
$scope.$watch(function () {
return Agenda.lastModified(itemId);
}, function () {
$scope.item = Agenda.get(itemId);
// all speakers
$scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight');
// next speakers
$scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
// current speaker
$scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null});
// last speakers
$scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'});
});
$scope.$watch(function () {
return Projector.lastModified();
}, function () {
var item_app_name = item.content_object.collection.split('/')[0];
var item_app_name = $scope.item.content_object.collection.split('/')[0];
var projectiondefaultItem = ProjectionDefault.filter({name: item_app_name})[0];
if (projectiondefaultItem) {
$scope.defaultProjectorItemId = projectiondefaultItem.projector_id;
@ -414,40 +422,35 @@ angular.module('OpenSlidesApp.agenda.site', [
$scope.defaultProjectorListOfSpeakersId = projectiondefaultListOfSpeakers.projector_id;
}
});
}
])
$scope.$watch(function () {
return Agenda.lastModified();
}, function () {
// all speakers
$scope.speakers = $filter('orderBy')(item.speakers, 'weight');
// next speakers
$scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
// current speaker
$scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null});
// last speakers
$scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'});
});
/* This is the controller for the list of speakers partial management template.
* The parent controller needs to provide a $scope.item, $scope.speakers, $scope.nextSpeakers,
* $scope.currentSpeakers, $scope.lastSpeakers. See (as example) ItemDetailCtrl. */
.controller('ListOfSpeakersManagementCtrl', [
'$scope',
'$http',
'$filter',
'Agenda',
'User',
'operator',
function ($scope, $http, $filter, Agenda, User, operator) {
User.bindAll({}, $scope, 'users');
$scope.speakerSelectBox = {};
$scope.alert = {};
$scope.$watch(function () {
return Agenda.lastModified();
}, function () {
$scope.speakers = $filter('orderBy')(item.speakers, 'weight');
});
// close/open list of speakers of current item
$scope.closeList = function (listClosed) {
item.speaker_list_closed = listClosed;
Agenda.save(item);
$scope.item.speaker_list_closed = listClosed;
Agenda.save($scope.item);
};
// add user to list of speakers
$scope.addSpeaker = function (userId) {
$http.post('/rest/agenda/item/' + item.id + '/manage_speaker/', {'user': userId})
$http.post('/rest/agenda/item/' + $scope.item.id + '/manage_speaker/', {'user': userId})
.success(function (data){
$scope.alert.show = false;
$scope.speakers = item.speakers;
$scope.speakers = $scope.item.speakers;
$scope.speakerSelectBox = {};
})
.error(function (data){
@ -459,37 +462,37 @@ angular.module('OpenSlidesApp.agenda.site', [
// delete speaker(!) from list of speakers
$scope.removeSpeaker = function (speakerId) {
$http.delete(
'/rest/agenda/item/' + item.id + '/manage_speaker/',
'/rest/agenda/item/' + $scope.item.id + '/manage_speaker/',
{headers: {'Content-Type': 'application/json'},
data: JSON.stringify({speaker: speakerId})}
)
.success(function(data){
$scope.speakers = item.speakers;
$scope.speakers = $scope.item.speakers;
})
.error(function(data){
$scope.alert = { type: 'danger', msg: data.detail, show: true };
});
$scope.speakers = item.speakers;
$scope.speakers = $scope.item.speakers;
};
//delete all speakers from list of speakers
$scope.removeAllSpeakers = function () {
var speakersOnList = [];
angular.forEach(item.speakers, function (speaker) {
angular.forEach($scope.item.speakers, function (speaker) {
speakersOnList.push(speaker.id);
});
$http.delete(
'/rest/agenda/item/' + item.id + '/manage_speaker/',
'/rest/agenda/item/' + $scope.item.id + '/manage_speaker/',
{headers: {'Content-Type': 'application/json'},
data: JSON.stringify({speaker: speakersOnList})}
)
.success(function(data){
$scope.speakers = item.speakers;
$scope.speakers = $scope.item.speakers;
})
.error(function(data){
$scope.alert = { type: 'danger', msg: data.detail, show: true };
});
$scope.speakers = item.speakers;
$scope.speakers = $scope.item.speakers;
};
// Return true if the requested user is allowed to do a specific action
@ -502,7 +505,7 @@ angular.module('OpenSlidesApp.agenda.site', [
switch (action) {
case 'add':
return (operator.hasPerms('agenda.can_be_speaker') &&
!item.speaker_list_closed &&
!$scope.item.speaker_list_closed &&
$.inArray(operator.user.id, nextUsers) == -1);
case 'remove':
if (operator.user) {
@ -525,7 +528,7 @@ angular.module('OpenSlidesApp.agenda.site', [
// begin speech of selected/next speaker
$scope.beginSpeech = function (speakerId) {
$http.put('/rest/agenda/item/' + item.id + '/speak/', {'speaker': speakerId})
$http.put('/rest/agenda/item/' + $scope.item.id + '/speak/', {'speaker': speakerId})
.success(function(data){
$scope.alert.show = false;
})
@ -537,7 +540,7 @@ angular.module('OpenSlidesApp.agenda.site', [
// end speech of current speaker
$scope.endSpeech = function () {
$http.delete(
'/rest/agenda/item/' + item.id + '/speak/',
'/rest/agenda/item/' + $scope.item.id + '/speak/',
{headers: {'Content-Type': 'application/json'}, data: {}}
)
.error(function(data){
@ -560,7 +563,7 @@ angular.module('OpenSlidesApp.agenda.site', [
angular.forEach(nextSpeakers, function (speaker) {
sortedSpeakers.push(speaker.id);
});
$http.post('/rest/agenda/item/' + item.id + '/sort_speakers/',
$http.post('/rest/agenda/item/' + $scope.item.id + '/sort_speakers/',
{speakers: sortedSpeakers}
);
}
@ -607,44 +610,64 @@ angular.module('OpenSlidesApp.agenda.site', [
.controller('CurrentListOfSpeakersViewCtrl', [
'$scope',
'$state',
'$http',
'$filter',
'Projector',
'ProjectionDefault',
'Agenda',
'Config',
'CurrentListOfSpeakersItem',
'CurrentListOfSpeakersSlide',
function($scope, $state, $http, Projector, ProjectionDefault, Config, CurrentListOfSpeakersItem,
CurrentListOfSpeakersSlide) {
function($scope, $http, $filter, Projector, ProjectionDefault, Agenda, Config,
CurrentListOfSpeakersItem, CurrentListOfSpeakersSlide) {
$scope.alert = {};
$scope.currentListOfSpeakers = CurrentListOfSpeakersSlide;
// Watch for changes in the current list of speakers reference
$scope.$watch(function () {
return Config.lastModified('projector_currentListOfSpeakers_reference');
}, function () {
$scope.currentListOfSpeakersReference = $scope.config('projector_currentListOfSpeakers_reference');
$scope.updateCurrentListOfSpeakers();
$scope.updateCurrentListOfSpeakersItem();
});
$scope.$watch(function () {
return Projector.lastModified();
}, function() {
$scope.projectors = Projector.getAll();
// If there is just one projector we provide just the overlay.
if ($scope.projectors.length === 1) {
$scope.currentListOfSpeakersAsOverlay = true;
}
$scope.updateCurrentListOfSpeakers();
$scope.updateCurrentListOfSpeakersItem();
$scope.listOfSpeakersDefaultProjectorId = ProjectionDefault.filter({name: 'agenda_current_list_of_speakers'})[0].projector_id;
});
$scope.updateCurrentListOfSpeakers = function () {
$scope.agendaItem = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference);
};
// go to the list of speakers (management) of the currently
// displayed projector slide
$scope.goToListOfSpeakers = function() {
if ($scope.agendaItem) {
$state.go('agenda.item.detail', {id: $scope.agendaItem.id});
$scope.$watch(function () {
return $scope.item ? Agenda.lastModified($scope.item.id) : void 0;
}, function () {
$scope.updateCurrentListOfSpeakersItem();
});
$scope.updateCurrentListOfSpeakersItem = function () {
$scope.item = CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference);
if ($scope.item) {
// all speakers
$scope.speakers = $filter('orderBy')($scope.item.speakers, 'weight');
// next speakers
$scope.nextSpeakers = $filter('filter')($scope.speakers, {'begin_time': null});
// current speaker
$scope.currentSpeaker = $filter('filter')($scope.speakers, {'begin_time': '!!', 'end_time': null});
// last speakers
$scope.lastSpeakers = $filter('filter')($scope.speakers, {'end_time': '!!'});
} else {
$scope.speakers = void 0;
$scope.nextSpeakers = void 0;
$scope.currentSpeaker = void 0;
$scope.lastSpeakers = void 0;
}
};
$scope.currentListOfSpeakers = CurrentListOfSpeakersSlide;
// Set the current overlay status
if ($scope.currentListOfSpeakers.isProjected().length) {
var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus();
@ -652,9 +675,6 @@ angular.module('OpenSlidesApp.agenda.site', [
} else {
$scope.currentListOfSpeakersAsOverlay = true;
}
$scope.currentListOfSpeakersItem = function () {
return CurrentListOfSpeakersItem.getItem($scope.currentListOfSpeakersReference);
};
$scope.setOverlay = function (overlay) {
$scope.currentListOfSpeakersAsOverlay = overlay;
var isProjected = $scope.currentListOfSpeakers.isProjectedWithOverlayStatus();

View File

@ -5,15 +5,6 @@
<i class="fa fa-angle-double-left fa-lg"></i>
<translate>Back to overview</translate>
</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>
<!-- Current list of speakers projector button -->
<div class="btn-group button" uib-dropdown
uib-tooltip="{{ 'Project the current list of speakers' | translate }}"
@ -58,26 +49,12 @@
</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> {{ item.getTitle() }}
<span class="slimlabel label label-danger ng-scope" style="" ng-if="item.speaker_list_closed" translate>
Closed
</span>
</h2>
</div>
</div>
<div class="details">
<!-- 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>
<ng-include src="'static/templates/agenda/list-of-speakers-partial-management.html'"></ng-include>

View File

@ -51,132 +51,4 @@
</div>
</div>
<div ng-if="item" class="details">
<div class="speakers-toolbar">
<div class="pull-right">
<span os-perms="agenda.can_manage">
<button ng-if="isAllowed('removeAll')" class="btn btn-sm btn-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to remove all speakers from this list?'| translate }}"
ng-bootbox-confirm-action="removeAllSpeakers()">
<i class="fa fa-trash fa-lg"></i>
<translate>Remove all speakers</translate>
</button>
<button ng-if="item.speaker_list_closed" ng-click="closeList(false)"
class="btn btn-sm btn-default">
<i class="fa fa-toggle-off"></i>
<translate>Closed</translate>
</button>
<button ng-if="!item.speaker_list_closed" ng-click="closeList(true)"
class="btn btn-sm btn-default">
<i class="fa fa-toggle-on"></i>
<translate>Open</translate>
</button>
</span>
</div>
<!-- Start/Stop controls -->
<button ng-if="isAllowed('endCurrentSpeech')" ng-click="endSpeech()"
class="btn btn-sm btn-default">
<i class="fa fa-stop"></i>
<translate>End current speech</translate>
</button>
<button ng-if="isAllowed('beginNextSpeech')" ng-click="beginSpeech()"
class="btn btn-sm btn-default">
<i class="fa fa-play"></i>
<translate>Begin next speech</translate>
</button>
</div>
<!-- text for empty list -->
<p ng-if="speakers.length == 0" translate>
The list of speakers is empty.
</p>
<template-hook hook-name="itemDetailListOfSpeakersButtons"></template-hook>
<!-- Last speakers -->
<div class="spacer-top-lg">
<button ng-if="isAllowed('showLastSpeakers')" ng-click="$parent.showOldSpeakers = !$parent.showOldSpeakers"
class="btn btn-xs btn-default">
<translate ng-if="!$parent.showOldSpeakers">Last speakers</translate>
<translate ng-if="$parent.showOldSpeakers">Hide</translate>
</button>
<div uib-collapse="!showOldSpeakers">
<ol class="indentation">
<li ng-repeat="speaker in lastSpeakers">
{{ speaker.user.get_full_name() }}
<small class="grey">
{{ getDuration(speaker) | osSecondsToTime }} <translate>minutes</translate>
(<translate>Start time</translate>:
{{ speaker.begin_time | date:'yyyy-MM-dd HH:mm:ss' }})
</small>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-xs" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</ol>
</div>
</div>
<!-- Current speaker -->
<p ng-repeat="speaker in currentSpeaker" class="currentSpeaker spacer indentation">
<i class="fa fa-microphone fa-lg"></i>
{{ speaker.user.get_full_name() }}
<button os-perms="agenda.can_manage" ng-click="endSpeech()"
class="btn btn-default btn-sm" title="{{ 'End speech' | translate }}">
<i class="fa fa-microphone-slash"></i> <translate>Stop</translate>
</button>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</p>
<!-- Next speakers -->
<div ng-show="nextSpeakers.length > 0">
<div ui-tree="treeOptions">
<ol ui-tree-nodes="" ng-model="speakers">
<li ng-repeat="speaker in nextSpeakers | orderBy:'weight'" ui-tree-node>
<i os-perms="agenda.can_manage" ui-tree-handle="" class="fa fa-arrows-v"></i>
{{ $index + 1 }}.
{{ speaker.user.get_full_name() }}
&nbsp;
<button os-perms="agenda.can_manage" ng-click="beginSpeech(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Begin speech' | translate }}">
<i class="fa fa-microphone"></i> <translate>Start</translate>
</button>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</ol>
</div>
</div>
<!-- Select speakers form -->
<div class="form-group spacer-top-lg">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
{{ alert.msg }}
</div>
<div os-perms="agenda.can_manage">
<select chosen
ng-model="speakerSelectBox.selected"
ng-change="addSpeaker(speakerSelectBox.selected)"
ng-options="user.id as user.get_full_name() for user in users"
search-contains="true"
placeholder-text-single="'Select or search a participant ...' | translate"
no-results-text="'No results available ...' | translate"
class="form-control">
<select>
</div>
<p class="spacer">
<button ng-if="isAllowed('add')" ng-click="addSpeaker()" class="btn btn-default btn-sm">
<i class="fa fa-plus"></i>
<translate>Add me</translate>
</button>
<button ng-if="isAllowed('remove')" ng-click="removeSpeaker()" class="btn btn-default btn-sm">
<i class="fa fa-minus"></i>
<translate>Remove me</translate>
</button>
</div>
</div>
<ng-include src="'static/templates/agenda/list-of-speakers-partial-management.html'"></ng-include>

View File

@ -0,0 +1,129 @@
<div ng-if="item" class="details" ng-controller="ListOfSpeakersManagementCtrl">
<div class="speakers-toolbar">
<div class="pull-right">
<span os-perms="agenda.can_manage">
<button ng-if="isAllowed('removeAll')" class="btn btn-sm btn-danger"
ng-bootbox-confirm="{{ 'Are you sure you want to remove all speakers from this list?'| translate }}"
ng-bootbox-confirm-action="removeAllSpeakers()">
<i class="fa fa-trash fa-lg"></i>
<translate>Remove all speakers</translate>
</button>
<button ng-if="item.speaker_list_closed" ng-click="closeList(false)"
class="btn btn-sm btn-default">
<i class="fa fa-toggle-off"></i>
<translate>Closed</translate>
</button>
<button ng-if="!item.speaker_list_closed" ng-click="closeList(true)"
class="btn btn-sm btn-default">
<i class="fa fa-toggle-on"></i>
<translate>Open</translate>
</button>
</span>
</div>
<!-- Start/Stop controls -->
<button ng-if="isAllowed('endCurrentSpeech')" ng-click="endSpeech()"
class="btn btn-sm btn-default">
<i class="fa fa-stop"></i>
<translate>End current speech</translate>
</button>
<button ng-if="isAllowed('beginNextSpeech')" ng-click="beginSpeech()"
class="btn btn-sm btn-default">
<i class="fa fa-play"></i>
<translate>Begin next speech</translate>
</button>
</div>
<!-- text for empty list -->
<p ng-if="speakers.length == 0" translate>
The list of speakers is empty.
</p>
<template-hook hook-name="itemDetailListOfSpeakersButtons"></template-hook>
<!-- Last speakers -->
<div class="spacer-top-lg">
<button ng-if="isAllowed('showLastSpeakers')" ng-click="$parent.showOldSpeakers = !$parent.showOldSpeakers"
class="btn btn-xs btn-default">
<translate ng-if="!$parent.showOldSpeakers">Last speakers</translate>
<translate ng-if="$parent.showOldSpeakers">Hide</translate>
</button>
<div uib-collapse="!showOldSpeakers">
<ol class="indentation">
<li ng-repeat="speaker in lastSpeakers">
{{ speaker.user.get_full_name() }}
<small class="grey">
{{ getDuration(speaker) | osSecondsToTime }} <translate>minutes</translate>
(<translate>Start time</translate>:
{{ speaker.begin_time | date:'yyyy-MM-dd HH:mm:ss' }})
</small>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-xs" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</ol>
</div>
</div>
<!-- Current speaker -->
<p ng-repeat="speaker in currentSpeaker" class="currentSpeaker spacer indentation">
<i class="fa fa-microphone fa-lg"></i>
{{ speaker.user.get_full_name() }}
<button os-perms="agenda.can_manage" ng-click="endSpeech()"
class="btn btn-default btn-sm" title="{{ 'End speech' | translate }}">
<i class="fa fa-microphone-slash"></i> <translate>Stop</translate>
</button>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</p>
<!-- Next speakers -->
<div ng-show="nextSpeakers.length > 0">
<div ui-tree="treeOptions">
<ol ui-tree-nodes="" ng-model="speakers">
<li ng-repeat="speaker in nextSpeakers | orderBy:'weight'" ui-tree-node>
<i os-perms="agenda.can_manage" ui-tree-handle="" class="fa fa-arrows-v"></i>
{{ $index + 1 }}.
{{ speaker.user.get_full_name() }}
&nbsp;
<button os-perms="agenda.can_manage" ng-click="beginSpeech(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Begin speech' | translate }}">
<i class="fa fa-microphone"></i> <translate>Start</translate>
</button>
<button os-perms="agenda.can_manage" ng-click="removeSpeaker(speaker.id)"
class="btn btn-default btn-sm" title="{{ 'Remove' | translate }}">
<i class="fa fa-times"></i>
</button>
</ol>
</div>
</div>
<!-- Select speakers form -->
<div class="form-group spacer-top-lg">
<div uib-alert ng-show="alert.show" ng-class="'alert-' + (alert.type || 'warning')" ng-click="alert={}" close="alert={}">
{{ alert.msg }}
</div>
<div os-perms="agenda.can_manage">
<select chosen
ng-model="speakerSelectBox.selected"
ng-change="addSpeaker(speakerSelectBox.selected)"
ng-options="user.id as user.get_full_name() for user in users"
search-contains="true"
placeholder-text-single="'Select or search a participant ...' | translate"
no-results-text="'No results available ...' | translate"
class="form-control">
<select>
</div>
<p class="spacer">
<button ng-if="isAllowed('add')" ng-click="addSpeaker()" class="btn btn-default btn-sm">
<i class="fa fa-plus"></i>
<translate>Add me</translate>
</button>
<button ng-if="isAllowed('remove')" ng-click="removeSpeaker()" class="btn btn-default btn-sm">
<i class="fa fa-minus"></i>
<translate>Remove me</translate>
</button>
</div>
</div>