Angular Client
* Split angular app into a site- and a projector app * Created client slide api and slides for customslide and user * JS-function to activate a slide
This commit is contained in:
parent
a5d9f0bb42
commit
a4c00d5ee3
@ -1,5 +1,18 @@
|
|||||||
angular.module('OpenSlidesApp.agenda', [])
|
angular.module('OpenSlidesApp.agenda', [])
|
||||||
|
|
||||||
|
.factory('Agenda', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'agenda/item',
|
||||||
|
endpoint: '/rest/agenda/item/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
// Make sure that the Agenda resource is loaded.
|
||||||
|
.run(function(Agenda) {});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.agenda.site', ['OpenSlidesApp.agenda'])
|
||||||
|
|
||||||
.config(function($stateProvider) {
|
.config(function($stateProvider) {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('agenda', {
|
.state('agenda', {
|
||||||
@ -68,13 +81,6 @@ angular.module('OpenSlidesApp.agenda', [])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('Agenda', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'agenda/item',
|
|
||||||
endpoint: '/rest/agenda/item/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('ItemListCtrl', function($scope, Agenda, tree) {
|
.controller('ItemListCtrl', function($scope, Agenda, tree) {
|
||||||
Agenda.bindAll({}, $scope, 'items');
|
Agenda.bindAll({}, $scope, 'items');
|
||||||
|
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
angular.module('OpenSlidesApp.assignments', [])
|
angular.module('OpenSlidesApp.assignments', [])
|
||||||
|
|
||||||
|
.factory('Assignment', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'assignments/assignment',
|
||||||
|
endpoint: '/rest/assignments/assignment/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(Assignment) {});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.assignments.site', ['OpenSlidesApp.assignments'])
|
||||||
|
|
||||||
.config(function($stateProvider) {
|
.config(function($stateProvider) {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('assignments', {
|
.state('assignments', {
|
||||||
@ -37,13 +49,6 @@ angular.module('OpenSlidesApp.assignments', [])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('Assignment', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'assignments/assignment',
|
|
||||||
endpoint: '/rest/assignments/assignment/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('AssignmentListCtrl', function($scope, Assignment, phases) {
|
.controller('AssignmentListCtrl', function($scope, Assignment, phases) {
|
||||||
Assignment.bindAll({}, $scope, 'assignments');
|
Assignment.bindAll({}, $scope, 'assignments');
|
||||||
// get all item types via OPTIONS request
|
// get all item types via OPTIONS request
|
||||||
|
@ -41,7 +41,7 @@ class Projector(RESTModelMixin, models.Model):
|
|||||||
('can_use_chat', ugettext_noop('Can use the chat')))
|
('can_use_chat', ugettext_noop('Can use the chat')))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def projector_elements(self):
|
def elements(self):
|
||||||
"""
|
"""
|
||||||
A generator to retrieve all projector elements given in the config
|
A generator to retrieve all projector elements given in the config
|
||||||
field. For every element the method get_data() is called and its
|
field. For every element the method get_data() is called and its
|
||||||
|
@ -13,24 +13,16 @@ class CustomSlideSlide(ProjectorElement):
|
|||||||
Slide definitions for custom slide model.
|
Slide definitions for custom slide model.
|
||||||
"""
|
"""
|
||||||
name = 'core/customslide'
|
name = 'core/customslide'
|
||||||
scripts = 'core/customslide_slide.js'
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
pk = self.config_entry.get('id')
|
pk = self.config_entry.get('id')
|
||||||
if not CustomSlide.objects.filter(pk=pk).exists():
|
if not CustomSlide.objects.filter(pk=pk).exists():
|
||||||
raise ProjectorException(_('Custom slide does not exist.'))
|
raise ProjectorException(_('Custom slide does not exist.'))
|
||||||
return [{
|
return {'id': pk}
|
||||||
'collection': 'core/customslide',
|
|
||||||
'id': pk}]
|
|
||||||
|
|
||||||
def get_requirements(self, config_entry):
|
def get_requirements(self, config_entry):
|
||||||
self.config_entry = config_entry
|
pk = config_entry.get('id')
|
||||||
try:
|
if pk is not None:
|
||||||
pk = self.get_context()[0]['id']
|
|
||||||
except ProjectorException:
|
|
||||||
# Custom slide does not exist so just do nothing.
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
yield ProjectorRequirement(
|
yield ProjectorRequirement(
|
||||||
view_class=CustomSlideViewSet,
|
view_class=CustomSlideViewSet,
|
||||||
view_action='retrieve',
|
view_action='retrieve',
|
||||||
@ -42,7 +34,6 @@ class Clock(ProjectorElement):
|
|||||||
Clock on the projector.
|
Clock on the projector.
|
||||||
"""
|
"""
|
||||||
name = 'core/clock'
|
name = 'core/clock'
|
||||||
scripts = 'core/clock.js'
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
return {'server_time': now().timestamp()}
|
return {'server_time': now().timestamp()}
|
||||||
@ -73,7 +64,6 @@ class Countdown(ProjectorElement):
|
|||||||
To hide a running countdown add {"hidden": true}.
|
To hide a running countdown add {"hidden": true}.
|
||||||
"""
|
"""
|
||||||
name = 'core/countdown'
|
name = 'core/countdown'
|
||||||
scripts = 'core/countdown.js'
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
if self.config_entry.get('countdown_time') is None:
|
if self.config_entry.get('countdown_time') is None:
|
||||||
@ -88,7 +78,6 @@ class Message(ProjectorElement):
|
|||||||
Short message on the projector. Rendered as overlay.
|
Short message on the projector. Rendered as overlay.
|
||||||
"""
|
"""
|
||||||
name = 'core/message'
|
name = 'core/message'
|
||||||
scripts = 'core/message.js'
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
if self.config_entry.get('message') is None:
|
if self.config_entry.get('message') is None:
|
||||||
|
@ -33,7 +33,7 @@ class ProjectorSerializer(ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Projector
|
model = Projector
|
||||||
fields = ('config', 'projector_elements', )
|
fields = ('id', 'config', 'elements', )
|
||||||
|
|
||||||
|
|
||||||
class CustomSlideSerializer(ModelSerializer):
|
class CustomSlideSerializer(ModelSerializer):
|
||||||
|
@ -1,24 +1,38 @@
|
|||||||
angular.module('OpenSlidesApp', [
|
angular.module('OpenSlidesApp', [
|
||||||
'ui.router',
|
|
||||||
'angular-loading-bar',
|
'angular-loading-bar',
|
||||||
'js-data',
|
'js-data',
|
||||||
'gettext',
|
'gettext',
|
||||||
'ngBootbox',
|
|
||||||
'ngFabForm',
|
|
||||||
'ngMessages',
|
|
||||||
'ngAnimate',
|
'ngAnimate',
|
||||||
'ngCsvImport',
|
|
||||||
'ngSanitize',
|
|
||||||
'ui.bootstrap',
|
'ui.bootstrap',
|
||||||
'ui.select',
|
|
||||||
'ui.tree',
|
'ui.tree',
|
||||||
'xeditable',
|
]);
|
||||||
'OpenSlidesApp.core',
|
|
||||||
|
angular.module('OpenSlidesApp.projector', [
|
||||||
|
'OpenSlidesApp',
|
||||||
|
'OpenSlidesApp.core.projector',
|
||||||
'OpenSlidesApp.agenda',
|
'OpenSlidesApp.agenda',
|
||||||
'OpenSlidesApp.motions',
|
'OpenSlidesApp.motions',
|
||||||
'OpenSlidesApp.assignments',
|
'OpenSlidesApp.assignments',
|
||||||
'OpenSlidesApp.users',
|
'OpenSlidesApp.users.projector',
|
||||||
'OpenSlidesApp.mediafiles',
|
'OpenSlidesApp.mediafiles',
|
||||||
|
]);
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.site', [
|
||||||
|
'OpenSlidesApp',
|
||||||
|
'ui.router',
|
||||||
|
'ngBootbox',
|
||||||
|
'ngFabForm',
|
||||||
|
'ngMessages',
|
||||||
|
'ngCsvImport',
|
||||||
|
'ngSanitize', // TODO: remove this as global dependency
|
||||||
|
'ui.select',
|
||||||
|
'xeditable',
|
||||||
|
'OpenSlidesApp.core.site',
|
||||||
|
'OpenSlidesApp.agenda.site',
|
||||||
|
'OpenSlidesApp.motions.site',
|
||||||
|
'OpenSlidesApp.assignments.site',
|
||||||
|
'OpenSlidesApp.users.site',
|
||||||
|
'OpenSlidesApp.mediafiles.site',
|
||||||
])
|
])
|
||||||
|
|
||||||
.config(function($urlRouterProvider, $locationProvider) {
|
.config(function($urlRouterProvider, $locationProvider) {
|
||||||
|
@ -1,5 +1,119 @@
|
|||||||
|
// The core module used for the OpenSlides site and the projector
|
||||||
angular.module('OpenSlidesApp.core', [])
|
angular.module('OpenSlidesApp.core', [])
|
||||||
|
|
||||||
|
.config(function(DSProvider) {
|
||||||
|
// Reloads everything after 5 minutes.
|
||||||
|
// TODO: * find a way only to reload things that are still needed
|
||||||
|
DSProvider.defaults.maxAge = 5 * 60 * 1000; // 5 minutes
|
||||||
|
DSProvider.defaults.reapAction = 'none';
|
||||||
|
DSProvider.defaults.afterReap = function(model, items) {
|
||||||
|
if (items.length > 5) {
|
||||||
|
model.findAll({}, {bypassCache: true});
|
||||||
|
} else {
|
||||||
|
_.forEach(items, function (item) {
|
||||||
|
model.refresh(item[model.idAttribute]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(DS, autoupdate) {
|
||||||
|
autoupdate.on_message(function(data) {
|
||||||
|
// TODO: when MODEL.find() is called after this
|
||||||
|
// a new request is fired. This could be a bug in DS
|
||||||
|
|
||||||
|
// TODO: Do not send the status code to the client, but make the decission
|
||||||
|
// on the server side. It is an implementation detail, that tornado
|
||||||
|
// sends request to wsgi, which should not concern the client.
|
||||||
|
console.log("Received object: " + data.collection + ", " + data.id);
|
||||||
|
if (data.status_code == 200) {
|
||||||
|
DS.inject(data.collection, data.data);
|
||||||
|
} else if (data.status_code == 404) {
|
||||||
|
DS.eject(data.collection, data.id);
|
||||||
|
}
|
||||||
|
// TODO: handle other statuscodes
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function($rootScope, Config) {
|
||||||
|
// Puts the config object into each scope.
|
||||||
|
// TODO: maybe rootscope.config has to set before findAll() is finished
|
||||||
|
Config.findAll().then(function() {
|
||||||
|
$rootScope.config = function(key) {
|
||||||
|
try {
|
||||||
|
return Config.get(key).value;
|
||||||
|
}
|
||||||
|
catch(err) {
|
||||||
|
console.log("Unkown config key: " + key);
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('autoupdate', function() {
|
||||||
|
var url = location.origin + "/sockjs";
|
||||||
|
|
||||||
|
var Autoupdate = {
|
||||||
|
socket: null,
|
||||||
|
message_receivers: [],
|
||||||
|
connect: function() {
|
||||||
|
var autoupdate = this;
|
||||||
|
this.socket = new SockJS(url);
|
||||||
|
|
||||||
|
this.socket.onmessage = function(event) {
|
||||||
|
_.forEach(autoupdate.message_receivers, function(receiver) {
|
||||||
|
receiver(event.data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.socket.onclose = function() {
|
||||||
|
setTimeout(autoupdate.connect, 5000);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
on_message: function(receiver) {
|
||||||
|
this.message_receivers.push(receiver);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Autoupdate.connect();
|
||||||
|
return Autoupdate;
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('Customslide', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'core/customslide',
|
||||||
|
endpoint: '/rest/core/customslide/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('Tag', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'core/tag',
|
||||||
|
endpoint: '/rest/core/tag/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('Config', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'config/config',
|
||||||
|
idAttribute: 'key',
|
||||||
|
endpoint: '/rest/config/config/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('Projector', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'core/projector',
|
||||||
|
endpoint: '/rest/core/projector/',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(Projector, Config, Tag, Customslide){});
|
||||||
|
|
||||||
|
|
||||||
|
// The core module for the OpenSlides site
|
||||||
|
angular.module('OpenSlidesApp.core.site', ['OpenSlidesApp.core'])
|
||||||
|
|
||||||
.config(function($stateProvider, $urlMatcherFactoryProvider) {
|
.config(function($stateProvider, $urlMatcherFactoryProvider) {
|
||||||
// Make the trailing slash optional
|
// Make the trailing slash optional
|
||||||
$urlMatcherFactoryProvider.strictMode(false)
|
$urlMatcherFactoryProvider.strictMode(false)
|
||||||
@ -80,6 +194,13 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
url: '/',
|
url: '/',
|
||||||
templateUrl: 'static/templates/dashboard.html'
|
templateUrl: 'static/templates/dashboard.html'
|
||||||
})
|
})
|
||||||
|
.state('projector', {
|
||||||
|
url: '/projector',
|
||||||
|
data: {extern: true},
|
||||||
|
onEnter: function($window) {
|
||||||
|
$window.location.href = this.url;
|
||||||
|
}
|
||||||
|
})
|
||||||
.state('core', {
|
.state('core', {
|
||||||
url: '/core',
|
url: '/core',
|
||||||
abstract: true,
|
abstract: true,
|
||||||
@ -108,22 +229,6 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
$locationProvider.html5Mode(true);
|
$locationProvider.html5Mode(true);
|
||||||
})
|
})
|
||||||
|
|
||||||
.config(function(DSProvider) {
|
|
||||||
// Reloads everything after 5 minutes.
|
|
||||||
// TODO: * find a way only to reload things that are still needed
|
|
||||||
DSProvider.defaults.maxAge = 5 * 60 * 1000; // 5 minutes
|
|
||||||
DSProvider.defaults.reapAction = 'none';
|
|
||||||
DSProvider.defaults.afterReap = function(model, items) {
|
|
||||||
if (items.length > 5) {
|
|
||||||
model.findAll({}, {bypassCache: true});
|
|
||||||
} else {
|
|
||||||
_.forEach(items, function (item) {
|
|
||||||
model.refresh(item[model.idAttribute]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
// config for ng-fab-form
|
// config for ng-fab-form
|
||||||
.config(function(ngFabFormProvider) {
|
.config(function(ngFabFormProvider) {
|
||||||
ngFabFormProvider.extendConfig({
|
ngFabFormProvider.extendConfig({
|
||||||
@ -131,6 +236,8 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Helper to add ui.router states at runtime.
|
||||||
|
// Needed for the django url_patterns.
|
||||||
.provider('runtimeStates', function($stateProvider) {
|
.provider('runtimeStates', function($stateProvider) {
|
||||||
this.$get = function($q, $timeout, $state) {
|
this.$get = function($q, $timeout, $state) {
|
||||||
return {
|
return {
|
||||||
@ -141,6 +248,7 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Load the django url patterns
|
||||||
.run(function(runtimeStates, $http) {
|
.run(function(runtimeStates, $http) {
|
||||||
$http.get('/core/url_patterns/').then(function(data) {
|
$http.get('/core/url_patterns/').then(function(data) {
|
||||||
for (var pattern in data.data) {
|
for (var pattern in data.data) {
|
||||||
@ -155,87 +263,20 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.run(function(DS, autoupdate) {
|
// options for angular-xeditable
|
||||||
autoupdate.on_message(function(data) {
|
|
||||||
// TODO: when MODEL.find() is called after this
|
|
||||||
// a new request is fired. This could be a bug in DS
|
|
||||||
|
|
||||||
// TODO: Do not send the status code to the client, but make the decission
|
|
||||||
// on the server side. It is an implementation detail, that tornado
|
|
||||||
// sends request to wsgi, which should not concern the client.
|
|
||||||
if (data.status_code == 200) {
|
|
||||||
DS.inject(data.collection, data.data);
|
|
||||||
} else if (data.status_code == 404) {
|
|
||||||
DS.eject(data.collection, data.id);
|
|
||||||
}
|
|
||||||
// TODO: handle other statuscodes
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.run(function($rootScope, Config) {
|
|
||||||
// Puts the config object into each scope.
|
|
||||||
// TODO: maybe rootscope.config has to set before findAll() is finished
|
|
||||||
Config.findAll().then(function() {;
|
|
||||||
$rootScope.config = function(key) {
|
|
||||||
return Config.get(key).value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
//options for angular-xeditable
|
|
||||||
.run(function(editableOptions) {
|
.run(function(editableOptions) {
|
||||||
editableOptions.theme = 'bs3';
|
editableOptions.theme = 'bs3';
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('autoupdate', function() {
|
// Activate an Element from the Rest-API on the projector
|
||||||
//TODO: use config here
|
// At the moment it only activates item on projector 1
|
||||||
var url = "http://" + location.host + "/sockjs";
|
.factory('projectorActivate', function($http) {
|
||||||
|
return function(model, id) {
|
||||||
var Autoupdate = {
|
return $http.post(
|
||||||
socket: null,
|
'/rest/core/projector/1/prune_elements/',
|
||||||
message_receivers: [],
|
[{name: model.name, id: id}]
|
||||||
connect: function() {
|
);
|
||||||
var autoupdate = this;
|
|
||||||
this.socket = new SockJS(url);
|
|
||||||
|
|
||||||
this.socket.onmessage = function(event) {
|
|
||||||
_.forEach(autoupdate.message_receivers, function(receiver) {
|
|
||||||
receiver(event.data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.socket.onclose = function() {
|
|
||||||
setTimeout(autoupdate.connect, 5000);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
on_message: function(receiver) {
|
|
||||||
this.message_receivers.push(receiver);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
Autoupdate.connect();
|
|
||||||
return Autoupdate;
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('Customslide', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'core/customslide',
|
|
||||||
endpoint: '/rest/core/customslide/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('Tag', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'core/tag',
|
|
||||||
endpoint: '/rest/core/tag/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('Config', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'config/config',
|
|
||||||
idAttribute: 'key',
|
|
||||||
endpoint: '/rest/config/config/'
|
|
||||||
});
|
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller("LanguageCtrl", function ($scope, gettextCatalog) {
|
.controller("LanguageCtrl", function ($scope, gettextCatalog) {
|
||||||
@ -346,3 +387,82 @@ angular.module('OpenSlidesApp.core', [])
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// The core module for the OpenSlides projector
|
||||||
|
angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
||||||
|
|
||||||
|
// Provider to register slides in a .config() statement.
|
||||||
|
.provider('slides', function() {
|
||||||
|
var slidesMap = {};
|
||||||
|
|
||||||
|
this.registerSlide = function(name, config) {
|
||||||
|
slidesMap[name] = config;
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.$get = function($templateRequest, $q) {
|
||||||
|
var self = this;
|
||||||
|
return {
|
||||||
|
getElements: function(projector) {
|
||||||
|
var elements = [];
|
||||||
|
var factory = this;
|
||||||
|
_.forEach(projector.elements, function(element) {
|
||||||
|
if (element.name in slidesMap) {
|
||||||
|
element.template = slidesMap[element.name].template;
|
||||||
|
elements.push(element);
|
||||||
|
} else {
|
||||||
|
console.log("Unknown slide: " + element.name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.config(function(slidesProvider) {
|
||||||
|
slidesProvider.registerSlide('core/customslide', {
|
||||||
|
template: 'static/templates/core/slide_customslide.html',
|
||||||
|
});
|
||||||
|
|
||||||
|
slidesProvider.registerSlide('core/clock', {
|
||||||
|
template: 'static/templates/core/slide_clock.html',
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.filter('osServertime',function() {
|
||||||
|
return function(serverOffset) {
|
||||||
|
var date = new Date();
|
||||||
|
return date.setTime(date.getTime() - serverOffset);
|
||||||
|
};
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('ProjectorCtrl', function($scope, Projector, slides) {
|
||||||
|
Projector.find(1).then(function() {
|
||||||
|
$scope.$watch(function () {
|
||||||
|
return Projector.lastModified(1);
|
||||||
|
}, function () {
|
||||||
|
$scope.elements = [];
|
||||||
|
_.forEach(slides.getElements(Projector.get(1)), function(element) {
|
||||||
|
$scope.elements.push(element);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('SlideCustomSlideCtr', function($scope, Customslide) {
|
||||||
|
// Attention! Each object that is used here has to be dealt on server side.
|
||||||
|
// Add it to the coresponding get_requirements method of the ProjectorElement
|
||||||
|
// class.
|
||||||
|
var id = $scope.element.context.id;
|
||||||
|
Customslide.find(id);
|
||||||
|
Customslide.bindOne(id, $scope, 'customslide');
|
||||||
|
})
|
||||||
|
|
||||||
|
.controller('SlideClockCtr', function($scope) {
|
||||||
|
// Attention! Each object that is used here has to be dealt on server side.
|
||||||
|
// Add it to the coresponding get_requirements method of the ProjectorElement
|
||||||
|
// class.
|
||||||
|
$scope.serverOffset = Date.parse(new Date().toUTCString()) - $scope.element.context.server_time;
|
||||||
|
});
|
||||||
|
3
openslides/core/static/templates/core/slide_clock.html
Normal file
3
openslides/core/static/templates/core/slide_clock.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div ng-controller="SlideClockCtr">
|
||||||
|
{{ serverOffset | osServertime | date:'HH:mm' }} Uhr
|
||||||
|
</div>
|
@ -0,0 +1,4 @@
|
|||||||
|
<div ng-controller="SlideCustomSlideCtr">
|
||||||
|
<h1>{{ customslide.title }}</h1>
|
||||||
|
{{ customslide.text }}
|
||||||
|
</div>
|
@ -2,7 +2,7 @@
|
|||||||
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
<!--[if lt IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8 lt-ie7"> <![endif]-->
|
||||||
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
<!--[if IE 7]> <html lang="en" ng-app="myApp" class="no-js lt-ie9 lt-ie8"> <![endif]-->
|
||||||
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
|
<!--[if IE 8]> <html lang="en" ng-app="myApp" class="no-js lt-ie9"> <![endif]-->
|
||||||
<!--[if gt IE 8]><!--> <html lang="en" ng-app="OpenSlidesApp" class="no-js"> <!--<![endif]-->
|
<!--[if gt IE 8]><!--> <html lang="en" ng-app="OpenSlidesApp.site" class="no-js"> <!--<![endif]-->
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<base href="/">
|
<base href="/">
|
||||||
@ -133,7 +133,7 @@
|
|||||||
<span class="text" translate>Home</span>
|
<span class="text" translate>Home</span>
|
||||||
</a>
|
</a>
|
||||||
<li>
|
<li>
|
||||||
<a href="#TODO">
|
<a ui-sref="projector">
|
||||||
<span class="ico"><i class="fa fa-video-camera fa-lg"></i></span>
|
<span class="ico"><i class="fa fa-video-camera fa-lg"></i></span>
|
||||||
<span class="text" translate>Projector</span>
|
<span class="text" translate>Projector</span>
|
||||||
</a>
|
</a>
|
||||||
|
25
openslides/core/static/templates/projector.html
Normal file
25
openslides/core/static/templates/projector.html
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en" ng-app="OpenSlidesApp.projector" class="no-js">
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<base href="/">
|
||||||
|
<title>OpenSlides – Projector</title>
|
||||||
|
<link rel="stylesheet" href="static/css/openslides-libs.css">
|
||||||
|
<link rel="stylesheet" href="static/css/projector.css">
|
||||||
|
<script src="static/js/openslides-libs.js"></script>
|
||||||
|
|
||||||
|
<div id="header">
|
||||||
|
<img id="logo" src="/static/img/logo.png" alt="OpenSlides" />
|
||||||
|
<span class="navbar-text optional">{{ config('general_event_name') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="content" ng-controller="ProjectorCtrl">
|
||||||
|
<div ng-repeat="element in elements"><div ng-include="element.template"></div></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="static/js/app.js"></script>
|
||||||
|
<script src="static/js/core.js"></script>
|
||||||
|
<script src="static/js/agenda/agenda.js"></script>
|
||||||
|
<script src="static/js/motions/motions.js"></script>
|
||||||
|
<script src="static/js/assignments/assignments.js"></script>
|
||||||
|
<script src="static/js/users/users.js"></script>
|
||||||
|
<script src="static/js/mediafiles/mediafiles.js"></script>
|
@ -10,4 +10,11 @@ urlpatterns = patterns(
|
|||||||
url(r'^core/version/$',
|
url(r'^core/version/$',
|
||||||
views.VersionView.as_view(),
|
views.VersionView.as_view(),
|
||||||
name='core_version'),
|
name='core_version'),
|
||||||
|
|
||||||
|
# View for the projectors are handelt by angular.
|
||||||
|
url(r'^projector.*$', views.ProjectorView.as_view()),
|
||||||
|
|
||||||
|
# Main entry point for all angular pages.
|
||||||
|
# Has to be the last entry in the urls.py
|
||||||
|
url(r'^.*$', views.IndexView.as_view()),
|
||||||
)
|
)
|
||||||
|
@ -43,6 +43,17 @@ class IndexView(utils_views.CSRFMixin, utils_views.View):
|
|||||||
return HttpResponse(content)
|
return HttpResponse(content)
|
||||||
|
|
||||||
|
|
||||||
|
class ProjectorView(utils_views.View):
|
||||||
|
"""
|
||||||
|
Access the projector.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def get(self, *args, **kwargs):
|
||||||
|
with open(finders.find('templates/projector.html')) as f:
|
||||||
|
content = f.read()
|
||||||
|
return HttpResponse(content)
|
||||||
|
|
||||||
|
|
||||||
class ProjectorViewSet(ReadOnlyModelViewSet):
|
class ProjectorViewSet(ReadOnlyModelViewSet):
|
||||||
"""
|
"""
|
||||||
API endpoint to list, retrieve and update the projector slide info.
|
API endpoint to list, retrieve and update the projector slide info.
|
||||||
|
@ -1,5 +1,17 @@
|
|||||||
angular.module('OpenSlidesApp.mediafiles', [])
|
angular.module('OpenSlidesApp.mediafiles', [])
|
||||||
|
|
||||||
|
.factory('Mediafile', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'mediafiles/mediafile',
|
||||||
|
endpoint: '/rest/mediafiles/mediafile/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(Mediafile) {});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.mediafiles.site', ['OpenSlidesApp.mediafiles'])
|
||||||
|
|
||||||
.config(function($stateProvider) {
|
.config(function($stateProvider) {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('mediafiles', {
|
.state('mediafiles', {
|
||||||
@ -26,13 +38,6 @@ angular.module('OpenSlidesApp.mediafiles', [])
|
|||||||
});
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('Mediafile', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'mediafiles/mediafile',
|
|
||||||
endpoint: '/rest/mediafiles/mediafile/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('MediafileListCtrl', function($scope, $http, Mediafile) {
|
.controller('MediafileListCtrl', function($scope, $http, Mediafile) {
|
||||||
Mediafile.bindAll({}, $scope, 'mediafiles');
|
Mediafile.bindAll({}, $scope, 'mediafiles');
|
||||||
|
|
||||||
@ -80,4 +85,3 @@ angular.module('OpenSlidesApp.mediafiles', [])
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,29 @@
|
|||||||
angular.module('OpenSlidesApp.motions', [])
|
angular.module('OpenSlidesApp.motions', [])
|
||||||
|
|
||||||
|
.factory('Motion', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'motions/motion',
|
||||||
|
endpoint: '/rest/motions/motion/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.factory('Category', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'motions/category',
|
||||||
|
endpoint: '/rest/motions/category/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.factory('Workflow', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'motions/workflow',
|
||||||
|
endpoint: '/rest/motions/workflow/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(Motion, Category, Workflow) {});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.motions.site', ['OpenSlidesApp.motions'])
|
||||||
|
|
||||||
.config(function($stateProvider) {
|
.config(function($stateProvider) {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('motions', {
|
.state('motions', {
|
||||||
@ -104,25 +128,6 @@ angular.module('OpenSlidesApp.motions', [])
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('Motion', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'motions/motion',
|
|
||||||
endpoint: '/rest/motions/motion/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.factory('Category', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'motions/category',
|
|
||||||
endpoint: '/rest/motions/category/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.factory('Workflow', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'motions/workflow',
|
|
||||||
endpoint: '/rest/motions/workflow/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.controller('MotionListCtrl', function($scope, Motion) {
|
.controller('MotionListCtrl', function($scope, Motion) {
|
||||||
Motion.bindAll({}, $scope, 'motions');
|
Motion.bindAll({}, $scope, 'motions');
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
from django.conf.urls import include, patterns, url
|
from django.conf.urls import include, patterns, url
|
||||||
from django.views.generic import RedirectView
|
from django.views.generic import RedirectView
|
||||||
|
|
||||||
from openslides.core.views import IndexView
|
|
||||||
from openslides.utils.rest_api import router
|
from openslides.utils.rest_api import router
|
||||||
|
|
||||||
urlpatterns = patterns(
|
urlpatterns = patterns(
|
||||||
'',
|
'',
|
||||||
url(r'^(?P<url>.*[^/])$', RedirectView.as_view(url='/%(url)s/')),
|
url(r'^(?P<url>.*[^/])$', RedirectView.as_view(url='/%(url)s/')),
|
||||||
url(r'^rest/', include(router.urls)),
|
url(r'^rest/', include(router.urls)),
|
||||||
url(r'^', include('openslides.core.urls')),
|
|
||||||
url(r'^agenda/', include('openslides.agenda.urls')),
|
url(r'^agenda/', include('openslides.agenda.urls')),
|
||||||
url(r'^assignments/', include('openslides.assignments.urls')),
|
url(r'^assignments/', include('openslides.assignments.urls')),
|
||||||
url(r'^motions/', include('openslides.motions.urls')),
|
url(r'^motions/', include('openslides.motions.urls')),
|
||||||
url(r'^users/', include('openslides.users.urls')),
|
url(r'^users/', include('openslides.users.urls')),
|
||||||
url(r'^.*$', IndexView.as_view()),
|
|
||||||
|
# The urls.py for the core app has to be the last entry in the urls.py
|
||||||
|
url(r'^', include('openslides.core.urls')),
|
||||||
)
|
)
|
||||||
|
@ -12,33 +12,23 @@ class UserSlide(ProjectorElement):
|
|||||||
Slide definitions for user model.
|
Slide definitions for user model.
|
||||||
"""
|
"""
|
||||||
name = 'users/user'
|
name = 'users/user'
|
||||||
scripts = 'users/user_slide.js'
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
pk = self.config_entry.get('id')
|
pk = self.config_entry.get('id')
|
||||||
try:
|
if not User.objects.filter(pk=pk).exists():
|
||||||
user = User.objects.get(pk=pk)
|
|
||||||
except User.DoesNotExist:
|
|
||||||
raise ProjectorException(_('User does not exist.'))
|
raise ProjectorException(_('User does not exist.'))
|
||||||
result = [{
|
return {'id': pk}
|
||||||
'collection': 'users/user',
|
|
||||||
'id': pk}]
|
|
||||||
for group in user.groups.all():
|
|
||||||
result.append({
|
|
||||||
'collection': 'users/group',
|
|
||||||
'id': group.pk})
|
|
||||||
return result
|
|
||||||
|
|
||||||
def get_requirements(self, config_entry):
|
def get_requirements(self, config_entry):
|
||||||
self.config_entry = config_entry
|
pk = config_entry.get('id')
|
||||||
try:
|
if pk is not None:
|
||||||
context = self.get_context()
|
yield ProjectorRequirement(
|
||||||
except ProjectorException:
|
view_class=UserViewSet,
|
||||||
# User does not exist so just do nothing.
|
view_action='retrive',
|
||||||
pass
|
pk=pk)
|
||||||
else:
|
|
||||||
for item in context:
|
for group in User.objects.get(pk=pk).groups.all():
|
||||||
yield ProjectorRequirement(
|
yield ProjectorRequirement(
|
||||||
view_class=UserViewSet if item['collection'] == 'users/user' else GroupViewSet,
|
view_class=GroupViewSet,
|
||||||
view_action='retrieve',
|
view_action='retrieve',
|
||||||
pk=str(item['id']))
|
pk=str(group.pk))
|
||||||
|
@ -1,5 +1,56 @@
|
|||||||
angular.module('OpenSlidesApp.users', [])
|
angular.module('OpenSlidesApp.users', [])
|
||||||
|
|
||||||
|
.factory('User', function(DS, Group) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'users/user',
|
||||||
|
endpoint: '/rest/users/user/',
|
||||||
|
methods: {
|
||||||
|
get_short_name: function() {
|
||||||
|
// should be the same as in the python user model.
|
||||||
|
var firstName = _.trim(this.first_name),
|
||||||
|
lastName = _.trim(this.last_name),
|
||||||
|
name;
|
||||||
|
|
||||||
|
if (firstName && lastName) {
|
||||||
|
// TODO: check config
|
||||||
|
name = [firstName, lastName].join(' ');
|
||||||
|
} else {
|
||||||
|
name = firstName || lastName || this.username;
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
},
|
||||||
|
getPerms: function() {
|
||||||
|
var allPerms = [];
|
||||||
|
_.forEach(this.groups, function(groupId) {
|
||||||
|
// Get group from server
|
||||||
|
Group.find(groupId);
|
||||||
|
// But do not work with the returned promise, because in
|
||||||
|
// this case this method can not be called in $watch
|
||||||
|
group = Group.get(groupId);
|
||||||
|
if (group) {
|
||||||
|
_.forEach(group.permissions, function(perm) {
|
||||||
|
allPerms.push(perm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return _.uniq(allPerms);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.factory('Group', function(DS) {
|
||||||
|
return DS.defineResource({
|
||||||
|
name: 'users/group',
|
||||||
|
endpoint: '/rest/users/group/'
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
.run(function(User, Group) {});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.users.site', ['OpenSlidesApp.users'])
|
||||||
|
|
||||||
.config(function($stateProvider) {
|
.config(function($stateProvider) {
|
||||||
$stateProvider
|
$stateProvider
|
||||||
.state('users', {
|
.state('users', {
|
||||||
@ -126,52 +177,6 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
$rootScope.operator = operator;
|
$rootScope.operator = operator;
|
||||||
})
|
})
|
||||||
|
|
||||||
.factory('User', function(DS, Group) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'users/user',
|
|
||||||
endpoint: '/rest/users/user/',
|
|
||||||
methods: {
|
|
||||||
get_short_name: function() {
|
|
||||||
// should be the same as in the python user model.
|
|
||||||
var firstName = _.trim(this.first_name),
|
|
||||||
lastName = _.trim(this.last_name),
|
|
||||||
name;
|
|
||||||
|
|
||||||
if (firstName && lastName) {
|
|
||||||
// TODO: check config
|
|
||||||
name = [firstName, lastName].join(' ');
|
|
||||||
} else {
|
|
||||||
name = firstName || lastName || this.username;
|
|
||||||
}
|
|
||||||
return name;
|
|
||||||
},
|
|
||||||
getPerms: function() {
|
|
||||||
var allPerms = [];
|
|
||||||
_.forEach(this.groups, function(groupId) {
|
|
||||||
// Get group from server
|
|
||||||
Group.find(groupId);
|
|
||||||
// But do not work with the returned promise, because in
|
|
||||||
// this case this method can not be called in $watch
|
|
||||||
group = Group.get(groupId);
|
|
||||||
if (group) {
|
|
||||||
_.forEach(group.permissions, function(perm) {
|
|
||||||
allPerms.push(perm);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return _.uniq(allPerms);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
.factory('Group', function(DS) {
|
|
||||||
return DS.defineResource({
|
|
||||||
name: 'users/group',
|
|
||||||
endpoint: '/rest/users/group/'
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Directive to check for permissions
|
* Directive to check for permissions
|
||||||
*
|
*
|
||||||
@ -239,7 +244,7 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
};
|
};
|
||||||
}])
|
}])
|
||||||
|
|
||||||
.controller('UserListCtrl', function($scope, User) {
|
.controller('UserListCtrl', function($scope, User, projectorActivate) {
|
||||||
User.bindAll({}, $scope, 'users');
|
User.bindAll({}, $scope, 'users');
|
||||||
|
|
||||||
// setup table sorting
|
// setup table sorting
|
||||||
@ -264,6 +269,10 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
$scope.delete = function (user) {
|
$scope.delete = function (user) {
|
||||||
User.destroy(user.id);
|
User.destroy(user.id);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
$scope.project = function(user) {
|
||||||
|
projectorActivate(User, user.id).error(function() {console.log('success')});
|
||||||
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
.controller('UserDetailCtrl', function($scope, User, user) {
|
.controller('UserDetailCtrl', function($scope, User, user) {
|
||||||
@ -369,9 +378,25 @@ angular.module('OpenSlidesApp.users', [])
|
|||||||
// DS.flush();
|
// DS.flush();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.users.projector', ['OpenSlidesApp.users'])
|
||||||
|
|
||||||
|
.config(function(slidesProvider) {
|
||||||
|
slidesProvider.registerSlide('users/user', {
|
||||||
|
template: 'static/templates/users/slide_user.html',
|
||||||
|
});
|
||||||
})
|
})
|
||||||
|
|
||||||
|
.controller('SlideUserCtr', function($scope, User) {
|
||||||
|
// Attention! Each object that is used here has to be dealt on server side.
|
||||||
|
// Add it to the coresponding get_requirements method of the ProjectorElement
|
||||||
|
// class.
|
||||||
|
var id = $scope.element.context.id;
|
||||||
|
User.find(id);
|
||||||
|
User.bindOne(id, $scope, 'user');
|
||||||
|
});
|
||||||
|
|
||||||
// this is code from angular.js. Find a way to call this function from this file
|
// this is code from angular.js. Find a way to call this function from this file
|
||||||
function getBlockNodes(nodes) {
|
function getBlockNodes(nodes) {
|
||||||
|
3
openslides/users/static/templates/users/slide_user.html
Normal file
3
openslides/users/static/templates/users/slide_user.html
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<div ng-controller="SlideUserCtr">
|
||||||
|
<h1>{{ user.username }}</h1>
|
||||||
|
</div>
|
@ -106,7 +106,8 @@
|
|||||||
<td os-perms="users.can_manage" class="optional">{{ user.last_login | date:'yyyy-MM-dd HH:mm:ss'}}
|
<td os-perms="users.can_manage" class="optional">{{ user.last_login | date:'yyyy-MM-dd HH:mm:ss'}}
|
||||||
<td os-perms="users.can_manage core.can_manage_projector" class="nobr">
|
<td os-perms="users.can_manage core.can_manage_projector" class="nobr">
|
||||||
<!-- projector, TODO: add link to activate slidea-->
|
<!-- projector, TODO: add link to activate slidea-->
|
||||||
<a href="#TODO" os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
|
<a os-perms="core.can_manage_projector" class="btn btn-default btn-sm"
|
||||||
|
ng-click="project(user)"
|
||||||
title="{{ 'Show' | translate }}">
|
title="{{ 'Show' | translate }}">
|
||||||
<i class="fa fa-video-camera"></i>
|
<i class="fa fa-video-camera"></i>
|
||||||
</a>
|
</a>
|
||||||
|
@ -8,13 +8,12 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
|
|||||||
Base class for an element on the projector.
|
Base class for an element on the projector.
|
||||||
|
|
||||||
Every app which wants to add projector elements has to create classes
|
Every app which wants to add projector elements has to create classes
|
||||||
subclassing from this base class with different names. The name and
|
subclassing from this base class with different names. The name attribute
|
||||||
scripts attributes have to be set. The metaclass
|
has to be set. The metaclass (SignalConnectMetaClass) does the rest of the
|
||||||
(SignalConnectMetaClass) does the rest of the magic.
|
magic.
|
||||||
"""
|
"""
|
||||||
signal = Signal()
|
signal = Signal()
|
||||||
name = None
|
name = None
|
||||||
scripts = None
|
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
@ -45,20 +44,8 @@ class ProjectorElement(object, metaclass=SignalConnectMetaClass):
|
|||||||
assert self.config_entry.get('name') == self.name, (
|
assert self.config_entry.get('name') == self.name, (
|
||||||
'To get data of a projector element, the correct config entry has to be given.')
|
'To get data of a projector element, the correct config entry has to be given.')
|
||||||
return {
|
return {
|
||||||
'scripts': self.get_scripts(),
|
|
||||||
'context': self.get_context()}
|
'context': self.get_context()}
|
||||||
|
|
||||||
def get_scripts(self):
|
|
||||||
"""
|
|
||||||
Returns ...?
|
|
||||||
"""
|
|
||||||
# TODO: Write docstring
|
|
||||||
if self.scripts is None:
|
|
||||||
raise NotImplementedError(
|
|
||||||
'A projector element class must define either a '
|
|
||||||
'get_scripts method or have a scripts argument.')
|
|
||||||
return self.scripts
|
|
||||||
|
|
||||||
def get_context(self):
|
def get_context(self):
|
||||||
"""
|
"""
|
||||||
Returns the context of the projector element.
|
Returns the context of the projector element.
|
||||||
|
@ -23,13 +23,11 @@ class ProjectorAPI(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(json.loads(response.content.decode()), {
|
self.assertEqual(json.loads(response.content.decode()), {
|
||||||
|
'id': 1,
|
||||||
'config': [{'name': 'core/customslide', 'id': customslide.id}],
|
'config': [{'name': 'core/customslide', 'id': customslide.id}],
|
||||||
'projector_elements': [
|
'elements': [
|
||||||
{'name': 'core/customslide',
|
{'name': 'core/customslide',
|
||||||
'scripts': 'core/customslide_slide.js',
|
'context': {'id': customslide.id}}]})
|
||||||
'context': [
|
|
||||||
{'collection': 'core/customslide',
|
|
||||||
'id': customslide.id}]}]})
|
|
||||||
|
|
||||||
def test_invalid_slide_on_default_projector(self):
|
def test_invalid_slide_on_default_projector(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
@ -41,8 +39,9 @@ class ProjectorAPI(TestCase):
|
|||||||
|
|
||||||
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
self.assertEqual(json.loads(response.content.decode()), {
|
self.assertEqual(json.loads(response.content.decode()), {
|
||||||
|
'id': 1,
|
||||||
'config': [{'name': 'invalid_slide'}],
|
'config': [{'name': 'invalid_slide'}],
|
||||||
'projector_elements': [
|
'elements': [
|
||||||
{'name': 'invalid_slide',
|
{'name': 'invalid_slide',
|
||||||
'error': 'Projector element does not exist.'}]})
|
'error': 'Projector element does not exist.'}]})
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user