Added global chatbox for managers.
This commit is contained in:
parent
f870a75119
commit
42796f6118
@ -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)
|
||||
==========================
|
||||
|
100
openslides/core/chatbox.py
Normal file
100
openslides/core/chatbox.py
Normal file
@ -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 '<span style="color:%(color)s;">%(person)s <small class="grey">%(time)s</small>:</span>' % {
|
||||
'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}
|
@ -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 {
|
||||
|
35
openslides/core/static/javascript/chatbox.js
Normal file
35
openslides/core/static/javascript/chatbox.js
Normal file
@ -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() + '<p>' + message + '</p>');
|
||||
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;
|
||||
});
|
||||
});
|
19
openslides/core/static/styles/chatbox.css
Normal file
19
openslides/core/static/styles/chatbox.css
Normal file
@ -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;
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
<link href="{% static 'css/bootstrap.min.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'css/bootstrap-responsive.min.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'css/base.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'css/chatbox.css' %}" type="text/css" rel="stylesheet" />
|
||||
<link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon" />
|
||||
{% for stylefile in extra_stylefiles %}
|
||||
<link href="{% static stylefile %}" type="text/css" rel="stylesheet" />
|
||||
@ -36,6 +37,14 @@
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Chatbox button -->
|
||||
{% if chat_messages != None %}
|
||||
<button class="btn" id="open-chatbox">
|
||||
<i class="icon-comments"></i>
|
||||
{% trans 'Chat' %}
|
||||
</button>
|
||||
{% endif %}
|
||||
|
||||
<!-- login/logout button -->
|
||||
<div class="btn-group">
|
||||
{% if user.is_authenticated %}
|
||||
@ -109,10 +118,15 @@
|
||||
</div><!--/#container-->
|
||||
{% endblock %}<!--/body-->
|
||||
|
||||
{% include 'core/chatbox.html' %}
|
||||
|
||||
<!-- JavaScript (Placed at the end of the document so the pages load faster) -->
|
||||
<script src="{% static 'js/jquery/jquery.min.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'js/utils.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'js/sockjs-0.3.min.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'js/utils.js' %}" type="text/javascript"></script>
|
||||
<script src="{% static 'js/chatbox.js' %}" type="text/javascript"></script>
|
||||
<script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
|
||||
{% for javascript in extra_javascript %}
|
||||
<script src="{% static javascript %}" type="text/javascript"></script>
|
||||
|
24
openslides/core/templates/core/chatbox.html
Normal file
24
openslides/core/templates/core/chatbox.html
Normal file
@ -0,0 +1,24 @@
|
||||
{% load i18n %}
|
||||
|
||||
{% if chat_messages != None %}
|
||||
<div class="hidden well" id="chatbox">
|
||||
<div>
|
||||
<h1>Chatbox</h1>
|
||||
<button class="btn btn-mini pull-right" id="close-chatbox"><i class="icon-remove"></i></button>
|
||||
</div>
|
||||
<div id="chatbox-text">
|
||||
{% for message in chat_messages %}
|
||||
<p>{{ message.html_time_and_person|safe }} {{ message.message }}</p>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<hr />
|
||||
<form class="form-inline" id="chatbox-form">
|
||||
<div class="input-append">
|
||||
<input id="chatbox-form-input" type="text" />
|
||||
<button id="chatbox-form-send" type="submit" class="btn btn-primary" title="{% trans 'Send message' %}">
|
||||
<i class="icon-ok icon-white"></i>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endif %}
|
@ -104,6 +104,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
|
||||
'django.core.context_processors.static',
|
||||
'openslides.utils.auth.anonymous_context_additions',
|
||||
'openslides.utils.main_menu.main_menu_entries',
|
||||
'openslides.core.chatbox.chat_messages_context_processor',
|
||||
)
|
||||
|
||||
CACHES = {
|
||||
|
@ -79,17 +79,20 @@ def run_tornado(addr, port, reload=False):
|
||||
url_string = 'http://%s:%s' % (addr, port)
|
||||
print _("Starting OpenSlides' tornado webserver listening to %(url_string)s") % {'url_string': url_string}
|
||||
|
||||
socket_js_router = SockJSRouter(ProjectorSocketHandler, '/projector/socket')
|
||||
|
||||
# Start the application
|
||||
# Setup WSGIContainer
|
||||
app = WSGIContainer(Django_WSGIHandler())
|
||||
tornado_app = Application(socket_js_router.urls + [
|
||||
|
||||
# Collect urls
|
||||
projectpr_socket_js_router = SockJSRouter(ProjectorSocketHandler, '/projector/socket')
|
||||
from openslides.core.chatbox import ChatboxSocketHandler
|
||||
chatbox_socket_js_router = SockJSRouter(ChatboxSocketHandler, '/core/chatbox')
|
||||
other_urls = [
|
||||
(r"%s(.*)" % settings.STATIC_URL, DjangoStaticFileHandler),
|
||||
(r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}),
|
||||
('.*', FallbackHandler, dict(fallback=app))
|
||||
], debug=reload)
|
||||
('.*', FallbackHandler, dict(fallback=app))]
|
||||
|
||||
# Start the application
|
||||
tornado_app = Application(projectpr_socket_js_router.urls + chatbox_socket_js_router.urls + other_urls, debug=reload)
|
||||
server = HTTPServer(tornado_app)
|
||||
server.listen(port=port,
|
||||
address=addr)
|
||||
server.listen(port=port, address=addr)
|
||||
IOLoop.instance().start()
|
||||
|
Loading…
Reference in New Issue
Block a user