diff --git a/CHANGELOG b/CHANGELOG index a20c85148..6c8b0a52b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -34,6 +34,7 @@ Other: - Created a poll description field for each assignment-poll. - Added possibility to use custom templates and static files in user data path directory. +- Added global chatbox for managers. Version 1.5.2 (unreleased) ========================== diff --git a/openslides/core/chatbox.py b/openslides/core/chatbox.py new file mode 100644 index 000000000..a359f5cd2 --- /dev/null +++ b/openslides/core/chatbox.py @@ -0,0 +1,100 @@ +# -*- coding: utf-8 -*- + +from datetime import datetime + +from django.conf import settings +from django.contrib.sessions.models import Session +from sockjs.tornado import SockJSConnection + + +class ChatboxSocketHandler(SockJSConnection): + """ + Websocket handler for the chatbox. + """ + clients = set() + + def on_open(self, info): + """ + Checks connecting user and adds his client to the clients list. + """ + from openslides.participant.models import User + + session_key = info.get_cookie(settings.SESSION_COOKIE_NAME).value + session = Session.objects.get(session_key=session_key) + try: + self.user = User.objects.get(pk=session.get_decoded().get('_auth_user_id')) + except User.DoesNotExist: + return_value = False + else: + # TODO: Use correct permission here + if self.user.has_perm('projector.can_manage_projector'): + self.clients.add(self) + return_value = True + else: + return_value = False + return return_value + + def on_message(self, message): + """ + Sends the given message to all clients. + + Also appends the message to the cache and removes old messages if there + are more than 100. + """ + # TODO: Use correct permission here + if self.user.has_perm('projector.can_manage_projector') and message: + message_object = ChatMessage(person=self.user, message=message) + chat_messages.append(message_object) + if len(chat_messages) > 100: + chat_messages.pop(0) + self.broadcast( + self.clients, + '%s %s' % (message_object.html_time_and_person(), + message_object.message)) + + def on_close(self): + """ + Removes client from the clients list. + """ + self.clients.remove(self) + + +class ChatMessage(object): + """ + Class for all chat messages. They are stored in the chat_messages object. + + The argument person has to be a Person object, the argument message has to + be the message as string. The argument color can be a three-tuple of RGB + color values. Default is black (0, 0, 0). + """ + def __init__(self, person, message, color=None): + self.person = person + self.message = message + self.color = color or (0, 0, 0) + self.time = datetime.now() + + def html_time_and_person(self): + """ + Returns a styled prefix for each message using span and small html tags. + """ + return '%(person)s %(time)s:' % { + 'color': 'rgb(%d,%d,%d)' % self.color, + 'person': self.person.clean_name, + 'time': self.time.strftime('%H:%M')} + + +chat_messages = [] +""" +Cache with all messages during livetime of the server. +""" + + +def chat_messages_context_processor(request): + """ + Adds all chat messages to the request context as template context processor. + """ + if True: # TODO: Add permission check here + value = chat_messages + else: + value = None + return {'chat_messages': value} diff --git a/openslides/core/static/css/base.css b/openslides/core/static/css/base.css index 41b767470..c470c6e7d 100644 --- a/openslides/core/static/css/base.css +++ b/openslides/core/static/css/base.css @@ -195,8 +195,7 @@ legend + .control-group { font-weight: bold; } - -/** Left sitebar navigation **/ +/** Left sidebar navigation **/ .leftmenu ul { margin: 0; list-style: none; @@ -320,6 +319,7 @@ legend + .control-group { .icon-presentations { background-position: -264px -48px; } + /** More glyphicons free icons **/ .status_link .icon-on, .icon-checked-new { background-image: url("../img/glyphicons_152_check.png"); @@ -370,7 +370,6 @@ legend + .control-group { background-position: 0; } - /** Responsive **/ @media (max-width: 767px) { body { diff --git a/openslides/core/static/javascript/chatbox.js b/openslides/core/static/javascript/chatbox.js new file mode 100644 index 000000000..a4b61df74 --- /dev/null +++ b/openslides/core/static/javascript/chatbox.js @@ -0,0 +1,35 @@ +// Functions for OpenSlides manager chatbox + +$("button#open-chatbox").click(function(){ + $("div#chatbox").removeClass('hidden'); +}); + +$("button#close-chatbox").click(function(){ + $("div#chatbox").addClass('hidden'); +}); + +$(document).ready(function(){ + //~ var transports = $('#protocols input:checked').map(function(){ + //~ return $(this).attr('id'); + //~ }).get(); + + function print_message(message) { + var chatbox = $('#chatbox-text'); + chatbox.html(chatbox.html() + '
' + message + '
'); + chatbox.scrollTop(chatbox.scrollTop() + 10000); + } + + //~ var connection = new SockJS('http://' + window.location.host + '/chatbox', transports); + var connection = new SockJS('http://' + window.location.host + '/core/chatbox'); + + connection.onmessage = function(event) { + print_message(event.data); + }; + + $("#chatbox-form-send").click(function(){ + var message = $('#chatbox-form-input').val(); + connection.send(message); + $('#chatbox-form-input').val('').focus(); + return false; + }); +}); diff --git a/openslides/core/static/styles/chatbox.css b/openslides/core/static/styles/chatbox.css new file mode 100644 index 000000000..87c2c6fda --- /dev/null +++ b/openslides/core/static/styles/chatbox.css @@ -0,0 +1,19 @@ +/** Chatbox **/ +.icon-comments { + background-position: -240px -120px; +} +div#chatbox { + width: 35%; +} +div#chatbox h1 { + border-bottom: none; + font-size: 14px; + margin-bottom: 10px; + float: left; +} +div#chatbox div#chatbox-text { + clear: both; + min-height: 150px; + max-height:150px; + overflow-y:scroll; +} diff --git a/openslides/core/templates/base.html b/openslides/core/templates/base.html index acf0c24a5..f5ca763de 100644 --- a/openslides/core/templates/base.html +++ b/openslides/core/templates/base.html @@ -12,6 +12,7 @@ + {% for stylefile in extra_stylefiles %} @@ -36,6 +37,14 @@ + + {% if chat_messages != None %} + + {% endif %} +{{ message.html_time_and_person|safe }} {{ message.message }}
+ {% endfor %} +