Merge pull request #1220 from emanuelschuetze/chatbox

Chatbox (Fixed #504)
This commit is contained in:
Oskar Hahn 2014-04-27 21:08:06 +02:00
commit dd7ae59f9a
21 changed files with 407 additions and 77 deletions

View File

@ -34,6 +34,7 @@ Other:
- Created a poll description field for each assignment-poll. - Created a poll description field for each assignment-poll.
- Added possibility to use custom templates and static files in user data path - Added possibility to use custom templates and static files in user data path
directory. directory.
- Added global chatbox for managers.
Version 1.5.2 (unreleased) Version 1.5.2 (unreleased)
========================== ==========================

View File

@ -296,10 +296,6 @@ OpenSlides uses the following projects or parts of them:
* `jQuery <http://www.jquery.com>`_, License: MIT * `jQuery <http://www.jquery.com>`_, License: MIT
* `jQuery UI <http://jqueryui.com>`_ with custom ui components: core,
widget, mouse, sortable, datepicker, slider and css theme 'smoothness',
License: MIT
* jQuery Plugins: * jQuery Plugins:
- `jQuery DataTables Plugin <http://www.datatables.net>`_, License: - `jQuery DataTables Plugin <http://www.datatables.net>`_, License:
@ -309,7 +305,7 @@ OpenSlides uses the following projects or parts of them:
License: MIT License: MIT
- `jQuery Cookie Plugin <https://github.com/carhartl/jquery-cookie/>`_, - `jQuery Cookie Plugin <https://github.com/carhartl/jquery-cookie/>`_,
License: MIT/GPL License: MIT
- `jQuery Form Plugin <http://malsup.com/jquery/form/>`_, License: MIT/GPLv2 - `jQuery Form Plugin <http://malsup.com/jquery/form/>`_, License: MIT/GPLv2
@ -321,6 +317,12 @@ OpenSlides uses the following projects or parts of them:
- `jQuery bsmSelect <https://github.com/vicb/bsmSelect/>`_, License: - `jQuery bsmSelect <https://github.com/vicb/bsmSelect/>`_, License:
MIT/GPLv2 MIT/GPLv2
* `jQuery UI <http://jqueryui.com>`_ with custom ui components: core,
widget, mouse, resizable, sortable, datepicker, slider and css theme 'smoothness',
License: MIT
* jQuery UI AddOns:
- `jQuery UI Nested Sortable - `jQuery UI Nested Sortable
<https://github.com/mjsarfatti/nestedSortable/>`_, License: MIT <https://github.com/mjsarfatti/nestedSortable/>`_, License: MIT
@ -332,7 +334,6 @@ OpenSlides uses the following projects or parts of them:
<http://trentrichardson.com/examples/timepicker/>`_, License: MIT/GPLv2 <http://trentrichardson.com/examples/timepicker/>`_, License: MIT/GPLv2
VII. License and authors VII. License and authors
======================== ========================

View File

@ -60,9 +60,8 @@ def setup_agenda_config(sender, **kwargs):
help_text=ugettext_lazy('[Begin speach] starts the countdown, [End speach] stops the countdown.'), help_text=ugettext_lazy('[Begin speach] starts the countdown, [End speach] stops the countdown.'),
required=False)) required=False))
extra_stylefiles = ['css/jquery-ui-timepicker.css', 'css/jquery-ui/jquery-ui.custom.min.css'] extra_stylefiles = ['css/jquery-ui-timepicker.css']
extra_javascript = ['js/jquery/jquery-ui.custom.min.js', extra_javascript = ['js/jquery/jquery-ui-timepicker-addon.min.js',
'js/jquery/jquery-ui-timepicker-addon.min.js',
'js/jquery/jquery-ui-sliderAccess.min.js', 'js/jquery/jquery-ui-sliderAccess.min.js',
'js/jquery/datepicker-config.js'] 'js/jquery/datepicker-config.js']

View File

