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
117
117
from mako.template import Template
118
from PyQt4 import QtCore
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
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'
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')
174
175
def do_post_processor(self):
310
311
Translate various strings in the mobile app.
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'),
340
def serve_file(self, file_name=None):
344
def stages(self, url_path, file_name):
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.
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
348
352
log.debug('serve file request %s' % 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('/')
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)
363
def _process_file(self, path):
365
Common file processing code
367
:param path: path to file to be loaded
368
:return: web resource to be loaded
359
371
ext, content_type = self.get_content_type(path)
360
372
file_handle = None
377
389
self.end_headers()
392
def serve_file(self, file_name=None):
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.
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'
400
log.debug('serve file request %s' % 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)
380
412
def get_content_type(self, file_name):
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
417
:param file_name: name of file
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):
391
425
Serve an image file. If not found return 404.
427
:param file_name: file name to be served
428
:param dimensions: image size
429
:param controller_name: controller to be called
393
431
log.debug('serve thumbnail %s/thumbnails%s/%s' % (controller_name, dimensions, file_name))
394
432
supported_controllers = ['presentations', 'images']
469
507
:param action: This is the action, either ``hide`` or ``show``.
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()
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])
493
531
def controller_text(self, var):
495
533
Perform an action on the slide controller.
535
:param var: variable - not used
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.
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:
555
595
data = json.loads(self.request_data)['request']['id']
557
597
return self.do_http_error()
559
599
# This slot expects an int within a list.
560
self.live_controller.emit(QtCore.SIGNAL(event), [data])
562
self.live_controller.emit(QtCore.SIGNAL(event))
563
603
json_data = {'results': {'success': True}}
564
604
self.do_json_header()
565
605
return json.dumps(json_data).encode()
579
619
:param action: The action to perform.
581
event = 'servicemanager_%s_item' % action
621
event = getattr(self.service_manager, 'servicemanager_%s_item' % action)
582
622
if self.request_data:
584
624
data = json.loads(self.request_data)['request']['id']
586
626
return self.do_http_error()
587
self.service_manager.emit(QtCore.SIGNAL(event), data)
589
self.service_manager.emit(QtCore.SIGNAL(event))
590
630
self.do_json_header()
591
631
return json.dumps({'results': {'success': True}}).encode()
626
666
def go_live(self, plugin_name):
628
668
Go live on an item of type ``plugin``.
670
:param plugin_name: name of plugin
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()
639
681
def add_to_service(self, plugin_name):
641
683
Add item of type ``plugin_name`` to the end of the service.
685
:param plugin_name: name of plugin to be called
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()