Merge pull request #592 from ostcar/overlay

New Overlay API
This commit is contained in:
Oskar Hahn 2013-04-15 04:26:58 -07:00
commit 1e389c25d8
17 changed files with 224 additions and 208 deletions

View File

@ -120,28 +120,6 @@ def register_slidefunc(key, func, control_template=None, weight=0, name=''):
name=name,) name=name,)
def projector_message_set(message, sid=None):
"""
Set the overlay-message.
if sid is set, only show the message on the sid-slide.
"""
from models import ProjectorOverlay
config['projector_message'] = message
try:
overlay = ProjectorOverlay.objects.get(def_name='Message')
except ProjectorOverlay.DoesNotExist:
overlay = ProjectorOverlay(def_name='Message', active=False)
overlay.sid = sid
overlay.save()
def projector_message_delete():
"""
Delete the overlay-message.
"""
config['projector_message'] = ''
def get_all_widgets(request, session=False): def get_all_widgets(request, session=False):
widgets = SortedDict() widgets = SortedDict()
session_widgets = request.session.get('widgets', {}) session_widgets = request.session.get('widgets', {})

View File

@ -54,17 +54,3 @@ class ProjectorSlide(models.Model, SlideMixin):
register_slidemodel(ProjectorSlide, control_template='projector/control_customslide.html') register_slidemodel(ProjectorSlide, control_template='projector/control_customslide.html')
class ProjectorOverlay(models.Model):
"""
Save information for a overlay.
"""
active = models.BooleanField(verbose_name=_('Active'))
def_name = models.CharField(max_length=64)
sid = models.CharField(max_length=64, null=True, blank=True)
def __unicode__(self):
if self.sid:
return "%s on %s" % (self.def_name, self.sid)
return self.def_name

View File

@ -16,7 +16,6 @@ from django.dispatch import receiver
from django.template.loader import render_to_string from django.template.loader import render_to_string
from openslides.config.api import config from openslides.config.api import config
from openslides.projector.signals import projector_overlays
SLIDE = {} SLIDE = {}
@ -146,35 +145,26 @@ class Widget(object):
return unicode(self.display_name) return unicode(self.display_name)
@receiver(projector_overlays, dispatch_uid="projector_countdown") class Overlay(object):
def countdown(sender, **kwargs): """
name = 'Countdown' Represents an overlay which can be seen on the projector.
if kwargs['register']: """
return name
if name in kwargs['call']:
if config['countdown_state'] == 'active':
seconds = max(0, int(config['countdown_start_stamp'] + config['countdown_time'] - time()))
elif config['countdown_state'] == 'paused':
seconds = max(0, int(config['countdown_start_stamp'] + config['countdown_time'] - config['countdown_pause_stamp']))
elif config['countdown_state'] == 'inactive':
seconds = max(0, int(config['countdown_time']))
else:
seconds = 0
if seconds == 0: def __init__(self, name, get_widget_html, get_projector_html):
config['countdown_state'] = 'expired' self.name = name
self.widget_html_callback = get_widget_html
self.projector_html_callback = get_projector_html
return (name, '%02d:%02d' % (seconds / 60, seconds % 60)) def get_widget_html(self):
return None return self.widget_html_callback()
def get_projector_html(self):
try:
return self._projector_html
except AttributeError:
self._projector_html = self.projector_html_callback()
return self.get_projector_html()
@receiver(projector_overlays, dispatch_uid="projector_message") def is_active(self):
def projector_message(sender, **kwargs): return (self.name in config['projector_active_overlays'] and
name = 'Message' self.get_projector_html() is not None)
if kwargs['register']:
return name
if name in kwargs['call']:
message = config['projector_message']
if message != '':
return (name, message)
return None

View File

