~suutari-olli/openlp/escape-fixes-1294111-1497637

« back to all changes in this revision

Viewing changes to openlp/plugins/remotes/lib/httprouter.py

  • Committer: suutari-olli
  • Date: 2016-01-07 02:53:59 UTC
  • mfrom: (2557.2.31 openlp)
  • Revision ID: suutari.olli@gmail.com-20160107025359-q2feybbwxaoihqxr
Merge to trunk on 1/7/2015.

I noticed this branch also seems to be fixing this bug:
https://bugs.launchpad.net/openlp/+bug/1531691

However, escape item still remains buggy with problems related to resuming
video and presentations.

Show diffs side-by-side

added added

removed removed

Lines of Context:
4
4
###############################################################################
5
5
# OpenLP - Open Source Lyrics Projection                                      #
6
6
# --------------------------------------------------------------------------- #
7
 
# Copyright (c) 2008-2015 OpenLP Developers                                   #
 
7
# Copyright (c) 2008-2016 OpenLP Developers                                   #
8
8
# --------------------------------------------------------------------------- #
9
9
# This program is free software; you can redistribute it and/or modify it     #
10
10
# under the terms of the GNU General Public License as published by the Free  #
115
115
from urllib.parse import urlparse, parse_qs
116
116
 
117
117
from mako.template import Template
118
 
from PyQt4 import QtCore
119
118
 
120
119
from openlp.core.common import RegistryProperties, AppLocation, Settings, translate, UiStrings
121
120
from openlp.core.lib import PluginStatus, StringContent, image_to_byte, ItemCapabilities, create_thumb
150
149
        self.routes = [
151
150
            ('^/$', {'function': self.serve_file, 'secure': False}),
152
151
            ('^/(stage)$', {'function': self.serve_file, 'secure': False}),
 
152
            ('^/(stage)/(.*)$', {'function': self.stages, 'secure': False}),
153
153
            ('^/(main)$', {'function': self.serve_file, 'secure': False}),
154
154
            (r'^/files/(.*)$', {'function': self.serve_file, 'secure': False}),
155
155
            (r'^/(\w+)/thumbnails([^/]+)?/(.*)$', {'function': self.serve_thumbnail, 'secure': False}),
170
170
        self.settings_section = 'remotes'
171
171
        self.translate()
172
172
        self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), 'remotes', 'html')
 
173
        self.config_dir = os.path.join(AppLocation.get_data_path(), 'stages')
173
174
 
174
175
    def do_post_processor(self):
175
176
        """
309
310
        """
310
311
        Translate various strings in the mobile app.
311
312
        """
 
313
        remote = translate('RemotePlugin.Mobile', 'Remote')
 
314
        stage = translate('RemotePlugin.Mobile', 'Stage View')
 
315
        live = translate('RemotePlugin.Mobile', 'Live View')
312
316
        self.template_vars = {
313
 
            'app_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Remote'),
314
 
            'stage_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Stage View'),
315
 
            'live_title': translate('RemotePlugin.Mobile', 'OpenLP 2.2 Live View'),
 
317
            'app_title': "%s %s" % (UiStrings().OLPV2x, remote),
 
318
            'stage_title': "%s %s" % (UiStrings().OLPV2x, stage),
 
319
            'live_title': "%s %s" % (UiStrings().OLPV2x, live),
316
320
            'service_manager': translate('RemotePlugin.Mobile', 'Service Manager'),
317
321
            'slide_controller': translate('RemotePlugin.Mobile', 'Slide Controller'),
318
322
            'alerts': translate('RemotePlugin.Mobile', 'Alerts'),
337
341
            'settings': translate('RemotePlugin.Mobile', 'Settings'),
338
342
        }
339
343
 
340
 
    def serve_file(self, file_name=None):
 
344
    def stages(self, url_path, file_name):
341
345
        """
342
 
        Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
343
 
        If subfolders requested return 404, easier for security for the present.
 
346
        Allow Stage view to be delivered with custom views.
344
347
 
345
 
        Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
346
 
        where xx is the language, e.g. 'en'
 
348
        :param url_path: base path of the URL. Not used but passed by caller
 
349
        :param file_name: file name with path
 
350
        :return:
347
351
        """
348
352
        log.debug('serve file request %s' % file_name)
349
 
        if not file_name:
350
 
            file_name = 'index.html'
351
 
        elif file_name == 'stage':
352
 
            file_name = 'stage.html'
353
 
        elif file_name == 'main':
354
 
            file_name = 'main.html'
355
 
        path = os.path.normpath(os.path.join(self.html_dir, file_name))
356
 
        if not path.startswith(self.html_dir):
 
353
        parts = file_name.split('/')
 
354
        if len(parts) == 1:
 
355
            file_name = os.path.join(parts[0], 'stage.html')
 
356
        elif len(parts) == 3:
 
357
            file_name = os.path.join(parts[1], parts[2])
 
358
        path = os.path.normpath(os.path.join(self.config_dir, file_name))
 
359
        if not path.startswith(self.config_dir):
357
360
            return self.do_not_found()
 
361
        return self._process_file(path)
 
362
 
 
363
    def _process_file(self, path):
 
364
        """
 
365
        Common file processing code
 
366
 
 
367
        :param path: path to file to be loaded
 
368
        :return: web resource to be loaded
 
369
        """
358
370
        content = None
359
371
        ext, content_type = self.get_content_type(path)
360
372
        file_handle = None
377
389
        self.end_headers()
378
390
        return content
379
391
 
 
392
    def serve_file(self, file_name=None):
 
393
        """
 
