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,)
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):
widgets = SortedDict()
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')
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 openslides.config.api import config
from openslides.projector.signals import projector_overlays
SLIDE = {}
@ -146,35 +145,26 @@ class Widget(object):
return unicode(self.display_name)
@receiver(projector_overlays, dispatch_uid="projector_countdown")
def countdown(sender, **kwargs):
name = 'Countdown'
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
class Overlay(object):
"""
Represents an overlay which can be seen on the projector.
"""
if seconds == 0:
config['countdown_state'] = 'expired'
def __init__(self, name, get_widget_html, get_projector_html):
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))
return None
def get_widget_html(self):
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 projector_message(sender, **kwargs):
name = 'Message'
if kwargs['register']:
return name
if name in kwargs['call']:
message = config['projector_message']
if message != '':
return (name, message)
return None
def is_active(self):
return (self.name in config['projector_active_overlays'] and
self.get_projector_html() is not None)

View File

@ -9,16 +9,22 @@
:copyright: 20112013 by OpenSlides team, see AUTHORS.
:license: GNU GPL, see LICENSE for more details.
"""
from time import time
from django.dispatch import Signal, receiver
from django import forms
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.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')
@ -64,8 +70,80 @@ def setup_projector_config_variables(sender, **kwargs):
name='up',
default_value=0)
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 = ConfigVariable(
name='projector_active_overlays',
default_value=[])
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',
success: function(data) {
if (data['active']) {
$('#' + data['def_name'] + '_active').show();
$('#' + data['def_name'] + '_inactive').hide();
$('#' + data['name'] + '_active').show();
$('#' + data['name'] + '_inactive').hide();
} else {
$('#' + data['def_name'] + '_active').hide();
$('#' + data['def_name'] + '_inactive').show();
$('#' + data['name'] + '_active').hide();
$('#' + data['name'] + '_inactive').show();
}
}
});

View File

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

View File

@ -73,32 +73,6 @@ body{
width: 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 ***/
#contentwrapper {

View File

@ -32,10 +32,9 @@
<div id="overlays">
{% for overlay in overlays %}
{% if overlay.0 != "Countdown" %}
<span id="overlay_transparent"></span>
{% endif %}
<div id="overlay_{{ overlay.0 }}">{{ overlay.1|safe }}</div>
<div id="overlay_{{ overlay.name }}">
{{ overlay.html|safe }}
</div>
{% endfor %}
</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">
{% for overlay in overlays %}
<li>
<a id="{{ overlay.def_name }}_active"
href="{% url 'projector_overlay_deactivate' overlay.def_name %}"
<a id="{{ overlay.name }}_active"
href="{% url 'projector_overlay_deactivate' overlay.name %}"
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>
</a>
<a id="{{ overlay.def_name }}_inactive"
href="{% url 'projector_overlay_activate' overlay.def_name %}"
<a id="{{ overlay.name }}_inactive"
href="{% url 'projector_overlay_activate' overlay.name %}"
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>
</a>
{# 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 %}
{{ overlay.get_widget_html|safe }}
</li>
{% endfor %}
</ul>

View File

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