@ -9,16 +9,22 @@
:copyright: 20112013 by OpenSlides team, see AUTHORS. :copyright: 20112013 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details. :license: GNU GPL, see LICENSE for more details.
""" """
from time import time
from django.dispatch import Signal, receiver from django.dispatch import Signal, receiver
from django import forms from django import forms
from django.utils.translation import ugettext_lazy, ugettext as _ from django.utils.translation import ugettext_lazy, ugettext as _
from django.template.loader import render_to_string
from django.core.context_processors import csrf
from openslides.config.api import config
from openslides.config.signals import config_signal from openslides.config.signals import config_signal
from openslides.config.api import ConfigVariable, ConfigPage from openslides.config.api import ConfigVariable, ConfigPage
from .projector import Overlay
projector_overlays = Signal(providing_args=['register', 'call'])
projector_overlays = Signal(providing_args=['request'])
@receiver(config_signal, dispatch_uid='setup_projector_config_variables') @receiver(config_signal, dispatch_uid='setup_projector_config_variables')
@ -64,8 +70,80 @@ def setup_projector_config_variables(sender, **kwargs):
name='up', name='up',
default_value=0) default_value=0)
return ConfigPage(title='No title here', projector_active_overlays = ConfigVariable(
url='bar', name='projector_active_overlays',
required_permission=None, default_value=[])
variables=(presentation, presentation_argument, projector_message, countdown_time,
countdown_start_stamp, countdown_pause_stamp, countdown_state, bigger, up)) return ConfigPage(
title='No title here', url='bar', required_permission=None, variables=(
presentation, presentation_argument, projector_message,
countdown_time, countdown_start_stamp, countdown_pause_stamp,
countdown_state, bigger, up, projector_active_overlays))
@receiver(projector_overlays, dispatch_uid="projector_countdown")
def countdown(sender, **kwargs):
"""
Reveiver for the countdown.
"""
name = 'projector_countdown'
request = kwargs.get('request', None)
def get_widget_html():
"""
Returns the the html-code to show in the overly-widget.
"""
context = {
'countdown_time': config['countdown_time'],
'countdown_state': config['countdown_state']}
context.update(csrf(request))
return render_to_string('projector/overlay_countdown_widget.html', context)
def get_projector_html():
"""
Returns an html-code to show on the projector.
"""
start = config['countdown_start_stamp']
duration = config['countdown_time']
pause = config['countdown_pause_stamp']
if config['countdown_state'] == 'active':
seconds = max(0, int(start + duration - time()))
elif config['countdown_state'] == 'paused':
seconds = max(0, int(start + duration - pause))
elif config['countdown_state'] == 'inactive':
seconds = max(0, int(duration))
else:
seconds = 0
if seconds == 0:
config['countdown_state'] = 'expired'
return render_to_string('projector/overlay_countdown_projector.html',
{'seconds': '%02d:%02d' % (seconds / 60, seconds % 60)})
return Overlay(name, get_widget_html, get_projector_html)
@receiver(projector_overlays, dispatch_uid="projector_message")
def projector_message(sender, **kwargs):
"""
Receiver to show the overlay_message on the projector or the form in the
overlay-widget on the dashboard.
"""
name = 'projector_message'
request = kwargs.get('request', None)
def get_widget_html():
"""
Returns the the html-code to show in the overly-widget.
"""
return render_to_string('projector/overlay_message_widget.html', csrf(request))
def get_projector_html():
"""
Returns an html-code to show on the projector.
"""
if config['projector_message']:
return render_to_string('projector/overlay_message_projector.html',
{'message': config['projector_message']})
return None
return Overlay(name, get_widget_html, get_projector_html)

View File

@ -88,11 +88,11 @@ $(function() {
dataType: 'json', dataType: 'json',
success: function(data) { success: function(data) {
if (data['active']) { if (data['active']) {
$('#' + data['def_name'] + '_active').show(); $('#' + data['name'] + '_active').show();
$('#' + data['def_name'] + '_inactive').hide(); $('#' + data['name'] + '_inactive').hide();
} else { } else {
$('#' + data['def_name'] + '_active').hide(); $('#' + data['name'] + '_active').hide();
$('#' + data['def_name'] + '_inactive').show(); $('#' + data['name'] + '_inactive').show();
} }
} }
}); });

View File