@ -12,7 +12,6 @@
{% block javascript %} {% block javascript %}
<script type="text/javascript" src="{% static 'js/agenda.js' %}"></script> <script type="text/javascript" src="{% static 'js/agenda.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery/jquery.cookie.js' %}"></script>
{% if perms.agenda.can_manage_agenda %} {% if perms.agenda.can_manage_agenda %}
<script type="text/javascript" src="{% static 'js/jquery/jquery-ui.custom.min.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery/jquery-ui.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery/jquery.once.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery/jquery.once.js' %}"></script>

View File

@ -8,7 +8,6 @@
{% block header %} {% block header %}
<link type="text/css" rel="stylesheet" media="all" href="{% static 'css/agenda.css' %}" /> <link type="text/css" rel="stylesheet" media="all" href="{% static 'css/agenda.css' %}" />
<link rel="stylesheet" href="{% static 'css/jquery-ui/jquery-ui.custom.min.css' %}" />
{% endblock %} {% endblock %}
{% block javascript %} {% block javascript %}

109
openslides/core/chatbox.py Normal file
View File

@ -0,0 +1,109 @@
# -*- coding: utf-8 -*-
from datetime import datetime
from django.conf import settings
from django.contrib.sessions.models import Session
from django.utils.html import urlize
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
# TODO: Use the django way to get the session to be compatible with
# other auth-backends; see comment in pull request #1220:
# https://github.com/OpenSlides/OpenSlides/pull/1220#discussion_r11565705
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:
if self.user.has_perm('core.can_use_chat'):
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.
"""
if self.user.has_perm('core.can_use_chat') 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 %s' % (message_object.html_time(),
message_object.html_person(),
urlize(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(self):
"""
Returns the message time in html style.
"""
return '<small class="grey">%s</small>' % self.time.strftime('%H:%M')
def html_person(self):
"""
Returns the message sender name in html style.
"""
return "<span style='color:%(color)s;'>%(person)s:</span>" % {
'color': 'rgb(%d,%d,%d)' % self.color,
'person': self.person.clean_name}
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.
Returns None if the request user has no permission to use the chat.
"""
if request.user.has_perm('core.can_use_chat'):
value = chat_messages
else:
value = None
return {'chat_messages': value}

View File

@ -24,6 +24,7 @@ class CustomSlide(SlideMixin, AbsoluteUrlMixin, models.Model):
('can_manage_projector', ugettext_noop('Can manage the projector')), ('can_manage_projector', ugettext_noop('Can manage the projector')),
('can_see_projector', ugettext_noop('Can see the projector')), ('can_see_projector', ugettext_noop('Can see the projector')),
('can_see_dashboard', ugettext_noop('Can see the dashboard')), ('can_see_dashboard', ugettext_noop('Can see the dashboard')),
('can_use_chat', ugettext_noop('Can use the chat')),
) )
def __unicode__(self): def __unicode__(self):

View File

