Added notify system.

This commit is contained in:
Norman Jäckel 2017-04-19 22:58:48 +02:00 committed by FinnStutzenstein
parent e1a95588e7
commit bd68997a5d
5 changed files with 122 additions and 20 deletions

View File

@ -21,6 +21,8 @@ Motions:
- Added support for export motions in a zip archive [#3189]. - Added support for export motions in a zip archive [#3189].
- Bugfix: changing motion line length did not invalidate cache [#3202] - Bugfix: changing motion line length did not invalidate cache [#3202]
- Bugfix: Added more distance in motion PDF for DEL-tags in new lines [#3211]. - Bugfix: Added more distance in motion PDF for DEL-tags in new lines [#3211].
- Added warning message if an edit dialog was already opened by another
client [#3212].
Users: Users:
- User without permission to see users can now see agenda item speakers, - User without permission to see users can now see agenda item speakers,
@ -36,6 +38,7 @@ Core:
application [#3172]. application [#3172].
- Adding support for choosing image files as logos [#3184, #3207]. - Adding support for choosing image files as logos [#3184, #3207].
- Fixing error when clearing empty chat [#3199]. - Fixing error when clearing empty chat [#3199].
- Added notify system [#3212].
General: General:
- Several bugfixes and minor improvements. - Several bugfixes and minor improvements.

View File

@ -99,6 +99,11 @@ angular.module('OpenSlidesApp.core', [
ErrorMessage.clearConnectionError(); ErrorMessage.clearConnectionError();
}; };
}; };
Autoupdate.send = function (message) {
if (socket) {
socket.send(JSON.stringify(message));
}
};
Autoupdate.closeConnection = function () { Autoupdate.closeConnection = function () {
if (socket) { if (socket) {
socket.close(); socket.close();
@ -262,38 +267,40 @@ angular.module('OpenSlidesApp.core', [
'autoupdate', 'autoupdate',
'dsEject', 'dsEject',
function (DS, autoupdate, dsEject) { function (DS, autoupdate, dsEject) {
// Handler for normal autoupdate messages.
autoupdate.onMessage(function(json) { autoupdate.onMessage(function(json) {
// TODO: when MODEL.find() is called after this
// a new request is fired. This could be a bug in DS
var dataList = []; var dataList = [];
try { try {
dataList = JSON.parse(json); dataList = JSON.parse(json);
} catch(err) { } catch(err) {
console.error(json); console.error(json);
} }
var dataListByCollection = _.groupBy(dataList, 'collection'); var dataListByCollection = _.groupBy(dataList, 'collection');
_.forEach(dataListByCollection, function(list, key) { _.forEach(dataListByCollection, function (list, key) {
var changedElements = []; var changedElements = [];
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 // Now handle autoupdate message but do not handle notify messages.
var instance = DS.get(data.collection, data.id); if (data.collection !== 'notify') {
if (instance) { // remove (=eject) object from local DS store
dsEject(data.collection, instance); var instance = DS.get(data.collection, data.id);
} if (instance) {
// check if object changed or deleted dsEject(data.collection, instance);
if (data.action === 'changed') { }
changedElements.push(data.data); // check if object changed or deleted
} else if (data.action === 'deleted') { if (data.action === 'changed') {
deletedElements.push(data.id); changedElements.push(data.data);
} else { } else if (data.action === 'deleted') {
console.error('Error: Undefined action for received object' + deletedElements.push(data.id);
'(' + data.collection + ', ' + data.id + ')'); } else {
console.error('Error: Undefined action for received object' +
'(' + data.collection + ', ' + data.id + ')');
}
} }
}); });
// add (=inject) all given objects into local DS store // add (=inject) all given objects into local DS store
@ -307,6 +314,26 @@ angular.module('OpenSlidesApp.core', [
}); });
}); });
}); });
// Handler for notify messages.
autoupdate.onMessage(function(json) {
var dataList = [];
try {
dataList = JSON.parse(json);
} catch(err) {
console.error(json);
}
var dataListByCollection = _.groupBy(dataList, 'collection');
_.forEach(dataListByCollection, function (list, key) {
_.forEach(list, function (data) {
if (data.collection === 'notify') {
// TODO: Add more code here.
console.log(data);
}
});
});
});
} }
]) ])

View File

@ -276,6 +276,7 @@ angular.module('OpenSlidesApp.motions.site', [
.factory('MotionForm', [ .factory('MotionForm', [
'gettextCatalog', 'gettextCatalog',
'operator', 'operator',
'autoupdate',
'Editor', 'Editor',
'MotionComment', 'MotionComment',
'Category', 'Category',
@ -287,10 +288,18 @@ angular.module('OpenSlidesApp.motions.site', [
'Workflow', 'Workflow',
'Agenda', 'Agenda',
'AgendaTree', 'AgendaTree',
function (gettextCatalog, operator, Editor, MotionComment, Category, Config, Mediafile, MotionBlock, Tag, User, Workflow, Agenda, AgendaTree) { function (gettextCatalog, operator, autoupdate, Editor, MotionComment, Category, Config, Mediafile, MotionBlock, Tag, User, Workflow, Agenda, AgendaTree) {
return { return {
// ngDialog for motion form // ngDialog for motion form
getDialog: function (motion) { getDialog: function (motion) {
//TODO: This is just a test implementation. Remove it later.
autoupdate.send([{
collection: 'notify',
name: 'motion_edit_dialog_opened',
//users: [4, 5, ],
//replyChannels: ["daphne.response.StSjKMGYeq!PfqbSbiJNP", ],
}]);
//End of test implementation
return { return {
template: 'static/templates/motions/motion-form.html', template: 'static/templates/motions/motion-form.html',
controller: motion ? 'MotionUpdateCtrl' : 'MotionCreateCtrl', controller: motion ? 'MotionUpdateCtrl' : 'MotionCreateCtrl',

View File

@ -6,6 +6,7 @@ from openslides.utils.autoupdate import (
ws_add_site, ws_add_site,
ws_disconnect_projector, ws_disconnect_projector,
ws_disconnect_site, ws_disconnect_site,
ws_receive_site,
) )
projector_routing = [ projector_routing = [
@ -16,6 +17,7 @@ projector_routing = [
site_routing = [ site_routing = [
route("websocket.connect", ws_add_site), route("websocket.connect", ws_add_site),
route("websocket.disconnect", ws_disconnect_site), route("websocket.disconnect", ws_disconnect_site),
route("websocket.receive", ws_receive_site),
] ]
channel_routing = [ channel_routing = [

View File

@ -1,7 +1,7 @@
import json import json
import time import time
import warnings import warnings
from collections import Iterable from collections import Iterable, defaultdict
from channels import Channel, Group from channels import Channel, Group
from channels.asgi import get_channel_layer from channels.asgi import get_channel_layer
@ -87,6 +87,67 @@ def ws_disconnect_site(message):
websocket_user_cache.remove(message.user.id or 0, message.reply_channel.name) websocket_user_cache.remove(message.user.id or 0, message.reply_channel.name)
@channel_session_user
def ws_receive_site(message):
"""
This function is called if a message from a client comes in. The message
should be a list. Every item is broadcasted to the given users (or all
users if no user list is given) if it is a notify element.
The server adds the sender's user id (0 for anonymous) and reply
channel name so that a receiver client may reply to the sender or to all
sender's instances.
"""
try:
incomming = json.loads(message.content['text'])
except ValueError:
# Message content is invalid. Just do nothing.
pass
else:
if isinstance(incomming, list):
# Parse all items
receivers_users = defaultdict(list)
receivers_reply_channels = defaultdict(list)
items_for_all = []
for item in incomming:
if item.get('collection') == 'notify':
use_receivers_dict = False
item['senderReplyChannelName'] = message.reply_channel.name
item['senderUserId'] = message.user.id or 0
users = item.get('users')
if isinstance(users, list):
# Send this item only to all reply channels of some site users.
for user_id in users:
receivers_users[user_id].append(item)
use_receivers_dict = True
reply_channels = item.get('replyChannels')
if isinstance(reply_channels, list):
# Send this item only to some reply channels.
for reply_channel_name in reply_channels:
receivers_reply_channels[reply_channel_name].append(item)
use_receivers_dict = True
if not use_receivers_dict:
# Send this item to all reply channels.
items_for_all.append(item)
# Send all items
for user_id, channel_names in websocket_user_cache.get_all().items():
output = receivers_users[user_id]
if len(output) > 0:
for channel_name in channel_names:
send_or_wait(Channel(channel_name).send, {'text': json.dumps(output)})
for channel_name, output in receivers_reply_channels.items():
if len(output) > 0:
send_or_wait(Channel(channel_name).send, {'text': json.dumps(output)})
if len(items_for_all) > 0:
send_or_wait(Group('site').send, {'text': json.dumps(items_for_all)})
@channel_session_user_from_http @channel_session_user_from_http
def ws_add_projector(message, projector_id): def ws_add_projector(message, projector_id):
""" """