@ -8,7 +8,6 @@
content_hash = null; content_hash = null;
function presentation_reload() { function presentation_reload() {
if ($('#config > #ajax').html() == 'on') { if ($('#config > #ajax').html() == 'on') {
$.ajax({ $.ajax({
type: 'GET', type: 'GET',
@ -33,11 +32,8 @@ function presentation_reload() {
$('#scrollcontent').animate({'margin-top': data.up + 'em'}, 200); $('#scrollcontent').animate({'margin-top': data.up + 'em'}, 200);
// overlays // overlays
$('#overlays div').remove(); $('#overlays div').remove();
$('#overlays span').remove(); $.each(data['overlays'], function (index, overlay){
$.each(data['overlays'], function (index, value){ $('#overlays').append('<div id="overlay_' + overlay['name'] + '">' + overlay['html'] + '</div>');
if (value[0] != "Countdown")
$('#overlays').append('<span id="overlay_transparent"></span>')
$('#overlays').append('<div id="overlay_' + value[0] + '">' + value[1] + '</div>');
}); });
setTimeout("presentation_reload()", 1000); setTimeout("presentation_reload()", 1000);
}, },

View File

@ -73,32 +73,6 @@ body{
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
#overlays div {
}
#overlay_Countdown {
position: absolute;
right: 40px;
top: 0;
font-size: 4em;
font-weight: bold;
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
background: #DDD9D9;
padding: 0 1em;
}
#overlay_Message {
position: absolute;
top: 35%;
left: 10%;
width: 80%;
text-align: center;
border-radius: 0.2em;
background: #FFFFFF;
font-size: 2.75em;
padding: 0.2em 0;
}
/*** CONTENT ***/ /*** CONTENT ***/
#contentwrapper { #contentwrapper {

View File

@ -32,10 +32,9 @@
<div id="overlays"> <div id="overlays">
{% for overlay in overlays %} {% for overlay in overlays %}
{% if overlay.0 != "Countdown" %} <div id="overlay_{{ overlay.name }}">
<span id="overlay_transparent"></span> {{ overlay.html|safe }}
{% endif %} </div>
<div id="overlay_{{ overlay.0 }}">{{ overlay.1|safe }}</div>
{% endfor %} {% endfor %}
</div> </div>

View File

@ -1,21 +0,0 @@
{% load i18n %}
{% load tags %}
<div class="form-inline">
<div class="input-append">
<input class="projector_countdown_spinval" id="countdown_time" name="countdown_time" type="number" min="0" value="{{ countdown_time }}">
<span class="add-on">{% trans "s" context "seconds" %}</span>
</div>
<a id="countdown_set" class="countdown_control btn btn-mini" href="{% url 'countdown_set_default' %}" title="{% trans 'Save time as default' %}">
<i class="icon-refresh"></i>
</a>
<a id="countdown_reset" class="countdown_control btn" href="{% url 'countdown_reset' %}" title="{% trans 'Reset countdown' %}">
<i class="icon-fast-backward"></i>
</a>
<a id="countdown_play" class="countdown_control btn" href="{% url 'countdown_start' %}" title="{% trans 'Start countdown' %}"{% if countdown_state == 'active' %} style="display:none"{% endif %}>
<i class="icon-play"></i>
</a>
<a id="countdown_stop" class="countdown_control btn" href="{% url 'countdown_stop' %}" title="{% trans 'Stop countdown' %}"{% if countdown_state == 'inactive' or countdown_state == 'paused' or countdown_state == 'expired' %} style="display:none"{% endif %}>
<i class="icon-pause"></i>
</a>
</div>

View File

@ -1,14 +0,0 @@
{% load i18n %}
{% load tags %}
<form class="form-inline" id="overlay_message" action="{% url 'projector_overlay_message' %}" method="post">{% csrf_token %}
<div class="input-append" style="width: 85%;">
<input class="input-block-level" id="overlay_message_text" name='message_text' type='text' value="{% get_config 'projector_message' %}">
<button type="submit" class="btn btn-primary" name="message" title="{% trans 'Apply' %}" style="width: 16px;">
<i class="icon-ok icon-white"></i>
</button>
<button type="submit" class="btn" name='message-clean' title="{% trans 'Clean message' %}" style="width: 16px;">
<i class="icon-delete"></i>
</button>
</div>
</form>

View File

@ -0,0 +1,17 @@
<style type="text/css">
#overlay_countdown_inner {
position: fixed;
right: 40px;
top: 0;
font-size: 4em;
font-weight: bold;
border-bottom-left-radius: 0.5em;
border-bottom-right-radius: 0.5em;
background: #DDD9D9;
padding: 0 1em;
}
</style>
<div id="overlay_countdown_inner">
{{ seconds }}
</div>

View File

@ -0,0 +1,24 @@
{% load i18n %}
{% load tags %}
<span>
{% trans "Countdown for speaking time" %}:
<div>
<div class="input-append">
<input class="projector_countdown_spinval" id="countdown_time" name="countdown_time" type="number" min="0" value="{{ countdown_time }}">
<span class="add-on">{% trans "s" context "seconds" %}</span>
</div>
<a id="countdown_set" class="countdown_control btn btn-mini" href="{% url 'countdown_set_default' %}" title="{% trans 'Save time as default' %}">
<i class="icon-refresh"></i>
</a>
<a id="countdown_reset" class="countdown_control btn" href="{% url 'countdown_reset' %}" title="{% trans 'Reset countdown' %}">
<i class="icon-fast-backward"></i>
</a>
<a id="countdown_play" class="countdown_control btn" href="{% url 'countdown_start' %}" title="{% trans 'Start countdown' %}"{% if countdown_state == 'active' %} style="display:none"{% endif %}>
<i class="icon-play"></i>
</a>
<a id="countdown_stop" class="countdown_control btn" href="{% url 'countdown_stop' %}" title="{% trans 'Stop countdown' %}"{% if countdown_state == 'inactive' or countdown_state == 'paused' or countdown_state == 'expired' %} style="display:none"{% endif %}>
<i class="icon-pause"></i>
</a>
</div>
</span>

View File

@ -0,0 +1,19 @@
<style type="text/css">
#overlay_message_inner {
position: fixed;
top: 35%;
left: 10%;
width: 80%;
text-align: center;
border-radius: 0.2em;
background: #FFFFFF;
font-size: 2.75em;
padding: 0.2em 0;
}
</style>
<span id="overlay_transparent"></span>
<div id="overlay_message_inner">
{{ message }}
</span>

View File

@ -0,0 +1,17 @@
{% load i18n %}
{% load tags %}
<span>
{% trans "Message" %}:
<form class="form-inline" id="overlay_message" action="{% url 'projector_overlay_message' %}" method="post">{% csrf_token %}
<div class="input-append" style="width: 85%;">
<input class="input-block-level" id="overlay_message_text" name='message_text' type='text' value="{% get_config 'projector_message' %}">
<button type="submit" class="btn btn-primary" name="message" title="{% trans 'Apply' %}" style="width: 16px;">
<i class="icon-ok icon-white"></i>
</button>
<button type="submit" class="btn" name='message-clean' title="{% trans 'Clean message' %}" style="width: 16px;">
<i class="icon-delete"></i>
</button>
</div>
</form>
</span>

View File

@ -4,28 +4,19 @@
<ul class="overlay_list"> <ul class="overlay_list">
{% for overlay in overlays %} {% for overlay in overlays %}
<li> <li>
<a id="{{ overlay.def_name }}_active" <a id="{{ overlay.name }}_active"
href="{% url 'projector_overlay_deactivate' overlay.def_name %}" href="{% url 'projector_overlay_deactivate' overlay.name %}"
class="overlay_activate_link btn btn-mini btn-primary" class="overlay_activate_link btn btn-mini btn-primary"
style="{% if not overlay.active %}display:none;{% endif %}"> style="{% if not overlay.is_active %}display:none;{% endif %}">
<i class="icon-checked-new_white"></i> <i class="icon-checked-new_white"></i>
</a> </a>
<a id="{{ overlay.def_name }}_inactive" <a id="{{ overlay.name }}_inactive"
href="{% url 'projector_overlay_activate' overlay.def_name %}" href="{% url 'projector_overlay_activate' overlay.name %}"
class="overlay_activate_link btn btn-mini" class="overlay_activate_link btn btn-mini"
style="{% if overlay.active %}display:none;{% endif %}"> style="{% if overlay.is_active %}display:none;{% endif %}">
<i class="icon-unchecked-new"></i> <i class="icon-unchecked-new"></i>
</a> </a>
{{ overlay.get_widget_html|safe }}
{# TODO: Call the html via overlay.html #}
{% if overlay.def_name == "Countdown" %}
{% trans "Countdown for speaking time" %}:<br>
{% include 'projector/control_countdown.html' %}
{% endif %}
{% if overlay.def_name == "Message" %}
{% trans "Message" %}:<br>
{% include 'projector/control_overlay_message.html' %}
{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View File

@ -28,11 +28,10 @@ from openslides.utils.views import (
TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin) TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin)
from openslides.config.api import config from openslides.config.api import config
from .api import ( from .api import (
get_active_slide, set_active_slide, projector_message_set, get_active_slide, set_active_slide, get_slide_from_sid, get_all_widgets,
projector_message_delete, get_slide_from_sid, get_all_widgets,
clear_projector_cache) clear_projector_cache)
from .forms import SelectWidgetsForm from .forms import SelectWidgetsForm
from .models import ProjectorOverlay, ProjectorSlide from .models import ProjectorSlide
from .projector import Widget from .projector import Widget
from .signals import projector_overlays from .signals import projector_overlays
@ -80,18 +79,17 @@ class Projector(TemplateView, AjaxMixin):
'title': config['event_name'], 'title': config['event_name'],
'template': 'projector/default.html', 'template': 'projector/default.html',
} }
data['overlays'] = []
data['ajax'] = ajax data['ajax'] = ajax
# Projector Overlays # Projector overlays
data['overlays'] = []
# Do not show overlays on slide preview
if self.kwargs['sid'] is None: if self.kwargs['sid'] is None:
active_defs = ProjectorOverlay.objects.filter(active=True) \ for receiver, overlay in projector_overlays.send(sender=self):
.filter(Q(sid=active_sid) | Q(sid=None)).values_list( if overlay.is_active():
'def_name', flat=True) data['overlays'].append({'name': overlay.name,
for receiver, response in projector_overlays.send( 'html': overlay.get_projector_html()})
sender=sid, register=False, call=active_defs):
if response is not None:
data['overlays'].append(response)
self._data = data self._data = data
return data return data
@ -290,9 +288,9 @@ class OverlayMessageView(RedirectView):
def pre_post_redirect(self, request, *args, **kwargs): def pre_post_redirect(self, request, *args, **kwargs):
if 'message' in request.POST: if 'message' in request.POST:
projector_message_set(request.POST['message_text']) config['projector_message'] = request.POST['message_text']
elif 'message-clean' in request.POST: elif 'message-clean' in request.POST:
projector_message_delete() config['projector_message'] = ''
def get_ajax_context(self, **kwargs): def get_ajax_context(self, **kwargs):
clear_projector_cache() clear_projector_cache()
@ -309,28 +307,23 @@ class ActivateOverlay(RedirectView):
allow_ajax = True allow_ajax = True
permission_required = 'projector.can_manage_projector' permission_required = 'projector.can_manage_projector'
@property
def overlay(self):
try:
return self._overlay
except AttributeError:
self._overlay = ProjectorOverlay.objects.get(
def_name=self.kwargs['name'])
return self._overlay
def pre_redirect(self, request, *args, **kwargs): def pre_redirect(self, request, *args, **kwargs):
self.name = kwargs['name']
active_overlays = config['projector_active_overlays']
if kwargs['activate']: if kwargs['activate']:
self.overlay.active = True if self.name not in active_overlays:
else: active_overlays.append(self.name)
self.overlay.active = False config['projector_active_overlays'] = active_overlays
self.overlay.save() self.active = True
elif not kwargs['activate']:
if self.name in active_overlays:
active_overlays.remove(self.name)
config['projector_active_overlays'] = active_overlays
self.active = False
def get_ajax_context(self, **kwargs): def get_ajax_context(self, **kwargs):
clear_projector_cache() clear_projector_cache()
return { return {'active': self.active, 'name': self.name}
'active': self.overlay.active,
'def_name': self.overlay.def_name,
}
class CustomSlideCreateView(CreateView): class CustomSlideCreateView(CreateView):
@ -406,21 +399,10 @@ def get_widgets(request):
# Overlay Widget # Overlay Widget
overlays = [] overlays = []
for receiver, name in projector_overlays.send(sender='registerer', for receiver, overlay in projector_overlays.send(sender='overlay_widget', request=request):
register=True): overlays.append(overlay)
if name is not None:
try:
projector_overlay = ProjectorOverlay.objects.get(
def_name=name)
except ProjectorOverlay.DoesNotExist:
projector_overlay = ProjectorOverlay(def_name=name, active=False)
projector_overlay.save()
overlays.append(projector_overlay)
context = { context = {'overlays': overlays}
'overlays': overlays,
'countdown_time': config['countdown_time'],
'countdown_state': config['countdown_state']}
context.update(csrf(request)) context.update(csrf(request))
widgets.append(Widget( widgets.append(Widget(
name='overlays', name='overlays',