#!/usr/bin/env python # -*- coding: utf-8 -*- """ openslides.projector.views ~~~~~~~~~~~~~~~~~~~~~~~~~~ Views for the projector app. :copyright: 2011, 2012 by OpenSlides team, see AUTHORS. :license: GNU GPL, see LICENSE for more details. """ from datetime import datetime from time import time from django.conf import settings from django.contrib import messages from django.core.urlresolvers import reverse from django.db.models import Q from django.dispatch import receiver from django.utils.datastructures import SortedDict from django.utils.importlib import import_module from django.utils.translation import ugettext_lazy as _ from openslides.utils.template import render_block_to_string, Tab from openslides.utils.utils import html_strong from openslides.utils.views import (TemplateView, RedirectView, CreateView, UpdateView, DeleteView, AjaxMixin) from openslides.config.models import config from openslides.projector.api import (get_active_slide, set_active_slide, projector_message_set, projector_message_delete, get_slide_from_sid) from openslides.projector.models import ProjectorOverlay, ProjectorSlide from openslides.projector.projector import SLIDE, Widget from openslides.projector.signals import projector_overlays class ControlView(TemplateView, AjaxMixin): """ Overview over all possible slides, the overlays and a liveview. """ template_name = 'projector/control.html' permission_required = 'projector.can_manage_projector' def get_projector_overlays(self): 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: active = name == 'Message' projector_overlay = ProjectorOverlay(def_name=name, active=active) projector_overlay.save() overlays.append(projector_overlay) return overlays def post(self, request, *args, **kwargs): if 'message' in request.POST: projector_message_set(request.POST['message_text']) elif 'message-clean' in request.POST: projector_message_delete() if request.is_ajax(): return self.ajax_get(request, *args, **kwargs) return self.get(request, *args, **kwargs) def get_ajax_context(self, **kwargs): return { 'overlay_message': config['projector_message'], } def get_context_data(self, **kwargs): context = super(ControlView, self).get_context_data(**kwargs) widgets = SortedDict() for app in settings.INSTALLED_APPS: try: mod = import_module(app + '.views') except ImportError: continue appname = mod.__name__.split('.')[0] try: modul_widgets = mod.get_widgets(self.request) except AttributeError: continue for widget in modul_widgets: widgets[widget.get_name()] = widget context.update({ 'countdown_time': config['countdown_time'], 'countdown_state' : config['countdown_state'], 'overlays': self.get_projector_overlays(), 'widgets': widgets, }) return context class Projector(TemplateView, AjaxMixin): """ The Projector-Page. """ permission_required = 'projector.can_see_projector' @property def data(self): try: return self._data except AttributeError: pass sid = self.kwargs['sid'] if sid is None: try: data = get_active_slide() except AttributeError: #TODO: It has to be an Slide.DoesNotExist data = None ajax = 'on' else: data = get_slide_from_sid(sid) ajax = 'off' if data is None: data = { 'title': config['event_name'], 'template': 'projector/default.html', } data['overlays'] = [] data['ajax'] = ajax # Projector Overlays if self.kwargs['sid'] is None: active_defs = ProjectorOverlay.objects.filter(active=True) \ .filter(Q(sid=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) self._data = data return data def get_template_names(self): return [self.data['template']] def get_context_data(self, **kwargs): context = super(Projector, self).get_context_data(**kwargs) context.update(self.data) return context def get_ajax_context(self, **kwargs): content = render_block_to_string(self.get_template_names()[0], 'content', self.data) scrollcontent = render_block_to_string(self.get_template_names()[0], 'scrollcontent', self.data) context = super(Projector, self).get_ajax_context(**kwargs) content_hash = hash(content) context.update({ 'content': content, 'scrollcontent': scrollcontent, 'time': datetime.now().strftime('%H:%M'), 'overlays': self.data['overlays'], 'title': self.data['title'], 'bigger': config['bigger'], 'up': config['up'], 'content_hash': content_hash, }) return context def get(self, request, *args, **kwargs): if request.is_ajax(): return self.ajax_get(request, *args, **kwargs) return super(Projector, self).get(request, *args, **kwargs) class ActivateView(RedirectView): """ Activate a Slide. """ permission_required = 'projector.can_manage_projector' url = 'projector_control' allow_ajax = True def pre_redirect(self, request, *args, **kwargs): try: set_active_slide(kwargs['sid'], kwargs['argument']) except KeyError: set_active_slide(kwargs['sid']) config['up'] = 0 config['bigger'] = 100 class ProjectorEdit(RedirectView): """ Scale or scroll the projector. """ permission_required = 'projector.can_manage_projector' url = 'projector_control' allow_ajax = True def pre_redirect(self, request, *args, **kwargs): direction = kwargs['direction'] if direction == 'bigger': config['bigger'] = int(config['bigger']) + 20 elif direction == 'smaller': config['bigger'] = int(config['bigger']) - 20 elif direction == 'up': config['up'] = int(config['up']) - 10 elif direction == 'down': if config['up'] < 0: config['up'] = int(config['up']) + 10 elif direction == 'clean': config['up'] = 0 config['bigger'] = 100 class CountdownEdit(RedirectView): """ Start, stop or reset the countdown. """ permission_required = 'projector.can_manage_projector' url = 'projector_control' allow_ajax = True def pre_redirect(self, request, *args, **kwargs): command = kwargs['command'] # countdown_state is one of 'inactive', 'paused' and 'active', 'expired' if command in ['reset', 'start', 'stop']: config['countdown_time'] = config['countdown_time'] if command == 'reset': config['countdown_start_stamp'] = time() config['countdown_pause_stamp'] = 0 config['countdown_state'] = 'inactive' elif command == 'start': # if we had stopped the countdown resume were we left of if config['countdown_state'] == 'paused': start_stamp = config['countdown_start_stamp'] pause_stamp = config['countdown_pause_stamp'] now = time() config['countdown_start_stamp'] = now - \ (pause_stamp - start_stamp) else: config['countdown_start_stamp'] = time() config['countdown_state'] = 'active' config['countdown_pause_stamp'] = 0 elif command == 'stop': if config['countdown_state'] == 'active': config['countdown_pause_stamp'] = time() config['countdown_state'] = 'paused' elif command == 'set-default': try: config['countdown_time'] = \ int(self.request.GET['countdown_time']) except ValueError: pass except AttributeError: pass def get_ajax_context(self, **kwargs): return { 'state': config['countdown_state'], 'countdown_time': config['countdown_time'], } class ActivateOverlay(RedirectView): """ Activate or deactivate an overlay. """ url = 'projector_control' 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): if kwargs['activate']: self.overlay.active = True else: self.overlay.active = False self.overlay.save() def get_ajax_context(self, **kwargs): return { 'active': self.overlay.active, 'def_name': self.overlay.def_name, } class CustomSlideCreateView(CreateView): """ Create a custom slide. """ permission_required = 'agenda.can_manage_agenda' template_name = 'projector/new.html' model = ProjectorSlide context_object_name = 'customslide' success_url = 'projector_control' apply_url = 'customslide_edit' class CustomSlideUpdateView(UpdateView): """ Update a custom slide. """ permission_required = 'projector.can_manage_projector' template_name = 'projector/new.html' model = ProjectorSlide context_object_name = 'customslide' success_url = 'projector_control' apply_url = 'customslide_edit' class CustomSlideDeleteView(DeleteView): """ Delete a custom slide. """ permission_required = 'projector.can_manage_projector' model = ProjectorSlide url = 'projector_control' def register_tab(request): """ Register the projector tab. """ selected = True if request.path.startswith('/projector/') else False return Tab( title=_('Projector'), url=reverse('projector_control'), permission=request.user.has_perm('projector.can_manage_projector'), selected=selected, ) def get_widgets(request): """ Return the custom slide widget. """ return [ Widget( name='projector', template='projector/widget.html', context={ 'slides': ProjectorSlide.objects.all(), 'welcomepage_is_active': not bool(config["presentation"]), } ), ]