@ -195,8 +195,7 @@ legend + .control-group {
font-weight: bold; font-weight: bold;
} }
/** Left sidebar navigation **/
/** Left sitebar navigation **/
.leftmenu ul { .leftmenu ul {
margin: 0; margin: 0;
list-style: none; list-style: none;
@ -320,6 +319,7 @@ legend + .control-group {
.icon-presentations { .icon-presentations {
background-position: -264px -48px; background-position: -264px -48px;
} }
/** More glyphicons free icons **/ /** More glyphicons free icons **/
.status_link .icon-on, .icon-checked-new { .status_link .icon-on, .icon-checked-new {
background-image: url("../img/glyphicons_152_check.png"); background-image: url("../img/glyphicons_152_check.png");
@ -370,7 +370,6 @@ legend + .control-group {
background-position: 0; background-position: 0;
} }
/** Responsive **/ /** Responsive **/
@media (max-width: 767px) { @media (max-width: 767px) {
body { body {

View File

@ -0,0 +1,64 @@
/** Chatbox **/
.icon-comments {
background-position: -240px -120px;
}
div#chatbox {
width: 40%;
height: 220px;
min-height: 140px;
min-width: 300px;
position: fixed;
right: 0px;
bottom: 0;
z-index: 99;
margin-bottom: 0;
box-shadow: -3px -3px 10px rgba(0,0,0,0.2);
border-width: 2px;
border-color: darkgrey;
padding: 0;
}
#chatbox-header{
background-color: #333333;
background-image: -moz-linear-gradient(top, #444444, #222222);
background-image: -ms-linear-gradient(top, #444444, #222222);
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#444444), to(#222222));
background-image: -webkit-linear-gradient(top, #444444, #222222);
background-image: -o-linear-gradient(top, #444444, #222222);
background-image: linear-gradient(top, #444444, #222222);
border-color: #333333;
}
h1#chatbox-title {
color: #999999;
font-size: 16px;
font-weight: bold;
line-height: 16px;
margin: 0;
padding: 3px 10px 7px 10px;
border-bottom: none;
}
div#chatbox div#chatbox-text {
clear: both;
overflow-y:scroll;
background-color: white;
padding: 0 10px;
font-size: 12px;
height: calc(100% - 65px);
}
#chatbox form {
margin-bottom: 0px;
padding: 5px;
}
#chatbox form input {
width: 90%;
}
#newchatmessage {
border-radius: 8px;
background-color: red;
color: white;
font-size: 0.7em;
position: relative;
right: 0;
top: -6px;
padding: 2px 6px;
display: none;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,111 @@
// Functions for OpenSlides chatbox
$(function() {
// Chatbox is resizable on two sides (north and west) and
// one corner (nortwest)
$("div#chatbox").resizable({ handles: 'n, w, nw' });
});
$("#chatboxbutton").click(function(){
if ($(this).hasClass('active')) {
/* close chatbox */
$("#chatbox").addClass('hidden');
// Save new chatbox state in cookie
save_cookie(false);
} else {
/* open chatbox */
$("#chatbox").removeClass('hidden');
// Scroll chat content to end
$("#chatbox-text").scrollTop(10000);
// Set focus to input field
$("#chatbox-form-input").val('').focus();
// Save new chatbox state in cookie
save_cookie(true)
$.cookie('openslides-chatbox-new-message', 0, { path: "/"});
// Hide new message number
$("#newchatmessage").fadeOut();
}
});
$("#close-chatbox").click(function(){
$("#chatbox").addClass('hidden');
$("#chatboxbutton").removeClass('active');
// Save new chatbox state in cookie
save_cookie(false);
});
$("#chatbox").resize(function() {
// If resizing the chatbox window the chatbox text scrolls to latest message.
// Sets a minimum for width and height.
$("#chatbox-text").scrollTop(10000);
$("#chatbox").resizable( "option", "minWidth", 300 );
$("#chatbox").resizable( "option", "minHeight", 140 );
save_cookie(true);
});
// Save chatbox state in cookie.
// Parameter 'active': chatbox window is open (true) or closed (false).
function save_cookie(active) {
var status = {
'active': active,
'width': $("#chatbox").width(),
'height': $("#chatbox").height()
};
$.cookie('openslides-chatbox', JSON.stringify(status), { path: "/"});
}
$(document).ready(function(){
// Load chatbox state from cookie.
var cookie = $.cookie('openslides-chatbox');
if (cookie) {
var status = $.parseJSON(cookie);
if (status['active']) {
$("#chatbox").removeClass('hidden');
$("#chatboxbutton").addClass('active');
// Scroll chat content to end
$("#chatbox-text").scrollTop(10000);
// Set focus to input field
$('#chatbox-form-input').val('').focus();
}
if (status['width']) {
$("#chatbox").width(status['width']);
}
if (status['height']) {
$("#chatbox").height(status['height']);
}
}
// Load number of new messages from cookie
if ($.cookie('openslides-chatbox-new-message') > 0) {
$("#newchatmessage").html($.cookie('openslides-chatbox-new-message'));
$("#newchatmessage").show(0);
}
// Print chat messages into chatbox
function print_message_into_box(message) {
var chatcontent = $('#chatbox-text');
chatcontent.html(chatcontent.html() + '<br>' + message);
chatcontent.scrollTop(chatcontent.scrollTop() + 10000);
// if chatbox is hidden show number of new messages and save in cookie
if ($("#chatbox").hasClass('hidden')){
new_messages = parseInt($.cookie('openslides-chatbox-new-message')) + 1;
if (new_messages == 1)
$("#newchatmessage").fadeIn();
$("#newchatmessage").html(new_messages);
$.cookie('openslides-chatbox-new-message', new_messages, { path: "/"});
}
}
var connection = new SockJS('http://' + window.location.host + '/core/chatbox');
connection.onmessage = function(event) {
print_message_into_box(event.data);
};
$("#chatbox-form").submit(function(){
var message = $('#chatbox-form-input').val();
connection.send(message);
$('#chatbox-form-input').val('').focus();
return false;
});
});

View File

@ -7,7 +7,7 @@
function saveOrder() { function saveOrder() {
$(".column").each(function(index, value){ $(".column").each(function(index, value){
var colid = value.id; var colid = value.id;
var cookieName = "cookie-" + colid; var cookieName = "openslides-dashboard-" + colid;
// Get the order for this column. // Get the order for this column.
var order = $('#' + colid).sortable("toArray"); var order = $('#' + colid).sortable("toArray");
var cookie_content = []; var cookie_content = [];
@ -24,7 +24,7 @@ function saveOrder() {
} }
cookie_content[i] = widget_id + '/' + is_collabsed + '/' + is_pinned; cookie_content[i] = widget_id + '/' + is_collabsed + '/' + is_pinned;
} }
$.cookie(cookieName, cookie_content, { path: "/", expiry: new Date(2012, 1, 1)}); $.cookie(cookieName, cookie_content);
}); });
} }
@ -32,9 +32,9 @@ function saveOrder() {
function restoreOrder() { function restoreOrder() {
$(".column").each(function(index, value) { $(".column").each(function(index, value) {
var colid = value.id; var colid = value.id;
var cookieName = "cookie-" + colid; var cookieName = "openslides-dashboard-" + colid;
var cookie = $.cookie(cookieName); var cookie = $.cookie(cookieName);
if ( cookie === null ) { return; } if ( cookie === undefined ) { return; }
var widgets = cookie.split(","); var widgets = cookie.split(",");
for (var i = 0, n = widgets.length; i < n; i++ ) { for (var i = 0, n = widgets.length; i < n; i++ ) {
var widget_information = widgets[i].split('/'); var widget_information = widgets[i].split('/');

File diff suppressed because one or more lines are too long

View File

@ -1,12 +0,0 @@
// $Id: jquery.cookie.js,v 1.1 2009/07/01 12:16:48 dries Exp $
/**
* Cookie plugin 1.0
*
* Copyright (c) 2006 Klaus Hartl (stilbuero.de)
* Dual licensed under the MIT and GPL licenses:
* http://www.opensource.org/licenses/mit-license.php
* http://www.gnu.org/licenses/gpl.html
*
*/
jQuery.cookie=function(b,j,m){if(typeof j!="undefined"){m=m||{};if(j===null){j="";m.expires=-1}var e="";if(m.expires&&(typeof m.expires=="number"||m.expires.toUTCString)){var f;if(typeof m.expires=="number"){f=new Date();f.setTime(f.getTime()+(m.expires*24*60*60*1000))}else{f=m.expires}e="; expires="+f.toUTCString()}var l=m.path?"; path="+(m.path):"";var g=m.domain?"; domain="+(m.domain):"";var a=m.secure?"; secure":"";document.cookie=[b,"=",encodeURIComponent(j),e,l,g,a].join("")}else{var d=null;if(document.cookie&&document.cookie!=""){var k=document.cookie.split(";");for(var h=0;h<k.length;h++){var c=jQuery.trim(k[h]);if(c.substring(0,b.length+1)==(b+"=")){d=decodeURIComponent(c.substring(b.length+1));break}}}return d}};

View File

@ -0,0 +1,11 @@
/*!
* jQuery Cookie Plugin v1.4.0
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2013 Klaus Hartl
* Released under the MIT license
*/
(function(factory){if(typeof define==="function"&&define.amd)define(["jquery"],factory);else if(typeof exports==="object")factory(require("jquery"));else factory(jQuery)})(function($){var pluses=/\+/g;function encode(s){return config.raw?s:encodeURIComponent(s)}function decode(s){return config.raw?s:decodeURIComponent(s)}function stringifyCookieValue(value){return encode(config.json?JSON.stringify(value):String(value))}function parseCookieValue(s){if(s.indexOf('"')===0)s=s.slice(1,-1).replace(/\\"/g,
'"').replace(/\\\\/g,"\\");try{s=decodeURIComponent(s.replace(pluses," "));return config.json?JSON.parse(s):s}catch(e){}}function read(s,converter){var value=config.raw?s:parseCookieValue(s);return $.isFunction(converter)?converter(value):value}var config=$.cookie=function(key,value,options){if(value!==undefined&&!$.isFunction(value)){options=$.extend({},config.defaults,options);if(typeof options.expires==="number"){var days=options.expires,t=options.expires=new Date;t.setTime(+t+days*864E5)}return document.cookie=
[encode(key),"=",stringifyCookieValue(value),options.expires?"; expires="+options.expires.toUTCString():"",options.path?"; path="+options.path:"",options.domain?"; domain="+options.domain:"",options.secure?"; secure":""].join("")}var result=key?undefined:{};var cookies=document.cookie?document.cookie.split("; "):[];for(var i=0,l=cookies.length;i<l;i++){var parts=cookies[i].split("=");var name=decode(parts.shift());var cookie=parts.join("=");if(key&&key===name){result=read(cookie,value);break}if(!key&&
(cookie=read(cookie))!==undefined)result[name]=cookie}return result};config.defaults={};$.removeCookie=function(key,options){if($.cookie(key)===undefined)return false;$.cookie(key,"",$.extend({},options,{expires:-1}));return!$.cookie(key)}});

View File

@ -11,7 +11,9 @@
<!-- styles --> <!-- styles -->
<link href="{% static 'css/bootstrap.min.css' %}" type="text/css" rel="stylesheet" /> <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/bootstrap-responsive.min.css' %}" type="text/css" rel="stylesheet" />
<link href="{% static 'css/jquery-ui/jquery-ui.custom.min.css'%}" type="text/css" rel="stylesheet" />
<link href="{% static 'css/base.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" /> <link href="{% static 'img/favicon.png' %}" type="image/png" rel="shortcut icon" />
{% for stylefile in extra_stylefiles %} {% for stylefile in extra_stylefiles %}
<link href="{% static stylefile %}" type="text/css" rel="stylesheet" /> <link href="{% static stylefile %}" type="text/css" rel="stylesheet" />
@ -25,7 +27,7 @@
<div class="span12"> <div class="span12">
<div id="header"> <div id="header">
<a href="/" class="logo" title="{% trans 'Home' %}"><img src="{% static 'img/logo.png' %}" alt="{% trans 'Logo' %}" /></a> <a href="/" class="logo" title="{% trans 'Home' %}"><img src="{% static 'img/logo.png' %}" alt="{% trans 'Logo' %}" /></a>
<span class="title optional">{% get_config 'event_name' %} {% get_config 'event_description' %}</span> <span class="title optional">{% get_config 'event_name' %} {% if 'event_description'|get_config %} {% get_config 'event_description' %}{% endif %}</span>
{% block loginbutton %} {% block loginbutton %}
<div class="pull-right"> <div class="pull-right">
<!-- Search field --> <!-- Search field -->
@ -36,6 +38,15 @@
</div> </div>
</form> &nbsp; </form> &nbsp;
<!-- Chatbox button -->
{% if chat_messages != None %}
<button class="btn" id="chatboxbutton" data-toggle="button">
<i class="icon-comments"></i>
{% trans 'Chat' %}
<span id="newchatmessage"></span>
</button>
{% endif %}
<!-- login/logout button --> <!-- login/logout button -->
<div class="btn-group"> <div class="btn-group">
{% if user.is_authenticated %} {% if user.is_authenticated %}
@ -109,10 +120,16 @@
</div><!--/#container--> </div><!--/#container-->
{% endblock %}<!--/body--> {% endblock %}<!--/body-->
{% include 'core/chatbox.html' %}
<!-- JavaScript (Placed at the end of the document so the pages load faster) --> <!-- 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/jquery/jquery.min.js' %}" type="text/javascript"></script>
<script src="{% static 'js/jquery/jquery.cookie.min.js' %}" type="text/javascript"></script>
<script src="{% static 'js/jquery/jquery-ui.custom.min.js' %}" type="text/javascript"></script>
<script src="{% static 'js/bootstrap.min.js' %}" type="text/javascript"></script> <script src="{% static 'js/bootstrap.min.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/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> <script src="{% url 'django.views.i18n.javascript_catalog' %}" type="text/javascript"></script>
{% for javascript in extra_javascript %} {% for javascript in extra_javascript %}
<script src="{% static javascript %}" type="text/javascript"></script> <script src="{% static javascript %}" type="text/javascript"></script>

View File

@ -0,0 +1,22 @@
{% load i18n %}
{% if chat_messages != None %}
<div class="hidden well" id="chatbox">
<div id="chatbox-header">
<h1 id="chatbox-title"><i class="icon-comments icon-white"></i> Chat
<button class="btn btn-mini right" id="close-chatbox"><i class="icon-remove"></i></button>
</h1>
</div>
<div id="chatbox-text">
{% for message in chat_messages %}
<br>{{ message.html_time|safe }} {{ message.html_person|safe }} {{ message.message|urlize }}
{% endfor %}
</div>
<form class="form-inline" id="chatbox-form">
<div class="input-append" style="width: calc(100% - 25px);">
<input id="chatbox-form-input" type="text" />
<button type="submit" class="btn"><i class="icon-comments"></i></button>
</div>
</form>
</div>
{% endif %}

View File

@ -8,8 +8,6 @@
{% endblock %} {% endblock %}
{% block javascript %} {% block javascript %}
<script type="text/javascript" src="{% static 'js/jquery/jquery-ui.custom.min.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery/jquery.cookie.js' %}"></script>
<script type="text/javascript" src="{% static 'js/jquery/jquery.form.js' %}"></script> <script type="text/javascript" src="{% static 'js/jquery/jquery.form.js' %}"></script>
<script type="text/javascript" src="{% static 'js/dashboard.js' %}"></script> <script type="text/javascript" src="{% static 'js/dashboard.js' %}"></script>
{% endblock %} {% endblock %}

View File

@ -104,6 +104,7 @@ TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.static', 'django.core.context_processors.static',
'openslides.utils.auth.anonymous_context_additions', 'openslides.utils.auth.anonymous_context_additions',
'openslides.utils.main_menu.main_menu_entries', 'openslides.utils.main_menu.main_menu_entries',
'openslides.core.chatbox.chat_messages_context_processor',
) )
CACHES = { CACHES = {

View File

@ -124,58 +124,63 @@ def create_builtin_groups_and_admin(sender, **kwargs):
# Do completely nothing if there are already some of our groups in the database. # Do completely nothing if there are already some of our groups in the database.
return return
# Anonymous and Registered # Anonymous (pk 1) and Registered (pk 2)
ct_core = ContentType.objects.get(app_label='core', model='customslide') ct_core = ContentType.objects.get(app_label='core', model='customslide')
perm_1 = Permission.objects.get(content_type=ct_core, codename='can_see_projector') perm_11 = Permission.objects.get(content_type=ct_core, codename='can_see_projector')
perm_2 = Permission.objects.get(content_type=ct_core, codename='can_see_dashboard') perm_12 = Permission.objects.get(content_type=ct_core, codename='can_see_dashboard')
ct_agenda = ContentType.objects.get(app_label='agenda', model='item') ct_agenda = ContentType.objects.get(app_label='agenda', model='item')
ct_speaker = ContentType.objects.get(app_label='agenda', model='speaker') ct_speaker = ContentType.objects.get(app_label='agenda', model='speaker')
perm_3 = Permission.objects.get(content_type=ct_agenda, codename='can_see_agenda') perm_13 = Permission.objects.get(content_type=ct_agenda, codename='can_see_agenda')
perm_3a = Permission.objects.get(content_type=ct_agenda, codename='can_see_orga_items') perm_14 = Permission.objects.get(content_type=ct_agenda, codename='can_see_orga_items')
can_speak = Permission.objects.get(content_type=ct_speaker, codename='can_be_speaker') can_speak = Permission.objects.get(content_type=ct_speaker, codename='can_be_speaker')
ct_motion = ContentType.objects.get(app_label='motion', model='motion') ct_motion = ContentType.objects.get(app_label='motion', model='motion')
perm_4 = Permission.objects.get(content_type=ct_motion, codename='can_see_motion') perm_15 = Permission.objects.get(content_type=ct_motion, codename='can_see_motion')
ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment') ct_assignment = ContentType.objects.get(app_label='assignment', model='assignment')
perm_5 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment') perm_16 = Permission.objects.get(content_type=ct_assignment, codename='can_see_assignment')
ct_participant = ContentType.objects.get(app_label='participant', model='user') ct_participant = ContentType.objects.get(app_label='participant', model='user')
perm_6 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant') perm_17 = Permission.objects.get(content_type=ct_participant, codename='can_see_participant')
ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile') ct_mediafile = ContentType.objects.get(app_label='mediafile', model='mediafile')
perm_6a = Permission.objects.get(content_type=ct_mediafile, codename='can_see') perm_18 = Permission.objects.get(content_type=ct_mediafile, codename='can_see')
group_anonymous = Group.objects.create(name=ugettext_noop('Anonymous'), pk=1) group_anonymous = Group.objects.create(name=ugettext_noop('Anonymous'), pk=1)
group_anonymous.permissions.add(perm_1, perm_2, perm_3, perm_3a, perm_4, perm_5, perm_6, perm_6a) group_anonymous.permissions.add(perm_11, perm_12, perm_13, perm_14, perm_15, perm_16, perm_17, perm_18)
group_registered = Group.objects.create(name=ugettext_noop('Registered'), pk=2) group_registered = Group.objects.create(name=ugettext_noop('Registered'), pk=2)
group_registered.permissions.add(perm_1, perm_2, perm_3, perm_3a, perm_4, perm_5, perm_6, perm_6a, can_speak) group_registered.permissions.add(perm_11, perm_12, perm_13, perm_14, perm_15, perm_16, perm_17, perm_18, can_speak)
# Delegates # Delegates (pk 3)
perm_7 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion') perm_31 = Permission.objects.get(content_type=ct_motion, codename='can_create_motion')
perm_8 = Permission.objects.get(content_type=ct_motion, codename='can_support_motion') perm_32 = Permission.objects.get(content_type=ct_motion, codename='can_support_motion')
perm_9 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_other') perm_33 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_other')
perm_10 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_self') perm_34 = Permission.objects.get(content_type=ct_assignment, codename='can_nominate_self')
perm_10a = Permission.objects.get(content_type=ct_mediafile, codename='can_upload') perm_35 = Permission.objects.get(content_type=ct_mediafile, codename='can_upload')
group_delegates = Group.objects.create(name=ugettext_noop('Delegates'), pk=3) group_delegates = Group.objects.create(name=ugettext_noop('Delegates'), pk=3)
group_delegates.permissions.add(perm_7, perm_8, perm_9, perm_10, perm_10a) group_delegates.permissions.add(perm_31, perm_32, perm_33, perm_34, perm_35)
# Staff # Staff (pk 4)
perm_11 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda') perm_41 = Permission.objects.get(content_type=ct_agenda, codename='can_manage_agenda')
perm_12 = Permission.objects.get(content_type=ct_motion, codename='can_manage_motion') perm_42 = Permission.objects.get(content_type=ct_motion, codename='can_manage_motion')
perm_13 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment') perm_43 = Permission.objects.get(content_type=ct_assignment, codename='can_manage_assignment')
perm_14 = Permission.objects.get(content_type=ct_participant, codename='can_manage_participant') perm_44 = Permission.objects.get(content_type=ct_participant, codename='can_manage_participant')
perm_15 = Permission.objects.get(content_type=ct_core, codename='can_manage_projector') perm_45 = Permission.objects.get(content_type=ct_core, codename='can_manage_projector')
perm_15a = Permission.objects.get(content_type=ct_mediafile, codename='can_manage') perm_46 = Permission.objects.get(content_type=ct_core, codename='can_use_chat')
perm_47 = Permission.objects.get(content_type=ct_mediafile, codename='can_manage')
ct_config = ContentType.objects.get(app_label='config', model='configstore') ct_config = ContentType.objects.get(app_label='config', model='configstore')
perm_16 = Permission.objects.get(content_type=ct_config, codename='can_manage') perm_48 = Permission.objects.get(content_type=ct_config, codename='can_manage')
group_staff = Group.objects.create(name=ugettext_noop('Staff'), pk=4) group_staff = Group.objects.create(name=ugettext_noop('Staff'), pk=4)
group_staff.permissions.add(perm_7, perm_9, perm_10, perm_10a, perm_11, perm_12, perm_13, perm_14, perm_15, perm_15a, perm_16) # add delegate permissions (without can_support_motion)
group_staff.permissions.add(perm_6) # TODO: Remove this redundancy after cleanup of the permission system group_staff.permissions.add(perm_31, perm_33, perm_34, perm_35)
# add staff permissions
group_staff.permissions.add(perm_41, perm_42, perm_43, perm_44, perm_45, perm_46, perm_47, perm_48)
# add can_see_participant permission
group_staff.permissions.add(perm_17) # TODO: Remove this redundancy after cleanup of the permission system
# Admin user # Admin user
create_or_reset_admin_user() create_or_reset_admin_user()

View File

@ -79,17 +79,20 @@ def run_tornado(addr, port, reload=False):
url_string = 'http://%s:%s' % (addr, port) url_string = 'http://%s:%s' % (addr, port)
print _("Starting OpenSlides' tornado webserver listening to %(url_string)s") % {'url_string': url_string} print _("Starting OpenSlides' tornado webserver listening to %(url_string)s") % {'url_string': url_string}
socket_js_router = SockJSRouter(ProjectorSocketHandler, '/projector/socket') # Setup WSGIContainer
# Start the application
app = WSGIContainer(Django_WSGIHandler()) 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.STATIC_URL, DjangoStaticFileHandler),
(r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}), (r'%s(.*)' % settings.MEDIA_URL, StaticFileHandler, {'path': settings.MEDIA_ROOT}),
('.*', FallbackHandler, dict(fallback=app)) ('.*', FallbackHandler, dict(fallback=app))]
], debug=reload)
# 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 = HTTPServer(tornado_app)
server.listen(port=port, server.listen(port=port, address=addr)
address=addr)
IOLoop.instance().start() IOLoop.instance().start()