Merge pull request #1220 from emanuelschuetze/chatbox
Chatbox (Fixed #504)
This commit is contained in:
commit
dd7ae59f9a
@ -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)
|
||||||
==========================
|
==========================
|
||||||
|
13
README.rst
13
README.rst
@ -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
|
||||||
========================
|
========================
|
||||||
|
|
||||||
|
@ -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']
|
||||||
|
|
||||||
|
@ -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>
|
||||||
|
@ -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
109
openslides/core/chatbox.py
Normal 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}
|
@ -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):
|
||||||
|
@ -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 {
|
||||||
|
64
openslides/core/static/css/chatbox.css
Normal file
64
openslides/core/static/css/chatbox.css
Normal 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
111
openslides/core/static/js/chatbox.js
Normal file
111
openslides/core/static/js/chatbox.js
Normal 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;
|
||||||
|
});
|
||||||
|
});
|
@ -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
@ -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}};
|
|
11
openslides/core/static/js/jquery/jquery.cookie.min.js
vendored
Normal file
11
openslides/core/static/js/jquery/jquery.cookie.min.js
vendored
Normal 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)}});
|
@ -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>
|
</form>
|
||||||
|
|
||||||
|
<!-- 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>
|
||||||
|
22
openslides/core/templates/core/chatbox.html
Normal file
22
openslides/core/templates/core/chatbox.html
Normal 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 %}
|
@ -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 %}
|
||||||
|
@ -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 = {
|
||||||
|
@ -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()
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user