394
        Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
 
395
        If subfolders requested return 404, easier for security for the present.
 
396
 
 
397
        Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
 
398
        where xx is the language, e.g. 'en'
 
399
        """
 
400
        log.debug('serve file request %s' % file_name)
 
401
        if not file_name:
 
402
            file_name = 'index.html'
 
403
        elif file_name == 'stage':
 
404
            file_name = 'stage.html'
 
405
        elif file_name == 'main':
 
406
            file_name = 'main.html'
 
407
        path = os.path.normpath(os.path.join(self.html_dir, file_name))
 
408
        if not path.startswith(self.html_dir):
 
409
            return self.do_not_found()
 
410
        return self._process_file(path)
 
411
 
380
412
    def get_content_type(self, file_name):
381
413
        """
382
414
        Examines the extension of the file and determines what the content_type should be, defaults to text/plain
383
415
        Returns the extension and the content_type
 
416
 
 
417
        :param file_name: name of file
384
418
        """
385
419
        ext = os.path.splitext(file_name)[1]
386
420
        content_type = FILE_TYPES.get(ext, 'text/plain')
389
423
    def serve_thumbnail(self, controller_name=None, dimensions=None, file_name=None):
390
424
        """
391
425
        Serve an image file. If not found return 404.
 
426
 
 
427
        :param file_name: file name to be served
 
428
        :param dimensions: image size
 
429
        :param controller_name: controller to be called
392
430
        """
393
431
        log.debug('serve thumbnail %s/thumbnails%s/%s' % (controller_name, dimensions, file_name))
394
432
        supported_controllers = ['presentations', 'images']
468
506
 
469
507
        :param action: This is the action, either ``hide`` or ``show``.
470
508
        """
471
 
        self.live_controller.emit(QtCore.SIGNAL('slidecontroller_toggle_display'), action)
 
509
        self.live_controller.slidecontroller_toggle_display.emit(action)
472
510
        self.do_json_header()
473
511
        return json.dumps({'results': {'success': True}}).encode()
474
512
 
483
521
            except KeyError:
484
522
                return self.do_http_error()
485
523
            text = urllib.parse.unquote(text)
486
 
            self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text])
 
524
            self.alerts_manager.alerts_text.emit([text])
487
525
            success = True
488
526
        else:
489
527
            success = False
493
531
    def controller_text(self, var):
494
532
        """
495
533
        Perform an action on the slide controller.
 
534
 
 
535
        :param var: variable - not used
496
536
        """
497
537
        log.debug("controller_text var = %s" % var)
498
538
        current_item = self.live_controller.service_item
549
589
        :param display_type: This is the type of slide controller, either ``preview`` or ``live``.
550
590
        :param action: The action to perform.
551
591
        """
552
 
        event = 'slidecontroller_%s_%s' % (display_type, action)
 
592
        event = getattr(self.live_controller, 'slidecontroller_%s_%s' % (display_type, action))
553
593
        if self.request_data:
554
594
            try:
555
595
                data = json.loads(self.request_data)['request']['id']
557
597
                return self.do_http_error()
558
598
            log.info(data)
559
599
            # This slot expects an int within a list.
560
 
            self.live_controller.emit(QtCore.SIGNAL(event), [data])
 
600
            event.emit([data])
561
601
        else:
562
 
            self.live_controller.emit(QtCore.SIGNAL(event))
 
602
            event.emit()
563
603
        json_data = {'results': {'success': True}}
564
604
        self.do_json_header()
565
605
        return json.dumps(json_data).encode()
578
618
 
579
619
        :param action: The action to perform.
580
620
        """
581
 
        event = 'servicemanager_%s_item' % action
 
621
        event = getattr(self.service_manager, 'servicemanager_%s_item' % action)
582
622
        if self.request_data:
583
623
            try:
584
624
                data = json.loads(self.request_data)['request']['id']
585
625
            except KeyError:
586
626
                return self.do_http_error()
587
 
            self.service_manager.emit(QtCore.SIGNAL(event), data)
 
627
            event.emit(data)
588
628
        else:
589
 
            self.service_manager.emit(QtCore.SIGNAL(event))
 
629
            event.emit()
590
630
        self.do_json_header()
591
631
        return json.dumps({'results': {'success': True}}).encode()
592
632
 
626
666
    def go_live(self, plugin_name):
627
667
        """
628
668
        Go live on an item of type ``plugin``.
 
669
 
 
670
        :param plugin_name: name of plugin
629
671
        """
630
672
        try:
631
673
            request_id = json.loads(self.request_data)['request']['id']
633
675
            return self.do_http_error()
634
676
        plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
635
677
        if plugin.status == PluginStatus.Active and plugin.media_item:
636
 
            plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [request_id, True])
 
678
            getattr(plugin.media_item, '%s_go_live' % plugin_name).emit([request_id, True])
637
679
        return self.do_http_success()
638
680
 
639
681
    def add_to_service(self, plugin_name):
640
682
        """
641
683
        Add item of type ``plugin_name`` to the end of the service.
 
684
 
 
685
        :param plugin_name: name of plugin to be called
642
686
        """
643
687
        try:
644
688
            request_id = json.loads(self.request_data)['request']['id']
647
691
        plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
648
692
        if plugin.status == PluginStatus.Active and plugin.media_item:
649
693
            item_id = plugin.media_item.create_item_from_id(request_id)
650
 
            plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True])
 
694
            getattr(plugin.media_item, '%s_add_to_service' % plugin_name).emit([item_id, True])
651
695
        self.do_http_success()