commit
624fcc663b
@ -96,6 +96,7 @@ Other:
|
|||||||
- Added new caching system with support for Redis.
|
- Added new caching system with support for Redis.
|
||||||
- Support https as websocket protocol (wss).
|
- Support https as websocket protocol (wss).
|
||||||
- Added migration path from 2.0.
|
- Added migration path from 2.0.
|
||||||
|
- Accelerated startup process (send all data to the client after login).
|
||||||
|
|
||||||
|
|
||||||
Version 2.0 (2016-04-18)
|
Version 2.0 (2016-04-18)
|
||||||
|
@ -35,3 +35,7 @@ class AgendaAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Item').get_collection_string(), ItemViewSet)
|
router.register(self.get_model('Item').get_collection_string(), ItemViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
return [Collection(self.get_model('Item').get_collection_string())]
|
||||||
|
@ -24,3 +24,7 @@ class AssignmentsAppConfig(AppConfig):
|
|||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)
|
router.register(self.get_model('Assignment').get_collection_string(), AssignmentViewSet)
|
||||||
router.register('assignments/poll', AssignmentPollViewSet)
|
router.register('assignments/poll', AssignmentPollViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
return [Collection(self.get_model('Assignment').get_collection_string())]
|
||||||
|
@ -14,10 +14,10 @@ class CoreAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Import all required stuff.
|
# Import all required stuff.
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
from openslides.core.config import config
|
from .config import config
|
||||||
from openslides.core.signals import post_permission_creation
|
from .signals import post_permission_creation
|
||||||
from openslides.utils.rest_api import router
|
from ..utils.rest_api import router
|
||||||
from openslides.utils.search import index_add_instance, index_del_instance
|
from ..utils.search import index_add_instance, index_del_instance
|
||||||
from .config_variables import get_config_variables
|
from .config_variables import get_config_variables
|
||||||
from .signals import delete_django_app_permissions
|
from .signals import delete_django_app_permissions
|
||||||
from .views import (
|
from .views import (
|
||||||
@ -55,3 +55,10 @@ class CoreAppConfig(AppConfig):
|
|||||||
signals.m2m_changed.connect(
|
signals.m2m_changed.connect(
|
||||||
index_add_instance,
|
index_add_instance,
|
||||||
dispatch_uid='m2m_index_add_instance')
|
dispatch_uid='m2m_index_add_instance')
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from .config import config
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
for model in ('Projector', 'ChatMessage', 'Tag', 'ProjectorMessage', 'Countdown'):
|
||||||
|
yield Collection(self.get_model(model).get_collection_string())
|
||||||
|
yield Collection(config.get_collection_string())
|
||||||
|
@ -129,6 +129,25 @@ img {
|
|||||||
margin: 0 auto 0 auto;
|
margin: 0 auto 0 auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#startup-overlay {
|
||||||
|
display: table;
|
||||||
|
background-color: #fff;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
#startup-overlay h1 {
|
||||||
|
font-size: 56px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#startup-overlay > div {
|
||||||
|
display: table-cell;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
/** Header **/
|
/** Header **/
|
||||||
#header {
|
#header {
|
||||||
float: left;
|
float: left;
|
||||||
@ -138,12 +157,6 @@ img {
|
|||||||
color: #999;
|
color: #999;
|
||||||
}
|
}
|
||||||
|
|
||||||
#header div.unconnected {
|
|
||||||
border: 1px solid red;
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1000;
|
|
||||||
}
|
|
||||||
#header a.headerlink {
|
#header a.headerlink {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
@ -53,13 +53,12 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
|
|
||||||
.factory('autoupdate', [
|
.factory('autoupdate', [
|
||||||
'DS',
|
'DS',
|
||||||
'$rootScope',
|
|
||||||
'REALM',
|
'REALM',
|
||||||
'ProjectorID',
|
'ProjectorID',
|
||||||
function (DS, $rootScope, REALM, ProjectorID) {
|
'$q',
|
||||||
|
function (DS, REALM, ProjectorID, $q) {
|
||||||
var socket = null;
|
var socket = null;
|
||||||
var recInterval = null;
|
var recInterval = null;
|
||||||
$rootScope.connected = false;
|
|
||||||
|
|
||||||
var websocketProtocol;
|
var websocketProtocol;
|
||||||
if (location.protocol == 'https:') {
|
if (location.protocol == 'https:') {
|
||||||
@ -69,50 +68,83 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
}
|
}
|
||||||
|
|
||||||
var websocketPath;
|
var websocketPath;
|
||||||
if (REALM == 'site') {
|
if (REALM === 'site') {
|
||||||
websocketPath = '/ws/site/';
|
websocketPath = '/ws/site/';
|
||||||
} else if (REALM == 'projector') {
|
} else if (REALM === 'projector') {
|
||||||
websocketPath = '/ws/projector/' + ProjectorID() + '/';
|
websocketPath = '/ws/projector/' + ProjectorID() + '/';
|
||||||
} else {
|
} else {
|
||||||
console.error('The constant REALM is not set properly.');
|
console.error('The constant REALM is not set properly.');
|
||||||
}
|
}
|
||||||
|
|
||||||
var Autoupdate = {
|
var Autoupdate = {};
|
||||||
messageReceivers: [],
|
Autoupdate.messageReceivers = [];
|
||||||
onMessage: function (receiver) {
|
// We use later a promise to defer the first message of the established ws connection.
|
||||||
this.messageReceivers.push(receiver);
|
Autoupdate.firstMessageDeferred = $q.defer();
|
||||||
},
|
Autoupdate.onMessage = function (receiver) {
|
||||||
reconnect: function () {
|
Autoupdate.messageReceivers.push(receiver);
|
||||||
|
};
|
||||||
|
Autoupdate.reconnect = function () {
|
||||||
if (socket) {
|
if (socket) {
|
||||||
socket.close();
|
socket.close();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
var newConnect = function () {
|
Autoupdate.newConnect = function () {
|
||||||
socket = new WebSocket(websocketProtocol + '//' + location.host + websocketPath);
|
socket = new WebSocket(websocketProtocol + '//' + location.host + websocketPath);
|
||||||
clearInterval(recInterval);
|
clearInterval(recInterval);
|
||||||
socket.onopen = function () {
|
|
||||||
$rootScope.connected = true;
|
|
||||||
};
|
|
||||||
socket.onclose = function () {
|
socket.onclose = function () {
|
||||||
$rootScope.connected = false;
|
|
||||||
socket = null;
|
socket = null;
|
||||||
recInterval = setInterval(function () {
|
recInterval = setInterval(function () {
|
||||||
newConnect();
|
Autoupdate.newConnect();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
};
|
};
|
||||||
socket.onmessage = function (event) {
|
socket.onmessage = function (event) {
|
||||||
_.forEach(Autoupdate.messageReceivers, function (receiver) {
|
_.forEach(Autoupdate.messageReceivers, function (receiver) {
|
||||||
receiver(event.data);
|
receiver(event.data);
|
||||||
});
|
});
|
||||||
|
// The first message is done: resolve the promise.
|
||||||
|
// TODO: check whether the promise is already resolved.
|
||||||
|
Autoupdate.firstMessageDeferred.resolve();
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
newConnect();
|
|
||||||
return Autoupdate;
|
return Autoupdate;
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
|
.factory('operator', [
|
||||||
|
'User',
|
||||||
|
'Group',
|
||||||
|
function (User, Group) {
|
||||||
|
var operator = {
|
||||||
|
user: null,
|
||||||
|
perms: [],
|
||||||
|
isAuthenticated: function () {
|
||||||
|
return !!this.user;
|
||||||
|
},
|
||||||
|
setUser: function(user_id, user_data) {
|
||||||
|
if (user_id && user_data) {
|
||||||
|
operator.user = User.inject(user_data);
|
||||||
|
operator.perms = operator.user.getPerms();
|
||||||
|
} else {
|
||||||
|
operator.user = null;
|
||||||
|
operator.perms = Group.get(1).permissions;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Returns true if the operator has at least one perm of the perms-list.
|
||||||
|
hasPerms: function(perms) {
|
||||||
|
if (typeof perms === 'string') {
|
||||||
|
perms = perms.split(' ');
|
||||||
|
}
|
||||||
|
return _.intersection(perms, operator.perms).length > 0;
|
||||||
|
},
|
||||||
|
// Returns true if the operator is a member of group.
|
||||||
|
isInGroup: function(group) {
|
||||||
|
return _.indexOf(operator.user.groups_id, group.id) > -1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return operator;
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
// gets all in OpenSlides available languages
|
// gets all in OpenSlides available languages
|
||||||
.factory('Languages', [
|
.factory('Languages', [
|
||||||
'gettext',
|
'gettext',
|
||||||
@ -230,7 +262,7 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
var deletedElements = [];
|
var deletedElements = [];
|
||||||
var collectionString = key;
|
var collectionString = key;
|
||||||
_.forEach(list, function(data) {
|
_.forEach(list, function(data) {
|
||||||
// uncomment this line for debugging to log all autoupdates:
|
// Uncomment this line for debugging to log all autoupdates:
|
||||||
// console.log("Received object: " + data.collection + ", " + data.id);
|
// console.log("Received object: " + data.collection + ", " + data.id);
|
||||||
|
|
||||||
// remove (=eject) object from local DS store
|
// remove (=eject) object from local DS store
|
||||||
@ -301,39 +333,7 @@ angular.module('OpenSlidesApp.core', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
.factory('loadGlobalData', [
|
|
||||||
'ChatMessage',
|
|
||||||
'Config',
|
|
||||||
'Projector',
|
|
||||||
'ProjectorMessage',
|
|
||||||
'Countdown',
|
|
||||||
function (ChatMessage, Config, Projector, ProjectorMessage, Countdown) {
|
|
||||||
return function () {
|
|
||||||
Config.findAll();
|
|
||||||
|
|
||||||
// Loads all projector data and the projectiondefaults
|
|
||||||
Projector.findAll();
|
|
||||||
ProjectorMessage.findAll();
|
|
||||||
Countdown.findAll();
|
|
||||||
|
|
||||||
// Loads all chat messages data and their user_ids
|
|
||||||
// TODO: add permission check if user has required chat permission
|
|
||||||
// error if include 'operator' here:
|
|
||||||
// "Circular dependency found: loadGlobalData <- operator <- loadGlobalData"
|
|
||||||
//if (operator.hasPerms("core.can_use_chat")) {
|
|
||||||
ChatMessage.findAll().then( function(chatmessages) {
|
|
||||||
angular.forEach(chatmessages, function (chatmessage) {
|
|
||||||
ChatMessage.loadRelations(chatmessage, 'user');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
//}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
// Template hooks
|
// Template hooks
|
||||||
|
|
||||||
.factory('templateHooks', [
|
.factory('templateHooks', [
|
||||||
function () {
|
function () {
|
||||||
var hooks = {};
|
var hooks = {};
|
||||||
|
@ -8,6 +8,13 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
|||||||
// Can be used to find out if the projector or the side is used
|
// Can be used to find out if the projector or the side is used
|
||||||
.constant('REALM', 'projector')
|
.constant('REALM', 'projector')
|
||||||
|
|
||||||
|
.run([
|
||||||
|
'autoupdate',
|
||||||
|
function (autoupdate) {
|
||||||
|
autoupdate.newConnect();
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
// Provider to register slides in a .config() statement.
|
// Provider to register slides in a .config() statement.
|
||||||
.provider('slides', [
|
.provider('slides', [
|
||||||
function() {
|
function() {
|
||||||
@ -84,10 +91,8 @@ angular.module('OpenSlidesApp.core.projector', ['OpenSlidesApp.core'])
|
|||||||
'$scope',
|
'$scope',
|
||||||
'$location',
|
'$location',
|
||||||
'gettext',
|
'gettext',
|
||||||
'loadGlobalData',
|
|
||||||
'Projector',
|
'Projector',
|
||||||
function($scope, $location, gettext, loadGlobalData, Projector) {
|
function($scope, $location, gettext, Projector) {
|
||||||
loadGlobalData();
|
|
||||||
$scope.error = '';
|
$scope.error = '';
|
||||||
|
|
||||||
// watch for changes in Projector
|
// watch for changes in Projector
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
// The core module for the OpenSlides site
|
// The core module for the OpenSlides site
|
||||||
angular.module('OpenSlidesApp.core.site', [
|
angular.module('OpenSlidesApp.core.site', [
|
||||||
'OpenSlidesApp.core',
|
'OpenSlidesApp.core',
|
||||||
|
'OpenSlidesApp.core.start',
|
||||||
'OpenSlidesApp.poll.majority',
|
'OpenSlidesApp.poll.majority',
|
||||||
'ui.router',
|
'ui.router',
|
||||||
'angular-loading-bar',
|
'angular-loading-bar',
|
||||||
@ -76,10 +77,7 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
this.$get = ['operator', function(operator) {
|
this.$get = ['operator', function(operator) {
|
||||||
return {
|
return {
|
||||||
registerScope: function (scope) {
|
registerScope: function (scope) {
|
||||||
var that = this;
|
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.updateMainMenu();
|
|
||||||
operator.onOperatorChange(function () {that.updateMainMenu();});
|
|
||||||
},
|
},
|
||||||
updateMainMenu: function () {
|
updateMainMenu: function () {
|
||||||
this.scope.elements = this.getElements();
|
this.scope.elements = this.getElements();
|
||||||
@ -99,15 +97,6 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// Load the global data when the operator changes
|
|
||||||
.run([
|
|
||||||
'loadGlobalData',
|
|
||||||
'operator',
|
|
||||||
function (loadGlobalData, operator) {
|
|
||||||
operator.onOperatorChange(loadGlobalData);
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.run([
|
.run([
|
||||||
'editableOptions',
|
'editableOptions',
|
||||||
'gettext',
|
'gettext',
|
||||||
@ -615,14 +604,6 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// Load the global data on startup
|
|
||||||
.run([
|
|
||||||
'loadGlobalData',
|
|
||||||
function(loadGlobalData) {
|
|
||||||
loadGlobalData();
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
// html-tag os-form-field to generate generic from fields
|
// html-tag os-form-field to generate generic from fields
|
||||||
// TODO: make it possible to use other fields then config fields
|
// TODO: make it possible to use other fields then config fields
|
||||||
.directive('osFormField', [
|
.directive('osFormField', [
|
||||||
@ -676,28 +657,6 @@ angular.module('OpenSlidesApp.core.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
.directive('routeLoadingIndicator', [
|
|
||||||
'$rootScope',
|
|
||||||
'$state',
|
|
||||||
'gettext',
|
|
||||||
function($rootScope, $state, gettext) {
|
|
||||||
gettext('Loading ...');
|
|
||||||
return {
|
|
||||||
restrict: 'E',
|
|
||||||
template: "<div class='header spacer-bottom' ng-if='isRouteLoading'><div class='title'><h1><translate>Loading ...</translate> <i class='fa fa-spinner fa-pulse'></i></h1></div></div>",
|
|
||||||
link: function(scope, elem, attrs) {
|
|
||||||
scope.isRouteLoading = false;
|
|
||||||
$rootScope.$on('$stateChangeStart', function() {
|
|
||||||
scope.isRouteLoading = true;
|
|
||||||
});
|
|
||||||
$rootScope.$on('$stateChangeSuccess', function() {
|
|
||||||
scope.isRouteLoading = false;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
/* This directive provides a csv import template.
|
/* This directive provides a csv import template.
|
||||||
* Papa Parse is used to parse the csv file. Accepted attributes:
|
* Papa Parse is used to parse the csv file. Accepted attributes:
|
||||||
* * change:
|
* * change:
|
||||||
|
35
openslides/core/static/js/core/start.js
Normal file
35
openslides/core/static/js/core/start.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
(function () {
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
angular.module('OpenSlidesApp.core.start', [])
|
||||||
|
|
||||||
|
.run([
|
||||||
|
'$http',
|
||||||
|
'$rootScope',
|
||||||
|
'$state',
|
||||||
|
'autoupdate',
|
||||||
|
'operator',
|
||||||
|
'Group',
|
||||||
|
'mainMenu',
|
||||||
|
function($http, $rootScope, $state, autoupdate, operator, Group, mainMenu) {
|
||||||
|
$rootScope.startupWaitingEnabled = true;
|
||||||
|
$http.get('/users/whoami/').success(function(data) {
|
||||||
|
$rootScope.guest_enabled = data.guest_enabled;
|
||||||
|
if (data.user_id === null && !data.guest_enabled) {
|
||||||
|
// Redirect to login dialog if user is not logged in.
|
||||||
|
$state.go('login', {guest_enabled: data.guest_enabled});
|
||||||
|
} else {
|
||||||
|
autoupdate.newConnect();
|
||||||
|
autoupdate.firstMessageDeferred.promise.then(function () {
|
||||||
|
operator.setUser(data.user_id, data.user);
|
||||||
|
$rootScope.operator = operator;
|
||||||
|
mainMenu.updateMainMenu();
|
||||||
|
$rootScope.startupWaitingEnabled = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
|
||||||
|
}());
|
@ -20,9 +20,15 @@
|
|||||||
|
|
||||||
<div id="wrapper" ng-cloak>
|
<div id="wrapper" ng-cloak>
|
||||||
|
|
||||||
|
<!-- please wait -->
|
||||||
|
<div id="startup-overlay" ng-if="startupWaitingEnabled">
|
||||||
|
<div>
|
||||||
|
<h1><i class="fa fa-spinner fa-pulse"></i></h1>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Header -->
|
<!-- Header -->
|
||||||
<div id="header">
|
<div id="header">
|
||||||
<div ng-hide="connected" class="unconnected"></div>
|
|
||||||
<div class="containerOS">
|
<div class="containerOS">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
<div class="title">
|
<div class="title">
|
||||||
@ -174,8 +180,7 @@
|
|||||||
<div class="containerOS">
|
<div class="containerOS">
|
||||||
<div class="col1" ng-class="isProjectorSidebar ? 'min' : 'max'">
|
<div class="col1" ng-class="isProjectorSidebar ? 'min' : 'max'">
|
||||||
<!-- dynamic views -->
|
<!-- dynamic views -->
|
||||||
<route-loading-indicator></route-loading-indicator>
|
<div ui-view></div>
|
||||||
<div ui-view ng-if="!isRouteLoading"></div>
|
|
||||||
<!-- footer -->
|
<!-- footer -->
|
||||||
<div id="footer">
|
<div id="footer">
|
||||||
© Copyright by <a href="http://www.openslides.org" target="_blank">OpenSlides</a> |
|
© Copyright by <a href="http://www.openslides.org" target="_blank">OpenSlides</a> |
|
||||||
|
@ -18,3 +18,7 @@ class MediafilesAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet)
|
router.register(self.get_model('Mediafile').get_collection_string(), MediafileViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
return [Collection(self.get_model('Mediafile').get_collection_string())]
|
||||||
|
@ -34,3 +34,8 @@ class MotionsAppConfig(AppConfig):
|
|||||||
router.register(self.get_model('MotionChangeRecommendation').get_collection_string(),
|
router.register(self.get_model('MotionChangeRecommendation').get_collection_string(),
|
||||||
MotionChangeRecommendationViewSet)
|
MotionChangeRecommendationViewSet)
|
||||||
router.register('motions/motionpoll', MotionPollViewSet)
|
router.register('motions/motionpoll', MotionPollViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
for model in ('Category', 'Motion', 'MotionBlock', 'Workflow', 'MotionChangeRecommendation'):
|
||||||
|
yield Collection(self.get_model(model).get_collection_string())
|
||||||
|
@ -198,14 +198,14 @@ angular.module('OpenSlidesApp.motions', [
|
|||||||
'jsDataModel',
|
'jsDataModel',
|
||||||
'gettext',
|
'gettext',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'operator',
|
|
||||||
'Config',
|
'Config',
|
||||||
'lineNumberingService',
|
'lineNumberingService',
|
||||||
'diffService',
|
'diffService',
|
||||||
'OpenSlidesSettings',
|
'OpenSlidesSettings',
|
||||||
'Projector',
|
'Projector',
|
||||||
|
'operator',
|
||||||
function(DS, $http, MotionPoll, MotionChangeRecommendation, MotionComment, jsDataModel, gettext, gettextCatalog,
|
function(DS, $http, MotionPoll, MotionChangeRecommendation, MotionComment, jsDataModel, gettext, gettextCatalog,
|
||||||
operator, Config, lineNumberingService, diffService, OpenSlidesSettings, Projector) {
|
Config, lineNumberingService, diffService, OpenSlidesSettings, Projector, operator) {
|
||||||
var name = 'motions/motion';
|
var name = 'motions/motion';
|
||||||
return DS.defineResource({
|
return DS.defineResource({
|
||||||
name: name,
|
name: name,
|
||||||
|
@ -285,14 +285,6 @@ angular.module('OpenSlidesApp.motions.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
// Load all MotionWorkflows at startup
|
|
||||||
.run([
|
|
||||||
'Workflow',
|
|
||||||
function (Workflow) {
|
|
||||||
Workflow.findAll();
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('ChangeRecommendationForm', [
|
.factory('ChangeRecommendationForm', [
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'Editor',
|
'Editor',
|
||||||
|
@ -18,3 +18,7 @@ class TopicsAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('Topic').get_collection_string(), TopicViewSet)
|
router.register(self.get_model('Topic').get_collection_string(), TopicViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
return [Collection(self.get_model('Topic').get_collection_string())]
|
||||||
|
@ -42,6 +42,8 @@ class UserAccessPermissions(BaseAccessPermissions):
|
|||||||
case = MANY_DATA
|
case = MANY_DATA
|
||||||
else:
|
else:
|
||||||
case = LITTLE_DATA
|
case = LITTLE_DATA
|
||||||
|
elif user.pk == full_data.get('id'):
|
||||||
|
case = LITTLE_DATA
|
||||||
else:
|
else:
|
||||||
case = NO_DATA
|
case = NO_DATA
|
||||||
|
|
||||||
|
@ -30,4 +30,9 @@ class UsersAppConfig(AppConfig):
|
|||||||
|
|
||||||
# Register viewsets.
|
# Register viewsets.
|
||||||
router.register(self.get_model('User').get_collection_string(), UserViewSet)
|
router.register(self.get_model('User').get_collection_string(), UserViewSet)
|
||||||
router.register('users/group', GroupViewSet)
|
router.register(self.get_model('Group').get_collection_string(), GroupViewSet)
|
||||||
|
|
||||||
|
def get_startup_elements(self):
|
||||||
|
from ..utils.collection import Collection
|
||||||
|
for model in ('User', 'Group'):
|
||||||
|
yield Collection(self.get_model(model).get_collection_string())
|
||||||
|
@ -4,66 +4,6 @@
|
|||||||
|
|
||||||
angular.module('OpenSlidesApp.users', [])
|
angular.module('OpenSlidesApp.users', [])
|
||||||
|
|
||||||
.factory('operator', [
|
|
||||||
'User',
|
|
||||||
'Group',
|
|
||||||
'loadGlobalData',
|
|
||||||
'autoupdate',
|
|
||||||
'DS',
|
|
||||||
function (User, Group, loadGlobalData, autoupdate, DS) {
|
|
||||||
var operatorChangeCallbacks = [autoupdate.reconnect];
|
|
||||||
var operator = {
|
|
||||||
user: null,
|
|
||||||
perms: [],
|
|
||||||
isAuthenticated: function () {
|
|
||||||
return !!this.user;
|
|
||||||
},
|
|
||||||
onOperatorChange: function (func) {
|
|
||||||
operatorChangeCallbacks.push(func);
|
|
||||||
},
|
|
||||||
setUser: function(user_id) {
|
|
||||||
if (user_id) {
|
|
||||||
User.find(user_id).then(function(user) {
|
|
||||||
operator.user = user;
|
|
||||||
// TODO: load only the needed groups
|
|
||||||
Group.findAll().then(function() {
|
|
||||||
operator.perms = user.getPerms();
|
|
||||||
_.forEach(operatorChangeCallbacks, function (callback) {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
operator.user = null;
|
|
||||||
operator.perms = [];
|
|
||||||
DS.clear();
|
|
||||||
_.forEach(operatorChangeCallbacks, function (callback) {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
Group.find(1).then(function(group) {
|
|
||||||
operator.perms = group.permissions;
|
|
||||||
_.forEach(operatorChangeCallbacks, function (callback) {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Returns true if the operator has at least one perm of the perms-list.
|
|
||||||
hasPerms: function(perms) {
|
|
||||||
if (typeof perms == 'string') {
|
|
||||||
perms = perms.split(' ');
|
|
||||||
}
|
|
||||||
return _.intersection(perms, operator.perms).length > 0;
|
|
||||||
},
|
|
||||||
// Returns true if the operator is a member of group.
|
|
||||||
isInGroup: function(group) {
|
|
||||||
return _.indexOf(operator.user.groups_id, group.id) > -1;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return operator;
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
.factory('User', [
|
.factory('User', [
|
||||||
'DS',
|
'DS',
|
||||||
'Group',
|
'Group',
|
||||||
|
@ -40,31 +40,13 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
abstract: true,
|
abstract: true,
|
||||||
template: "<ui-view/>",
|
template: "<ui-view/>",
|
||||||
})
|
})
|
||||||
.state('users.user.list', {
|
.state('users.user.list', {})
|
||||||
resolve: {
|
.state('users.user.create', {})
|
||||||
users: function(User) {
|
|
||||||
return User.findAll();
|
|
||||||
},
|
|
||||||
groups: function(Group) {
|
|
||||||
return Group.findAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('users.user.create', {
|
|
||||||
resolve: {
|
|
||||||
groups: function(Group) {
|
|
||||||
return Group.findAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.state('users.user.detail', {
|
.state('users.user.detail', {
|
||||||
resolve: {
|
resolve: {
|
||||||
user: function(User, $stateParams) {
|
userId: ['$stateParams', function($stateParams) {
|
||||||
return User.find($stateParams.id);
|
return $stateParams.id;
|
||||||
},
|
}]
|
||||||
groups: function(Group) {
|
|
||||||
return Group.findAll();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
// Redirects to user detail view and opens user edit form dialog, uses edit url.
|
// Redirects to user detail view and opens user edit form dialog, uses edit url.
|
||||||
@ -72,8 +54,8 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
// (from users list controller use UserForm factory instead to open dialog in front of
|
// (from users list controller use UserForm factory instead to open dialog in front of
|
||||||
// current view without redirect)
|
// current view without redirect)
|
||||||
.state('users.user.detail.update', {
|
.state('users.user.detail.update', {
|
||||||
onEnter: ['$stateParams', '$state', 'ngDialog', 'User',
|
onEnter: ['$stateParams', '$state', 'ngDialog',
|
||||||
function($stateParams, $state, ngDialog, User) {
|
function($stateParams, $state, ngDialog) {
|
||||||
ngDialog.open({
|
ngDialog.open({
|
||||||
template: 'static/templates/users/user-form.html',
|
template: 'static/templates/users/user-form.html',
|
||||||
controller: 'UserUpdateCtrl',
|
controller: 'UserUpdateCtrl',
|
||||||
@ -81,7 +63,7 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
closeByEscape: false,
|
closeByEscape: false,
|
||||||
closeByDocument: false,
|
closeByDocument: false,
|
||||||
resolve: {
|
resolve: {
|
||||||
user: function() {return User.find($stateParams.id);}
|
userId: function() {return $stateParams.id;}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -106,22 +88,14 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
controller: 'UserChangePasswordCtrl',
|
controller: 'UserChangePasswordCtrl',
|
||||||
templateUrl: 'static/templates/users/user-change-password.html',
|
templateUrl: 'static/templates/users/user-change-password.html',
|
||||||
resolve: {
|
resolve: {
|
||||||
user: function(User, $stateParams) {
|
userId: ['$stateParams', function($stateParams) {
|
||||||
return User.find($stateParams.id);
|
return $stateParams.id;
|
||||||
}
|
}]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.state('users.user.import', {
|
.state('users.user.import', {
|
||||||
url: '/import',
|
url: '/import',
|
||||||
controller: 'UserImportCtrl',
|
controller: 'UserImportCtrl',
|
||||||
resolve: {
|
|
||||||
groups: function(Group) {
|
|
||||||
return Group.findAll();
|
|
||||||
},
|
|
||||||
users: function(User) {
|
|
||||||
return User.findAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
// groups
|
// groups
|
||||||
.state('users.group', {
|
.state('users.group', {
|
||||||
@ -134,9 +108,6 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
})
|
})
|
||||||
.state('users.group.list', {
|
.state('users.group.list', {
|
||||||
resolve: {
|
resolve: {
|
||||||
groups: function(Group) {
|
|
||||||
return Group.findAll();
|
|
||||||
},
|
|
||||||
permissions: function(Group) {
|
permissions: function(Group) {
|
||||||
return Group.getPermissions();
|
return Group.getPermissions();
|
||||||
}
|
}
|
||||||
@ -162,28 +133,6 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
.run([
|
|
||||||
'operator',
|
|
||||||
'$rootScope',
|
|
||||||
'$http',
|
|
||||||
'$state',
|
|
||||||
'Group',
|
|
||||||
function(operator, $rootScope, $http, $state, Group) {
|
|
||||||
// Put the operator into the root scope
|
|
||||||
$http.get('/users/whoami/').success(function(data) {
|
|
||||||
operator.setUser(data.user_id);
|
|
||||||
$rootScope.guest_enabled = data.guest_enabled;
|
|
||||||
if (data.user_id === null && !data.guest_enabled) {
|
|
||||||
// redirect to login dialog if use is not logged in
|
|
||||||
$state.go('login', {guest_enabled: data.guest_enabled});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$rootScope.operator = operator;
|
|
||||||
// Load all Groups. They are needed later
|
|
||||||
Group.findAll();
|
|
||||||
}
|
|
||||||
])
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Directive to check for permissions
|
* Directive to check for permissions
|
||||||
*
|
*
|
||||||
@ -210,7 +159,7 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
}
|
}
|
||||||
$scope.$watch(
|
$scope.$watch(
|
||||||
function (scope) {
|
function (scope) {
|
||||||
return scope.operator.hasPerms(perms);
|
return scope.operator && scope.operator.hasPerms(perms);
|
||||||
},
|
},
|
||||||
function (value) {
|
function (value) {
|
||||||
if ($attr.osPerms[0] === '!') {
|
if ($attr.osPerms[0] === '!') {
|
||||||
@ -284,19 +233,15 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
return {
|
return {
|
||||||
// ngDialog for user form
|
// ngDialog for user form
|
||||||
getDialog: function (user) {
|
getDialog: function (user) {
|
||||||
var resolve;
|
|
||||||
if (user) {
|
|
||||||
resolve = {
|
|
||||||
user: function(User) {return User.find(user.id);}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
return {
|
||||||
template: 'static/templates/users/user-form.html',
|
template: 'static/templates/users/user-form.html',
|
||||||
controller: (user) ? 'UserUpdateCtrl' : 'UserCreateCtrl',
|
controller: (user) ? 'UserUpdateCtrl' : 'UserCreateCtrl',
|
||||||
className: 'ngdialog-theme-default wide-form',
|
className: 'ngdialog-theme-default wide-form',
|
||||||
closeByEscape: false,
|
closeByEscape: false,
|
||||||
closeByDocument: false,
|
closeByDocument: false,
|
||||||
resolve: (resolve) ? resolve : null
|
resolve: {
|
||||||
|
userId: function () {return user ? user.id : void 0;},
|
||||||
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// angular-formly fields for user form
|
// angular-formly fields for user form
|
||||||
@ -697,12 +642,12 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
'ngDialog',
|
'ngDialog',
|
||||||
'UserForm',
|
'UserForm',
|
||||||
'User',
|
'User',
|
||||||
'user',
|
'userId',
|
||||||
'Group',
|
'Group',
|
||||||
'Projector',
|
'Projector',
|
||||||
'ProjectionDefault',
|
'ProjectionDefault',
|
||||||
function($scope, ngDialog, UserForm, User, user, Group, Projector, ProjectionDefault) {
|
function($scope, ngDialog, UserForm, User, userId, Group, Projector, ProjectionDefault) {
|
||||||
User.bindOne(user.id, $scope, 'user');
|
User.bindOne(userId, $scope, 'user');
|
||||||
Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
|
Group.bindAll({where: {id: {'>': 1}}}, $scope, 'groups');
|
||||||
$scope.$watch(function () {
|
$scope.$watch(function () {
|
||||||
return Projector.lastModified();
|
return Projector.lastModified();
|
||||||
@ -760,13 +705,13 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
'User',
|
'User',
|
||||||
'UserForm',
|
'UserForm',
|
||||||
'Group',
|
'Group',
|
||||||
'user',
|
'userId',
|
||||||
function($scope, $state, $http, User, UserForm, Group, user) {
|
function($scope, $state, $http, User, UserForm, Group, userId) {
|
||||||
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
|
Group.bindAll({where: {id: {'>': 2}}}, $scope, 'groups');
|
||||||
$scope.alert = {};
|
$scope.alert = {};
|
||||||
// set initial values for form model by create deep copy of user object
|
// set initial values for form model by create deep copy of user object
|
||||||
// so list/detail view is not updated while editing
|
// so list/detail view is not updated while editing
|
||||||
$scope.model = angular.copy(user);
|
$scope.model = angular.copy(User.get(userId));
|
||||||
|
|
||||||
// get all form fields
|
// get all form fields
|
||||||
$scope.formFields = UserForm.getFormFields();
|
$scope.formFields = UserForm.getFormFields();
|
||||||
@ -825,11 +770,11 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
'$state',
|
'$state',
|
||||||
'$http',
|
'$http',
|
||||||
'User',
|
'User',
|
||||||
'user',
|
'userId',
|
||||||
'gettextCatalog',
|
'gettextCatalog',
|
||||||
'PasswordGenerator',
|
'PasswordGenerator',
|
||||||
function($scope, $state, $http, User, user, gettextCatalog, PasswordGenerator) {
|
function($scope, $state, $http, User, userId, gettextCatalog, PasswordGenerator) {
|
||||||
User.bindOne(user.id, $scope, 'user');
|
User.bindOne(userId, $scope, 'user');
|
||||||
$scope.alert={};
|
$scope.alert={};
|
||||||
$scope.generatePassword = function () {
|
$scope.generatePassword = function () {
|
||||||
$scope.new_password = PasswordGenerator.generate();
|
$scope.new_password = PasswordGenerator.generate();
|
||||||
@ -1173,7 +1118,7 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
$scope.groups.forEach(function (group) {
|
$scope.groups.forEach(function (group) {
|
||||||
if ((_.indexOf(group.permissions, 'users.can_see_name') > -1) &&
|
if ((_.indexOf(group.permissions, 'users.can_see_name') > -1) &&
|
||||||
(_.indexOf(group.permissions, 'users.can_manage') > -1)){
|
(_.indexOf(group.permissions, 'users.can_manage') > -1)){
|
||||||
if (operator.isInGroup(group)){
|
if (!operator.user || operator.isInGroup(group)){
|
||||||
groups_danger.push(group);
|
groups_danger.push(group);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1305,19 +1250,15 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
};
|
};
|
||||||
|
|
||||||
$scope.openDialog = function (group) {
|
$scope.openDialog = function (group) {
|
||||||
var resolve;
|
|
||||||
if (group) {
|
|
||||||
resolve = {
|
|
||||||
group: function() {return Group.find(group.id);}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
ngDialog.open({
|
ngDialog.open({
|
||||||
template: 'static/templates/users/group-edit.html',
|
template: 'static/templates/users/group-edit.html',
|
||||||
controller: group ? 'GroupRenameCtrl' : 'GroupCreateCtrl',
|
controller: group ? 'GroupRenameCtrl' : 'GroupCreateCtrl',
|
||||||
className: 'ngdialog-theme-default wide-form',
|
className: 'ngdialog-theme-default wide-form',
|
||||||
closeByEscape: false,
|
closeByEscape: false,
|
||||||
closeByDocument: false,
|
closeByDocument: false,
|
||||||
resolve: (resolve) ? resolve : null
|
resolve: {
|
||||||
|
group: function () {return group;},
|
||||||
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1402,10 +1343,15 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
'$rootScope',
|
'$rootScope',
|
||||||
'$scope',
|
'$scope',
|
||||||
'$http',
|
'$http',
|
||||||
|
'$state',
|
||||||
'$stateParams',
|
'$stateParams',
|
||||||
|
'$q',
|
||||||
'operator',
|
'operator',
|
||||||
'gettext',
|
'gettext',
|
||||||
function ($rootScope, $scope, $http, $stateParams, operator, gettext) {
|
'autoupdate',
|
||||||
|
'mainMenu',
|
||||||
|
'DS',
|
||||||
|
function ($rootScope, $scope, $http, $state, $stateParams, $q, operator, gettext, autoupdate, mainMenu, DS) {
|
||||||
$scope.alerts = [];
|
$scope.alerts = [];
|
||||||
|
|
||||||
// get login info-text from server
|
// get login info-text from server
|
||||||
@ -1441,11 +1387,19 @@ angular.module('OpenSlidesApp.users.site', [
|
|||||||
$http.post('/users/login/', data).then(
|
$http.post('/users/login/', data).then(
|
||||||
function (response) {
|
function (response) {
|
||||||
// Success: User logged in.
|
// Success: User logged in.
|
||||||
operator.setUser(response.data.user_id);
|
// Clear store and reset deferred first message, if guests was enabled before.
|
||||||
|
DS.clear();
|
||||||
|
autoupdate.firstMessageDeferred = $q.defer();
|
||||||
|
// The next lines are partly the same lines as in core/start.js
|
||||||
|
autoupdate.newConnect();
|
||||||
|
autoupdate.firstMessageDeferred.promise.then(function () {
|
||||||
|
operator.setUser(response.data.user_id, response.data.user);
|
||||||
|
$rootScope.operator = operator;
|
||||||
|
mainMenu.updateMainMenu();
|
||||||
$scope.closeThisDialog();
|
$scope.closeThisDialog();
|
||||||
setTimeout(function(){
|
$state.go('home');
|
||||||
window.location.replace('/');
|
$rootScope.startupWaitingEnabled = false;
|
||||||
}, 1000);
|
});
|
||||||
},
|
},
|
||||||
function (response) {
|
function (response) {
|
||||||
// Error: Username or password is not correct.
|
// Error: Username or password is not correct.
|
||||||
|
@ -5,6 +5,7 @@ from django.utils.encoding import force_text
|
|||||||
from django.utils.translation import ugettext as _
|
from django.utils.translation import ugettext as _
|
||||||
|
|
||||||
from ..core.config import config
|
from ..core.config import config
|
||||||
|
from ..utils.collection import CollectionElement
|
||||||
from ..utils.rest_api import (
|
from ..utils.rest_api import (
|
||||||
ModelViewSet,
|
ModelViewSet,
|
||||||
Response,
|
Response,
|
||||||
@ -208,6 +209,8 @@ class UserLoginView(APIView):
|
|||||||
else:
|
else:
|
||||||
# self.request.method == 'POST'
|
# self.request.method == 'POST'
|
||||||
context['user_id'] = self.user.pk
|
context['user_id'] = self.user.pk
|
||||||
|
user_collection = CollectionElement.from_instance(self.user)
|
||||||
|
context['user'] = user_collection.as_dict_for_user(self.user)
|
||||||
return super().get_context_data(**context)
|
return super().get_context_data(**context)
|
||||||
|
|
||||||
|
|
||||||
@ -234,10 +237,18 @@ class WhoAmIView(APIView):
|
|||||||
"""
|
"""
|
||||||
Appends the user id to the context. Uses None for the anonymous
|
Appends the user id to the context. Uses None for the anonymous
|
||||||
user. Appends also a flag if guest users are enabled in the config.
|
user. Appends also a flag if guest users are enabled in the config.
|
||||||
|
Appends also the serialized user if available.
|
||||||
"""
|
"""
|
||||||
|
user_id = self.request.user.pk
|
||||||
|
if user_id is not None:
|
||||||
|
user_collection = CollectionElement.from_instance(self.request.user)
|
||||||
|
user_data = user_collection.as_dict_for_user(self.request.user)
|
||||||
|
else:
|
||||||
|
user_data = None
|
||||||
return super().get_context_data(
|
return super().get_context_data(
|
||||||
user_id=self.request.user.pk,
|
user_id=user_id,
|
||||||
guest_enabled=config['general_system_enable_anonymous'],
|
guest_enabled=config['general_system_enable_anonymous'],
|
||||||
|
user=user_data,
|
||||||
**context)
|
**context)
|
||||||
|
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ from collections import Iterable
|
|||||||
from asgiref.inmemory import ChannelLayer
|
from asgiref.inmemory import ChannelLayer
|
||||||
from channels import Channel, Group
|
from channels import Channel, Group
|
||||||
from channels.auth import channel_session_user, channel_session_user_from_http
|
from channels.auth import channel_session_user, channel_session_user_from_http
|
||||||
|
from django.apps import apps
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
|
|
||||||
from ..core.config import config
|
from ..core.config import config
|
||||||
@ -19,12 +20,33 @@ def ws_add_site(message):
|
|||||||
Adds the websocket connection to a group specific to the connecting user.
|
Adds the websocket connection to a group specific to the connecting user.
|
||||||
|
|
||||||
The group with the name 'user-None' stands for all anonymous users.
|
The group with the name 'user-None' stands for all anonymous users.
|
||||||
|
|
||||||
|
Send all "startup-data" through the connection.
|
||||||
"""
|
"""
|
||||||
Group('site').add(message.reply_channel)
|
Group('site').add(message.reply_channel)
|
||||||
message.channel_session['user_id'] = message.user.id
|
message.channel_session['user_id'] = message.user.id
|
||||||
# Saves the reply channel to the user. Uses 0 for anonymous users.
|
# Saves the reply channel to the user. Uses 0 for anonymous users.
|
||||||
websocket_user_cache.add(message.user.id or 0, message.reply_channel.name)
|
websocket_user_cache.add(message.user.id or 0, message.reply_channel.name)
|
||||||
message.reply_channel.send({"accept": True})
|
|
||||||
|
# Collect all elements that shoud be send to the client when the websocket
|
||||||
|
# connection is established
|
||||||
|
output = []
|
||||||
|
for app in apps.get_app_configs():
|
||||||
|
try:
|
||||||
|
# Get the method get_startup_elements() from an app.
|
||||||
|
# This method has to return an iterable of Collection objects.
|
||||||
|
get_startup_elements = app.get_startup_elements
|
||||||
|
except AttributeError:
|
||||||
|
# Skip apps that do not implement get_startup_elements
|
||||||
|
continue
|
||||||
|
for collection in get_startup_elements():
|
||||||
|
output.extend(collection.as_autoupdate_for_user(message.user))
|
||||||
|
|
||||||
|
# Send all data. If there is no data, then only accept the connection
|
||||||
|
if output:
|
||||||
|
message.reply_channel.send({'text': json.dumps(output)})
|
||||||
|
else:
|
||||||
|
message.reply_channel.send({'accept': True})
|
||||||
|
|
||||||
|
|
||||||
@channel_session_user
|
@channel_session_user
|
||||||
|
@ -352,6 +352,17 @@ class Collection:
|
|||||||
output.append(content)
|
output.append(content)
|
||||||
return output
|
return output
|
||||||
|
|
||||||
|
def as_autoupdate_for_user(self, user):
|
||||||
|
"""
|
||||||
|
Returns a list of dicts, that can be send though the websocket to a user.
|
||||||
|
"""
|
||||||
|
output = []
|
||||||
|
for collection_element in self.element_generator():
|
||||||
|
content = collection_element.as_autoupdate_for_user(user)
|
||||||
|
if content is not None:
|
||||||
|
output.append(content)
|
||||||
|
return output
|
||||||
|
|
||||||
def as_list_for_user(self, user):
|
def as_list_for_user(self, user):
|
||||||
"""
|
"""
|
||||||
Returns a list of dictonaries to send them to a user, for example over
|
Returns a list of dictonaries to send them to a user, for example over
|
||||||
|
@ -14,7 +14,7 @@ class TestWhoAmIView(TestCase):
|
|||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
json.loads(response.content.decode()),
|
json.loads(response.content.decode()),
|
||||||
{'user_id': None, 'guest_enabled': False})
|
{'user_id': None, 'user': None, 'guest_enabled': False})
|
||||||
|
|
||||||
def test_get_authenticated_user(self):
|
def test_get_authenticated_user(self):
|
||||||
self.client.login(username='admin', password='admin')
|
self.client.login(username='admin', password='admin')
|
||||||
@ -22,9 +22,8 @@ class TestWhoAmIView(TestCase):
|
|||||||
response = self.client.get(self.url)
|
response = self.client.get(self.url)
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(
|
self.assertEqual(json.loads(response.content.decode()).get('user_id'), 1)
|
||||||
json.loads(response.content.decode()),
|
self.assertEqual(json.loads(response.content.decode()).get('guest_enabled'), False)
|
||||||
{'user_id': 1, 'guest_enabled': False})
|
|
||||||
|
|
||||||
def test_post(self):
|
def test_post(self):
|
||||||
response = self.client.post(self.url)
|
response = self.client.post(self.url)
|
||||||
@ -79,9 +78,7 @@ class TestUserLoginView(TestCase):
|
|||||||
{'username': 'admin', 'password': 'admin'})
|
{'username': 'admin', 'password': 'admin'})
|
||||||
|
|
||||||
self.assertEqual(response.status_code, 200)
|
self.assertEqual(response.status_code, 200)
|
||||||
self.assertEqual(
|
self.assertEqual(json.loads(response.content.decode()).get('user_id'), 1)
|
||||||
json.loads(response.content.decode()),
|
|
||||||
{'user_id': 1})
|
|
||||||
|
|
||||||
def test_post_incorrect_data(self):
|
def test_post_incorrect_data(self):
|
||||||
response = self.client.post(
|
response = self.client.post(
|
||||||
|
Loading…
Reference in New Issue
Block a user