forked from openlp/openlp
r2231
This commit is contained in:
commit
58b21030e0
@ -103,6 +103,9 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
self.retranslateUi()
|
||||
self.auto_select_id = -1
|
||||
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
|
||||
|
||||
def required_icons(self):
|
||||
"""
|
||||
@ -481,6 +484,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
else:
|
||||
self.go_live()
|
||||
|
||||
def go_live_remote(self, message):
|
||||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item_id:Remote.
|
||||
"""
|
||||
self.go_live(message[0], remote=message[1])
|
||||
|
||||
def go_live(self, item_id=None, remote=False):
|
||||
"""
|
||||
Make the currently selected item go live.
|
||||
@ -523,6 +535,15 @@ class MediaManagerItem(QtGui.QWidget):
|
||||
for item in items:
|
||||
self.add_to_service(item)
|
||||
|
||||
def add_to_service_remote(self, message):
|
||||
"""
|
||||
Remote Call wrapper
|
||||
|
||||
``message``
|
||||
The passed data item:Remote.
|
||||
"""
|
||||
self.add_to_service(message[0], remote=message[1])
|
||||
|
||||
def add_to_service(self, item=None, replace=None, remote=False):
|
||||
"""
|
||||
Add this item to the current service.
|
||||
|
@ -103,7 +103,7 @@ class Plugin(QtCore.QObject):
|
||||
``add_export_menu_Item(export_menu)``
|
||||
Add an item to the Export menu.
|
||||
|
||||
``create_settings_Tab()``
|
||||
``create_settings_tab()``
|
||||
Creates a new instance of SettingsTabItem to be used in the Settings
|
||||
dialog.
|
||||
|
||||
@ -252,7 +252,7 @@ class Plugin(QtCore.QObject):
|
||||
"""
|
||||
pass
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create a tab for the settings window to display the configurable options
|
||||
for this plugin to the user.
|
||||
|
@ -153,7 +153,7 @@ class PluginManager(object):
|
||||
"""
|
||||
for plugin in self.plugins:
|
||||
if plugin.status is not PluginStatus.Disabled:
|
||||
plugin.create_settings_Tab(self.settings_form)
|
||||
plugin.create_settings_tab(self.settings_form)
|
||||
|
||||
def hook_import_menu(self):
|
||||
"""
|
||||
|
@ -62,12 +62,10 @@ class ItemCapabilities(object):
|
||||
tab when making the previous item live.
|
||||
|
||||
``CanEdit``
|
||||
The capability to allow the ServiceManager to allow the item to be
|
||||
edited
|
||||
The capability to allow the ServiceManager to allow the item to be edited
|
||||
|
||||
``CanMaintain``
|
||||
The capability to allow the ServiceManager to allow the item to be
|
||||
reordered.
|
||||
The capability to allow the ServiceManager to allow the item to be reordered.
|
||||
|
||||
``RequiresMedia``
|
||||
Determines is the service_item needs a Media Player
|
||||
|
@ -77,6 +77,11 @@ try:
|
||||
ICU_VERSION = u'OK'
|
||||
except ImportError:
|
||||
ICU_VERSION = u'-'
|
||||
try:
|
||||
import cherrypy
|
||||
CHERRYPY_VERSION = cherrypy.__version__
|
||||
except ImportError:
|
||||
CHERRYPY_VERSION = u'-'
|
||||
try:
|
||||
import uno
|
||||
arg = uno.createUnoStruct(u'com.sun.star.beans.PropertyValue')
|
||||
@ -151,6 +156,7 @@ class ExceptionForm(QtGui.QDialog, Ui_ExceptionDialog):
|
||||
u'PyEnchant: %s\n' % ENCHANT_VERSION + \
|
||||
u'PySQLite: %s\n' % SQLITE_VERSION + \
|
||||
u'Mako: %s\n' % MAKO_VERSION + \
|
||||
u'CherryPy: %s\n' % CHERRYPY_VERSION + \
|
||||
u'pyICU: %s\n' % ICU_VERSION + \
|
||||
u'pyUNO bridge: %s\n' % UNO_VERSION + \
|
||||
u'VLC: %s\n' % VLC_VERSION
|
||||
|
@ -273,7 +273,6 @@ class ServiceManagerDialog(object):
|
||||
Registry().register_function(u'config_screen_changed', self.regenerate_service_Items)
|
||||
Registry().register_function(u'theme_update_global', self.theme_change)
|
||||
Registry().register_function(u'mediaitem_suffix_reset', self.reset_supported_suffixes)
|
||||
Registry().register_function(u'servicemanager_set_item', self.on_set_item)
|
||||
|
||||
def drag_enter_event(self, event):
|
||||
"""
|
||||
@ -315,6 +314,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
self.layout.setSpacing(0)
|
||||
self.layout.setMargin(0)
|
||||
self.setup_ui(self)
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'servicemanager_set_item'), self.on_set_item)
|
||||
|
||||
def set_modified(self, modified=True):
|
||||
"""
|
||||
@ -993,7 +994,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
|
||||
|
||||
def on_set_item(self, message):
|
||||
"""
|
||||
Called by a signal to select a specific item.
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
"""
|
||||
self.set_item(int(message))
|
||||
|
||||
|
@ -96,6 +96,7 @@ class SettingsForm(QtGui.QDialog, Ui_SettingsDialog):
|
||||
"""
|
||||
Process the form saving the settings
|
||||
"""
|
||||
log.debug(u'Processing settings exit')
|
||||
for tabIndex in range(self.stacked_layout.count()):
|
||||
self.stacked_layout.widget(tabIndex).save()
|
||||
# if the display of image background are changing we need to regenerate the image cache
|
||||
|
@ -360,8 +360,9 @@ class SlideController(DisplayController):
|
||||
# Signals
|
||||
self.preview_list_widget.clicked.connect(self.onSlideSelected)
|
||||
if self.is_live:
|
||||
# Need to use event as called across threads and UI is updated
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_toggle_display'), self.toggle_display)
|
||||
Registry().register_function(u'slidecontroller_live_spin_delay', self.receive_spin_delay)
|
||||
Registry().register_function(u'slidecontroller_toggle_display', self.toggle_display)
|
||||
self.toolbar.set_widget_visible(self.loop_list, False)
|
||||
self.toolbar.set_widget_visible(self.wide_menu, False)
|
||||
else:
|
||||
@ -373,13 +374,16 @@ class SlideController(DisplayController):
|
||||
else:
|
||||
self.preview_list_widget.addActions([self.nextItem, self.previous_item])
|
||||
Registry().register_function(u'slidecontroller_%s_stop_loop' % self.type_prefix, self.on_stop_loop)
|
||||
Registry().register_function(u'slidecontroller_%s_next' % self.type_prefix, self.on_slide_selected_next)
|
||||
Registry().register_function(u'slidecontroller_%s_previous' % self.type_prefix, self.on_slide_selected_previous)
|
||||
Registry().register_function(u'slidecontroller_%s_change' % self.type_prefix, self.on_slide_change)
|
||||
Registry().register_function(u'slidecontroller_%s_set' % self.type_prefix, self.on_slide_selected_index)
|
||||
Registry().register_function(u'slidecontroller_%s_blank' % self.type_prefix, self.on_slide_blank)
|
||||
Registry().register_function(u'slidecontroller_%s_unblank' % self.type_prefix, self.on_slide_unblank)
|
||||
Registry().register_function(u'slidecontroller_update_slide_limits', self.update_slide_limits)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_set' % self.type_prefix),
|
||||
self.on_slide_selected_index)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_next' % self.type_prefix),
|
||||
self.on_slide_selected_next)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'slidecontroller_%s_previous' % self.type_prefix),
|
||||
self.on_slide_selected_previous)
|
||||
|
||||
def _slideShortcutActivated(self):
|
||||
"""
|
||||
|
@ -49,10 +49,12 @@ class AlertsManager(QtCore.QObject):
|
||||
|
||||
def __init__(self, parent):
|
||||
QtCore.QObject.__init__(self, parent)
|
||||
Registry().register(u'alerts_manager', self)
|
||||
self.timer_id = 0
|
||||
self.alert_list = []
|
||||
Registry().register_function(u'live_display_active', self.generate_alert)
|
||||
Registry().register_function(u'alerts_text', self.alert_text)
|
||||
QtCore.QObject.connect(self, QtCore.SIGNAL(u'alerts_text'), self.alert_text)
|
||||
|
||||
def alert_text(self, message):
|
||||
"""
|
||||
|
@ -55,6 +55,7 @@ UGLY_CHARS = {
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BGExtract(object):
|
||||
"""
|
||||
Extract verses from BibleGateway
|
||||
@ -671,6 +672,7 @@ class HTTPBible(BibleDB):
|
||||
|
||||
application = property(_get_application)
|
||||
|
||||
|
||||
def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
pre_parse_substitute=None, cleaner=None):
|
||||
"""
|
||||
@ -715,6 +717,7 @@ def get_soup_for_bible_ref(reference_url, header=None, pre_parse_regex=None,
|
||||
Registry().get(u'application').process_events()
|
||||
return soup
|
||||
|
||||
|
||||
def send_error_message(error_type):
|
||||
"""
|
||||
Send a standard error message informing the user of an issue.
|
||||
|
@ -54,7 +54,7 @@ class MediaPlugin(Plugin):
|
||||
# passed with drag and drop messages
|
||||
self.dnd_id = u'Media'
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create the settings Tab
|
||||
"""
|
||||
|
@ -69,7 +69,7 @@ class PresentationPlugin(Plugin):
|
||||
self.icon_path = u':/plugins/plugin_presentations.png'
|
||||
self.icon = build_icon(self.icon_path)
|
||||
|
||||
def create_settings_Tab(self, parent):
|
||||
def create_settings_tab(self, parent):
|
||||
"""
|
||||
Create the settings Tab
|
||||
"""
|
||||
|
@ -147,7 +147,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/api/poll",
|
||||
"/stage/api/poll",
|
||||
function (data, status) {
|
||||
var prevItem = OpenLP.currentItem;
|
||||
OpenLP.currentSlide = data.results.slide;
|
||||
|
@ -26,7 +26,7 @@
|
||||
window.OpenLP = {
|
||||
loadService: function (event) {
|
||||
$.getJSON(
|
||||
"/api/service/list",
|
||||
"/stage/api/service/list",
|
||||
function (data, status) {
|
||||
OpenLP.nextSong = "";
|
||||
$("#notes").html("");
|
||||
@ -46,7 +46,7 @@ window.OpenLP = {
|
||||
},
|
||||
loadSlides: function (event) {
|
||||
$.getJSON(
|
||||
"/api/controller/live/text",
|
||||
"/stage/api/controller/live/text",
|
||||
function (data, status) {
|
||||
OpenLP.currentSlides = data.results.slides;
|
||||
OpenLP.currentSlide = 0;
|
||||
@ -137,7 +137,7 @@ window.OpenLP = {
|
||||
},
|
||||
pollServer: function () {
|
||||
$.getJSON(
|
||||
"/api/poll",
|
||||
"/stage/api/poll",
|
||||
function (data, status) {
|
||||
OpenLP.updateClock(data);
|
||||
if (OpenLP.currentItem != data.results.item ||
|
||||
|
@ -43,7 +43,7 @@ the remotes.
|
||||
``/files/{filename}``
|
||||
Serve a static file.
|
||||
|
||||
``/api/poll``
|
||||
``/stage/api/poll``
|
||||
Poll to see if there are any changes. Returns a JSON-encoded dict of
|
||||
any changes that occurred::
|
||||
|
||||
@ -119,122 +119,198 @@ import os
|
||||
import re
|
||||
import urllib
|
||||
import urlparse
|
||||
import cherrypy
|
||||
|
||||
from PyQt4 import QtCore, QtNetwork
|
||||
from mako.template import Template
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.lib import Registry, Settings, PluginStatus, StringContent
|
||||
|
||||
from openlp.core.utils import AppLocation, translate
|
||||
|
||||
from cherrypy._cpcompat import sha, ntob
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpResponse(object):
|
||||
def make_sha_hash(password):
|
||||
"""
|
||||
A simple object to encapsulate a pseudo-http response.
|
||||
Create an encrypted password for the given password.
|
||||
"""
|
||||
code = '200 OK'
|
||||
content = ''
|
||||
headers = {
|
||||
'Content-Type': 'text/html; charset="utf-8"\r\n'
|
||||
}
|
||||
return sha(ntob(password)).hexdigest()
|
||||
|
||||
def __init__(self, content='', headers=None, code=None):
|
||||
if headers is None:
|
||||
headers = {}
|
||||
self.content = content
|
||||
for key, value in headers.iteritems():
|
||||
self.headers[key] = value
|
||||
if code:
|
||||
self.code = code
|
||||
|
||||
def fetch_password(username):
|
||||
"""
|
||||
Fetch the password for a provided user.
|
||||
"""
|
||||
if username != Settings().value(u'remotes/user id'):
|
||||
return None
|
||||
return make_sha_hash(Settings().value(u'remotes/password'))
|
||||
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
Ability to control OpenLP via a web browser.
|
||||
This class controls the Cherrypy server and configuration.
|
||||
"""
|
||||
def __init__(self, plugin):
|
||||
_cp_config = {
|
||||
'tools.sessions.on': True,
|
||||
'tools.auth.on': True
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the http server, and start the server.
|
||||
"""
|
||||
log.debug(u'Initialise httpserver')
|
||||
self.plugin = plugin
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
self.connections = []
|
||||
self.start_tcp()
|
||||
self.settings_section = u'remotes'
|
||||
self.router = HttpRouter()
|
||||
|
||||
def start_tcp(self):
|
||||
def start_server(self):
|
||||
"""
|
||||
Start the http server, use the port in the settings default to 4316.
|
||||
Listen out for slide and song changes so they can be broadcast to
|
||||
clients. Listen out for socket connections.
|
||||
Start the http server based on configuration.
|
||||
"""
|
||||
log.debug(u'Start TCP server')
|
||||
port = Settings().value(self.plugin.settings_section + u'/port')
|
||||
address = Settings().value(self.plugin.settings_section + u'/ip address')
|
||||
self.server = QtNetwork.QTcpServer()
|
||||
self.server.listen(QtNetwork.QHostAddress(address), port)
|
||||
self.server.newConnection.connect(self.new_connection)
|
||||
log.debug(u'TCP listening on port %d' % port)
|
||||
log.debug(u'Start CherryPy server')
|
||||
# Define to security levels and inject the router code
|
||||
self.root = self.Public()
|
||||
self.root.files = self.Files()
|
||||
self.root.stage = self.Stage()
|
||||
self.root.router = self.router
|
||||
self.root.files.router = self.router
|
||||
self.root.stage.router = self.router
|
||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||
# Turn off the flood of access messages cause by poll
|
||||
cherrypy.log.access_log.propagate = False
|
||||
cherrypy.engine.start()
|
||||
|
||||
def new_connection(self):
|
||||
def define_config(self):
|
||||
"""
|
||||
A new http connection has been made. Create a client object to handle
|
||||
communication.
|
||||
Define the configuration of the server.
|
||||
"""
|
||||
log.debug(u'new http connection')
|
||||
socket = self.server.nextPendingConnection()
|
||||
if socket:
|
||||
self.connections.append(HttpConnection(self, socket))
|
||||
if Settings().value(self.settings_section + u'/https enabled'):
|
||||
port = Settings().value(self.settings_section + u'/https port')
|
||||
address = Settings().value(self.settings_section + u'/ip address')
|
||||
local_data = AppLocation.get_directory(AppLocation.DataDir)
|
||||
cherrypy.config.update({u'server.socket_host': str(address),
|
||||
u'server.socket_port': port,
|
||||
u'server.ssl_certificate': os.path.join(local_data, u'remotes', u'openlp.crt'),
|
||||
u'server.ssl_private_key': os.path.join(local_data, u'remotes', u'openlp.key')})
|
||||
else:
|
||||
port = Settings().value(self.settings_section + u'/port')
|
||||
address = Settings().value(self.settings_section + u'/ip address')
|
||||
cherrypy.config.update({u'server.socket_host': str(address)})
|
||||
cherrypy.config.update({u'server.socket_port': port})
|
||||
cherrypy.config.update({u'environment': u'embedded'})
|
||||
cherrypy.config.update({u'engine.autoreload_on': False})
|
||||
directory_config = {u'/': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': Settings().value(u'remotes/authentication enabled'),
|
||||
u'tools.basic_auth.realm': u'OpenLP Remote Login',
|
||||
u'tools.basic_auth.users': fetch_password,
|
||||
u'tools.basic_auth.encrypt': make_sha_hash},
|
||||
u'/files': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False},
|
||||
u'/stage': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.router.html_dir,
|
||||
u'tools.basic_auth.on': False}}
|
||||
return directory_config
|
||||
|
||||
def close_connection(self, connection):
|
||||
class Public(object):
|
||||
"""
|
||||
The connection has been closed. Clean up
|
||||
Main access class with may have security enabled on it.
|
||||
"""
|
||||
log.debug(u'close http connection')
|
||||
if connection in self.connections:
|
||||
self.connections.remove(connection)
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
self.router.request_data = None
|
||||
if isinstance(kwargs, dict):
|
||||
self.router.request_data = kwargs.get(u'data', None)
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Files(object):
|
||||
"""
|
||||
Provides access to files and has no security available. These are read only accesses
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
class Stage(object):
|
||||
"""
|
||||
Stageview is read only so security is not relevant and would reduce it's usability
|
||||
"""
|
||||
@cherrypy.expose
|
||||
def default(self, *args, **kwargs):
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
return self.router.process_http_request(url.path, *args)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close down the http server.
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
self.server.close()
|
||||
cherrypy.engine.exit()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
class HttpRouter(object):
|
||||
"""
|
||||
A single connection, this handles communication between the server
|
||||
and the client.
|
||||
This code is called by the HttpServer upon a request and it processes it based on the routing table.
|
||||
"""
|
||||
def __init__(self, parent, socket):
|
||||
def __init__(self):
|
||||
"""
|
||||
Initialise the http connection. Listen out for socket signals.
|
||||
Initialise the router
|
||||
"""
|
||||
log.debug(u'Initialise HttpConnection: %s' % socket.peerAddress())
|
||||
self.socket = socket
|
||||
self.parent = parent
|
||||
self.routes = [
|
||||
(u'^/$', self.serve_file),
|
||||
(u'^/(stage)$', self.serve_file),
|
||||
(r'^/files/(.*)$', self.serve_file),
|
||||
(r'^/api/poll$', self.poll),
|
||||
(r'^/stage/api/poll$', self.poll),
|
||||
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/stage/api/controller/(live|preview)/(.*)$', self.controller),
|
||||
(r'^/api/service/(.*)$', self.service),
|
||||
(r'^/stage/api/service/(.*)$', self.service),
|
||||
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
|
||||
(r'^/api/alert$', self.alert),
|
||||
(r'^/api/plugin/(search)$', self.pluginInfo),
|
||||
(r'^/api/plugin/(search)$', self.plugin_info),
|
||||
(r'^/api/(.*)/search$', self.search),
|
||||
(r'^/api/(.*)/live$', self.go_live),
|
||||
(r'^/api/(.*)/add$', self.add_to_service)
|
||||
]
|
||||
self.socket.readyRead.connect(self.ready_read)
|
||||
self.socket.disconnected.connect(self.disconnected)
|
||||
self.translate()
|
||||
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
|
||||
|
||||
def process_http_request(self, url_path, *args):
|
||||
"""
|
||||
Common function to process HTTP requests
|
||||
|
||||
``url_path``
|
||||
The requested URL.
|
||||
|
||||
``*args``
|
||||
Any passed data.
|
||||
"""
|
||||
response = None
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url_path)
|
||||
if match:
|
||||
log.debug('Route "%s" matched "%s"', route, url_path)
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
response = func(*args)
|
||||
break
|
||||
if response:
|
||||
return response
|
||||
else:
|
||||
return self._http_not_found()
|
||||
|
||||
def _get_service_items(self):
|
||||
"""
|
||||
Read the service item in use and return the data as a json object
|
||||
"""
|
||||
service_items = []
|
||||
if self.live_controller.service_item:
|
||||
current_unique_identifier = self.live_controller.service_item.unique_identifier
|
||||
@ -281,40 +357,6 @@ class HttpConnection(object):
|
||||
'slides': translate('RemotePlugin.Mobile', 'Slides')
|
||||
}
|
||||
|
||||
def ready_read(self):
|
||||
"""
|
||||
Data has been sent from the client. Respond to it
|
||||
"""
|
||||
log.debug(u'ready to read socket')
|
||||
if self.socket.canReadLine():
|
||||
data = str(self.socket.readLine())
|
||||
try:
|
||||
log.debug(u'received: ' + data)
|
||||
except UnicodeDecodeError:
|
||||
# Malicious request containing non-ASCII characters.
|
||||
self.close()
|
||||
return
|
||||
words = data.split(' ')
|
||||
response = None
|
||||
if words[0] == u'GET':
|
||||
url = urlparse.urlparse(words[1])
|
||||
self.url_params = urlparse.parse_qs(url.query)
|
||||
# Loop through the routes we set up earlier and execute them
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url.path)
|
||||
if match:
|
||||
log.debug('Route "%s" matched "%s"', route, url.path)
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
response = func(*args)
|
||||
break
|
||||
if response:
|
||||
self.send_response(response)
|
||||
else:
|
||||
self.send_response(HttpResponse(code='404 Not Found'))
|
||||
self.close()
|
||||
|
||||
def serve_file(self, filename=None):
|
||||
"""
|
||||
Send a file to the socket. For now, just a subset of file types
|
||||
@ -329,9 +371,9 @@ class HttpConnection(object):
|
||||
filename = u'index.html'
|
||||
elif filename == u'stage':
|
||||
filename = u'stage.html'
|
||||
path = os.path.normpath(os.path.join(self.parent.html_dir, filename))
|
||||
if not path.startswith(self.parent.html_dir):
|
||||
return HttpResponse(code=u'404 Not Found')
|
||||
path = os.path.normpath(os.path.join(self.html_dir, filename))
|
||||
if not path.startswith(self.html_dir):
|
||||
return self._http_not_found()
|
||||
ext = os.path.splitext(filename)[1]
|
||||
html = None
|
||||
if ext == u'.html':
|
||||
@ -360,11 +402,12 @@ class HttpConnection(object):
|
||||
content = file_handle.read()
|
||||
except IOError:
|
||||
log.exception(u'Failed to open %s' % path)
|
||||
return HttpResponse(code=u'404 Not Found')
|
||||
return self._http_not_found()
|
||||
finally:
|
||||
if file_handle:
|
||||
file_handle.close()
|
||||
return HttpResponse(content, {u'Content-Type': mimetype})
|
||||
cherrypy.response.headers['Content-Type'] = mimetype
|
||||
return content
|
||||
|
||||
def poll(self):
|
||||
"""
|
||||
@ -379,18 +422,20 @@ class HttpConnection(object):
|
||||
u'theme': self.live_controller.theme_screen.isChecked(),
|
||||
u'display': self.live_controller.desktop_screen.isChecked()
|
||||
}
|
||||
return HttpResponse(json.dumps({u'results': result}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': result})
|
||||
|
||||
def display(self, action):
|
||||
"""
|
||||
Hide or show the display screen.
|
||||
This is a cross Thread call and UI is updated so Events need to be used.
|
||||
|
||||
``action``
|
||||
This is the action, either ``hide`` or ``show``.
|
||||
"""
|
||||
Registry().execute(u'slidecontroller_toggle_display', action)
|
||||
return HttpResponse(json.dumps({u'results': {u'success': True}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
self.live_controller.emit(QtCore.SIGNAL(u'slidecontroller_toggle_display'), action)
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': True}})
|
||||
|
||||
def alert(self):
|
||||
"""
|
||||
@ -399,16 +444,16 @@ class HttpConnection(object):
|
||||
plugin = self.plugin_manager.get_plugin_by_name("alerts")
|
||||
if plugin.status == PluginStatus.Active:
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||
text = json.loads(self.request_data)[u'request'][u'text']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
text = urllib.unquote(text)
|
||||
Registry().execute(u'alerts_text', [text])
|
||||
self.alerts_manager.emit(QtCore.SIGNAL(u'alerts_text'), [text])
|
||||
success = True
|
||||
else:
|
||||
success = False
|
||||
return HttpResponse(json.dumps({u'results': {u'success': success}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': success}})
|
||||
|
||||
def controller(self, display_type, action):
|
||||
"""
|
||||
@ -444,44 +489,44 @@ class HttpConnection(object):
|
||||
if current_item:
|
||||
json_data[u'results'][u'item'] = self.live_controller.service_item.unique_identifier
|
||||
else:
|
||||
if self.url_params and self.url_params.get(u'data'):
|
||||
if self.request_data:
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
data = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
log.info(data)
|
||||
# This slot expects an int within a list.
|
||||
id = data[u'request'][u'id']
|
||||
Registry().execute(event, [id])
|
||||
self.live_controller.emit(QtCore.SIGNAL(event), [data])
|
||||
else:
|
||||
Registry().execute(event)
|
||||
self.live_controller.emit(QtCore.SIGNAL(event))
|
||||
json_data = {u'results': {u'success': True}}
|
||||
return HttpResponse(json.dumps(json_data), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps(json_data)
|
||||
|
||||
def service(self, action):
|
||||
"""
|
||||
Handles requests for service items
|
||||
Handles requests for service items in the service manager
|
||||
|
||||
``action``
|
||||
The action to perform.
|
||||
"""
|
||||
event = u'servicemanager_%s' % action
|
||||
if action == u'list':
|
||||
return HttpResponse(json.dumps({u'results': {u'items': self._get_service_items()}}),
|
||||
{u'Content-Type': u'application/json'})
|
||||
else:
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': self._get_service_items()}})
|
||||
event += u'_item'
|
||||
if self.url_params and self.url_params.get(u'data'):
|
||||
if self.request_data:
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
Registry().execute(event, data[u'request'][u'id'])
|
||||
data = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError:
|
||||
return self._http_bad_request()
|
||||
self.service_manager.emit(QtCore.SIGNAL(event), data)
|
||||
else:
|
||||
Registry().execute(event)
|
||||
return HttpResponse(json.dumps({u'results': {u'success': True}}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'success': True}})
|
||||
|
||||
def pluginInfo(self, action):
|
||||
def plugin_info(self, action):
|
||||
"""
|
||||
Return plugin related information, based on the action.
|
||||
|
||||
@ -493,8 +538,9 @@ class HttpConnection(object):
|
||||
searches = []
|
||||
for plugin in self.plugin_manager.plugins:
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
searches.append([plugin.name, unicode(plugin.textStrings[StringContent.Name][u'plural'])])
|
||||
return HttpResponse(json.dumps({u'results': {u'items': searches}}), {u'Content-Type': u'application/json'})
|
||||
searches.append([plugin.name, unicode(plugin.text_strings[StringContent.Name][u'plural'])])
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': searches}})
|
||||
|
||||
def search(self, plugin_name):
|
||||
"""
|
||||
@ -504,69 +550,63 @@ class HttpConnection(object):
|
||||
The plugin name to search in.
|
||||
"""
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[u'request'][u'text']
|
||||
text = json.loads(self.request_data)[u'request'][u'text']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
text = urllib.unquote(text)
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
|
||||
results = plugin.media_item.search(text, False)
|
||||
else:
|
||||
results = []
|
||||
return HttpResponse(json.dumps({u'results': {u'items': results}}), {u'Content-Type': u'application/json'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': results}})
|
||||
|
||||
def go_live(self, plugin_name):
|
||||
"""
|
||||
Go live on an item of type ``plugin``.
|
||||
"""
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||
id = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
plugin.media_item.go_live(id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
plugin.media_item.emit(QtCore.SIGNAL(u'%s_go_live' % plugin_name), [id, True])
|
||||
return self._http_success()
|
||||
|
||||
def add_to_service(self, plugin_name):
|
||||
"""
|
||||
Add item of type ``plugin_name`` to the end of the service.
|
||||
"""
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[u'request'][u'id']
|
||||
id = json.loads(self.request_data)[u'request'][u'id']
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
|
||||
if plugin.status == PluginStatus.Active and plugin.media_item:
|
||||
item_id = plugin.media_item.createItemFromId(id)
|
||||
plugin.media_item.add_to_service(item_id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
item_id = plugin.media_item.create_item_from_id(id)
|
||||
plugin.media_item.emit(QtCore.SIGNAL(u'%s_add_to_service' % plugin_name), [item_id, True])
|
||||
self._http_success()
|
||||
|
||||
def send_response(self, response):
|
||||
http = u'HTTP/1.1 %s\r\n' % response.code
|
||||
for header, value in response.headers.iteritems():
|
||||
http += '%s: %s\r\n' % (header, value)
|
||||
http += '\r\n'
|
||||
self.socket.write(http)
|
||||
self.socket.write(response.content)
|
||||
def _http_success(self):
|
||||
"""
|
||||
Set the HTTP success return code.
|
||||
"""
|
||||
cherrypy.response.status = 200
|
||||
|
||||
def disconnected(self):
|
||||
def _http_bad_request(self):
|
||||
"""
|
||||
The client has disconnected. Tidy up
|
||||
Set the HTTP bad response return code.
|
||||
"""
|
||||
log.debug(u'socket disconnected')
|
||||
self.close()
|
||||
cherrypy.response.status = 400
|
||||
|
||||
def close(self):
|
||||
def _http_not_found(self):
|
||||
"""
|
||||
The server has closed the connection. Tidy up
|
||||
Set the HTTP not found return code.
|
||||
"""
|
||||
if not self.socket:
|
||||
return
|
||||
log.debug(u'close socket')
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
self.parent.close_connection(self)
|
||||
cherrypy.response.status = 404
|
||||
cherrypy.response.body = ["<html><body>Sorry, an error occurred </body></html>"]
|
||||
|
||||
def _get_service_manager(self):
|
||||
"""
|
||||
@ -597,3 +637,13 @@ class HttpConnection(object):
|
||||
return self._plugin_manager
|
||||
|
||||
plugin_manager = property(_get_plugin_manager)
|
||||
|
||||
def _get_alerts_manager(self):
|
||||
"""
|
||||
Adds the alerts manager to the class dynamically
|
||||
"""
|
||||
if not hasattr(self, u'_alerts_manager'):
|
||||
self._alerts_manager = Registry().get(u'alerts_manager')
|
||||
return self._alerts_manager
|
||||
|
||||
alerts_manager = property(_get_alerts_manager)
|
||||
|
@ -27,9 +27,12 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
import os.path
|
||||
|
||||
from PyQt4 import QtCore, QtGui, QtNetwork
|
||||
|
||||
from openlp.core.lib import Registry, Settings, SettingsTab, translate
|
||||
from openlp.core.lib import Settings, SettingsTab, translate
|
||||
from openlp.core.utils import AppLocation
|
||||
|
||||
|
||||
ZERO_URL = u'0.0.0.0'
|
||||
@ -53,32 +56,84 @@ class RemoteTab(SettingsTab):
|
||||
self.address_label.setObjectName(u'address_label')
|
||||
self.address_edit = QtGui.QLineEdit(self.server_settings_group_box)
|
||||
self.address_edit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
|
||||
self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(
|
||||
u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self))
|
||||
self.address_edit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'),
|
||||
self))
|
||||
self.address_edit.setObjectName(u'address_edit')
|
||||
self.server_settings_layout.addRow(self.address_label, self.address_edit)
|
||||
self.twelve_hour_check_box = QtGui.QCheckBox(self.server_settings_group_box)
|
||||
self.twelve_hour_check_box.setObjectName(u'twelve_hour_check_box')
|
||||
self.server_settings_layout.addRow(self.twelve_hour_check_box)
|
||||
self.port_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.left_layout.addWidget(self.server_settings_group_box)
|
||||
self.http_settings_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.http_settings_group_box.setObjectName(u'http_settings_group_box')
|
||||
self.http_setting_layout = QtGui.QFormLayout(self.http_settings_group_box)
|
||||
self.http_setting_layout.setObjectName(u'http_setting_layout')
|
||||
self.port_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.port_label.setObjectName(u'port_label')
|
||||
self.port_spin_box = QtGui.QSpinBox(self.server_settings_group_box)
|
||||
self.port_spin_box = QtGui.QSpinBox(self.http_settings_group_box)
|
||||
self.port_spin_box.setMaximum(32767)
|
||||
self.port_spin_box.setObjectName(u'port_spin_box')
|
||||
self.server_settings_layout.addRow(self.port_label, self.port_spin_box)
|
||||
self.remote_url_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.port_label, self.port_spin_box)
|
||||
self.remote_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.remote_url_label.setObjectName(u'remote_url_label')
|
||||
self.remote_url = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.remote_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.remote_url.setObjectName(u'remote_url')
|
||||
self.remote_url.setOpenExternalLinks(True)
|
||||
self.server_settings_layout.addRow(self.remote_url_label, self.remote_url)
|
||||
self.stage_url_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.remote_url_label, self.remote_url)
|
||||
self.stage_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_url_label.setObjectName(u'stage_url_label')
|
||||
self.stage_url = QtGui.QLabel(self.server_settings_group_box)
|
||||
self.stage_url = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_url.setObjectName(u'stage_url')
|
||||
self.stage_url.setOpenExternalLinks(True)
|
||||
self.server_settings_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.left_layout.addWidget(self.server_settings_group_box)
|
||||
self.http_setting_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.left_layout.addWidget(self.http_settings_group_box)
|
||||
self.https_settings_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.https_settings_group_box.setCheckable(True)
|
||||
self.https_settings_group_box.setChecked(False)
|
||||
self.https_settings_group_box.setObjectName(u'https_settings_group_box')
|
||||
self.https_settings_layout = QtGui.QFormLayout(self.https_settings_group_box)
|
||||
self.https_settings_layout.setObjectName(u'https_settings_layout')
|
||||
self.https_error_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.https_error_label.setVisible(False)
|
||||
self.https_error_label.setWordWrap(True)
|
||||
self.https_error_label.setObjectName(u'https_error_label')
|
||||
self.https_settings_layout.addRow(self.https_error_label)
|
||||
self.https_port_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.https_port_label.setObjectName(u'https_port_label')
|
||||
self.https_port_spin_box = QtGui.QSpinBox(self.https_settings_group_box)
|
||||
self.https_port_spin_box.setMaximum(32767)
|
||||
self.https_port_spin_box.setObjectName(u'https_port_spin_box')
|
||||
self.https_settings_layout.addRow(self.https_port_label, self.https_port_spin_box)
|
||||
self.remote_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.remote_https_url.setObjectName(u'remote_http_url')
|
||||
self.remote_https_url.setOpenExternalLinks(True)
|
||||
self.remote_https_url_label = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.remote_https_url_label.setObjectName(u'remote_http_url_label')
|
||||
self.https_settings_layout.addRow(self.remote_https_url_label, self.remote_https_url)
|
||||
self.stage_https_url_label = QtGui.QLabel(self.http_settings_group_box)
|
||||
self.stage_https_url_label.setObjectName(u'stage_https_url_label')
|
||||
self.stage_https_url = QtGui.QLabel(self.https_settings_group_box)
|
||||
self.stage_https_url.setObjectName(u'stage_https_url')
|
||||
self.stage_https_url.setOpenExternalLinks(True)
|
||||
self.https_settings_layout.addRow(self.stage_https_url_label, self.stage_https_url)
|
||||
self.left_layout.addWidget(self.https_settings_group_box)
|
||||
self.user_login_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.user_login_group_box.setCheckable(True)
|
||||
self.user_login_group_box.setChecked(False)
|
||||
self.user_login_group_box.setObjectName(u'user_login_group_box')
|
||||
self.user_login_layout = QtGui.QFormLayout(self.user_login_group_box)
|
||||
self.user_login_layout.setObjectName(u'user_login_layout')
|
||||
self.user_id_label = QtGui.QLabel(self.user_login_group_box)
|
||||
self.user_id_label.setObjectName(u'user_id_label')
|
||||
self.user_id = QtGui.QLineEdit(self.user_login_group_box)
|
||||
self.user_id.setObjectName(u'user_id')
|
||||
self.user_login_layout.addRow(self.user_id_label, self.user_id)
|
||||
self.password_label = QtGui.QLabel(self.user_login_group_box)
|
||||
self.password_label.setObjectName(u'password_label')
|
||||
self.password = QtGui.QLineEdit(self.user_login_group_box)
|
||||
self.password.setObjectName(u'password')
|
||||
self.user_login_layout.addRow(self.password_label, self.password)
|
||||
self.left_layout.addWidget(self.user_login_group_box)
|
||||
self.android_app_group_box = QtGui.QGroupBox(self.right_column)
|
||||
self.android_app_group_box.setObjectName(u'android_app_group_box')
|
||||
self.right_layout.addWidget(self.android_app_group_box)
|
||||
@ -96,9 +151,11 @@ class RemoteTab(SettingsTab):
|
||||
self.qr_layout.addWidget(self.qr_description_label)
|
||||
self.left_layout.addStretch()
|
||||
self.right_layout.addStretch()
|
||||
self.twelve_hour_check_box.stateChanged.connect(self.onTwelveHourCheckBoxChanged)
|
||||
self.twelve_hour_check_box.stateChanged.connect(self.on_twelve_hour_check_box_changed)
|
||||
self.address_edit.textChanged.connect(self.set_urls)
|
||||
self.port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_port_spin_box.valueChanged.connect(self.set_urls)
|
||||
self.https_settings_group_box.clicked.connect(self.https_changed)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
|
||||
@ -112,8 +169,21 @@ class RemoteTab(SettingsTab):
|
||||
'Scan the QR code or click <a href="https://play.google.com/store/'
|
||||
'apps/details?id=org.openlp.android">download</a> to install the '
|
||||
'Android app from Google Play.'))
|
||||
self.https_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'HTTPS Server'))
|
||||
self.https_error_label.setText(translate('RemotePlugin.RemoteTab',
|
||||
'Could not find an SSL certificate. The HTTPS server will not be available unless an SSL certificate '
|
||||
'is found. Please see the manual for more information.'))
|
||||
self.https_port_label.setText(self.port_label.text())
|
||||
self.remote_https_url_label.setText(self.remote_url_label.text())
|
||||
self.stage_https_url_label.setText(self.stage_url_label.text())
|
||||
self.user_login_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'User Authentication'))
|
||||
self.user_id_label.setText(translate('RemotePlugin.RemoteTab', 'User id:'))
|
||||
self.password_label.setText(translate('RemotePlugin.RemoteTab', 'Password:'))
|
||||
|
||||
def set_urls(self):
|
||||
"""
|
||||
Update the display based on the data input on the screen
|
||||
"""
|
||||
ip_address = u'localhost'
|
||||
if self.address_edit.text() == ZERO_URL:
|
||||
interfaces = QtNetwork.QNetworkInterface.allInterfaces()
|
||||
@ -129,31 +199,73 @@ class RemoteTab(SettingsTab):
|
||||
break
|
||||
else:
|
||||
ip_address = self.address_edit.text()
|
||||
url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value())
|
||||
self.remote_url.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
url += u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
http_url = u'http://%s:%s/' % (ip_address, self.port_spin_box.value())
|
||||
https_url = u'https://%s:%s/' % (ip_address, self.https_port_spin_box.value())
|
||||
self.remote_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
|
||||
self.remote_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
|
||||
http_url += u'stage'
|
||||
https_url += u'stage'
|
||||
self.stage_url.setText(u'<a href="%s">%s</a>' % (http_url, http_url))
|
||||
self.stage_https_url.setText(u'<a href="%s">%s</a>' % (https_url, https_url))
|
||||
|
||||
def load(self):
|
||||
"""
|
||||
Load the configuration and update the server configuration if necessary
|
||||
"""
|
||||
self.port_spin_box.setValue(Settings().value(self.settings_section + u'/port'))
|
||||
self.https_port_spin_box.setValue(Settings().value(self.settings_section + u'/https port'))
|
||||
self.address_edit.setText(Settings().value(self.settings_section + u'/ip address'))
|
||||
self.twelve_hour = Settings().value(self.settings_section + u'/twelve hour')
|
||||
self.twelve_hour_check_box.setChecked(self.twelve_hour)
|
||||
local_data = AppLocation.get_directory(AppLocation.DataDir)
|
||||
if not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.crt')) or \
|
||||
not os.path.exists(os.path.join(local_data, u'remotes', u'openlp.key')):
|
||||
self.https_settings_group_box.setChecked(False)
|
||||
self.https_settings_group_box.setEnabled(False)
|
||||
self.https_error_label.setVisible(True)
|
||||
else:
|
||||
self.https_settings_group_box.setChecked(Settings().value(self.settings_section + u'/https enabled'))
|
||||
self.https_settings_group_box.setEnabled(True)
|
||||
self.https_error_label.setVisible(False)
|
||||
self.user_login_group_box.setChecked(Settings().value(self.settings_section + u'/authentication enabled'))
|
||||
self.user_id.setText(Settings().value(self.settings_section + u'/user id'))
|
||||
self.password.setText(Settings().value(self.settings_section + u'/password'))
|
||||
self.set_urls()
|
||||
self.https_changed()
|
||||
|
||||
def save(self):
|
||||
changed = False
|
||||
"""
|
||||
Save the configuration and update the server configuration if necessary
|
||||
"""
|
||||
if Settings().value(self.settings_section + u'/ip address') != self.address_edit.text() or \
|
||||
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value():
|
||||
changed = True
|
||||
Settings().value(self.settings_section + u'/port') != self.port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https port') != self.https_port_spin_box.value() or \
|
||||
Settings().value(self.settings_section + u'/https enabled') != \
|
||||
self.https_settings_group_box.isChecked() or \
|
||||
Settings().value(self.settings_section + u'/authentication enabled') != \
|
||||
self.user_login_group_box.isChecked():
|
||||
self.settings_form.register_post_process(u'remotes_config_updated')
|
||||
Settings().setValue(self.settings_section + u'/port', self.port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https port', self.https_port_spin_box.value())
|
||||
Settings().setValue(self.settings_section + u'/https enabled', self.https_settings_group_box.isChecked())
|
||||
Settings().setValue(self.settings_section + u'/ip address', self.address_edit.text())
|
||||
Settings().setValue(self.settings_section + u'/twelve hour', self.twelve_hour)
|
||||
if changed:
|
||||
Registry().execute(u'remotes_config_updated')
|
||||
Settings().setValue(self.settings_section + u'/authentication enabled', self.user_login_group_box.isChecked())
|
||||
Settings().setValue(self.settings_section + u'/user id', self.user_id.text())
|
||||
Settings().setValue(self.settings_section + u'/password', self.password.text())
|
||||
|
||||
def onTwelveHourCheckBoxChanged(self, check_state):
|
||||
def on_twelve_hour_check_box_changed(self, check_state):
|
||||
"""
|
||||
Toggle the 12 hour check box.
|
||||
"""
|
||||
self.twelve_hour = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.twelve_hour = True
|
||||
|
||||
def https_changed(self):
|
||||
"""
|
||||
Invert the HTTP group box based on Https group settings
|
||||
"""
|
||||
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())
|
||||
|
||||
|
@ -29,6 +29,8 @@
|
||||
|
||||
import logging
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
from openlp.core.lib import Plugin, StringContent, translate, build_icon
|
||||
from openlp.plugins.remotes.lib import RemoteTab, HttpServer
|
||||
|
||||
@ -37,6 +39,11 @@ log = logging.getLogger(__name__)
|
||||
__default_settings__ = {
|
||||
u'remotes/twelve hour': True,
|
||||
u'remotes/port': 4316,
|
||||
u'remotes/https port': 4317,
|
||||
u'remotes/https enabled': False,
|
||||
u'remotes/user id': u'openlp',
|
||||
u'remotes/password': u'password',
|
||||
u'remotes/authentication enabled': False,
|
||||
u'remotes/ip address': u'0.0.0.0'
|
||||
}
|
||||
|
||||
@ -60,7 +67,8 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
log.debug(u'initialise')
|
||||
Plugin.initialise(self)
|
||||
self.server = HttpServer(self)
|
||||
self.server = HttpServer()
|
||||
self.server.start_server()
|
||||
|
||||
def finalise(self):
|
||||
"""
|
||||
@ -70,6 +78,7 @@ class RemotesPlugin(Plugin):
|
||||
Plugin.finalise(self)
|
||||
if self.server:
|
||||
self.server.close()
|
||||
self.server = None
|
||||
|
||||
def about(self):
|
||||
"""
|
||||
@ -99,5 +108,6 @@ class RemotesPlugin(Plugin):
|
||||
"""
|
||||
Called when Config is changed to restart the server on new address or port
|
||||
"""
|
||||
self.finalise()
|
||||
self.initialise()
|
||||
log.debug(u'remote config changed')
|
||||
self.main_window.information_message(translate('RemotePlugin', 'Configuration Change'),
|
||||
translate('RemotePlugin', 'OpenLP will need to be restarted for the Remote changes to become active.'))
|
||||
|
@ -81,6 +81,7 @@ MODULES = [
|
||||
'enchant',
|
||||
'BeautifulSoup',
|
||||
'mako',
|
||||
'cherrypy',
|
||||
'migrate',
|
||||
'uno',
|
||||
'icu',
|
||||
|
@ -74,7 +74,7 @@ class TestPluginManager(TestCase):
|
||||
# WHEN: We run hook_settings_tabs()
|
||||
plugin_manager.hook_settings_tabs()
|
||||
|
||||
# THEN: The create_settings_Tab() method should have been called
|
||||
# THEN: The hook_settings_tabs() method should have been called
|
||||
assert mocked_plugin.create_media_manager_item.call_count == 0, \
|
||||
u'The create_media_manager_item() method should not have been called.'
|
||||
|
||||
@ -94,8 +94,8 @@ class TestPluginManager(TestCase):
|
||||
# WHEN: We run hook_settings_tabs()
|
||||
plugin_manager.hook_settings_tabs()
|
||||
|
||||
# THEN: The create_settings_Tab() method should not have been called, but the plugins lists should be the same
|
||||
assert mocked_plugin.create_settings_Tab.call_count == 0, \
|
||||
# THEN: The create_settings_tab() method should not have been called, but the plugins lists should be the same
|
||||
assert mocked_plugin.create_settings_tab.call_count == 0, \
|
||||
u'The create_media_manager_item() method should not have been called.'
|
||||
self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
|
||||
u'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
@ -117,7 +117,7 @@ class TestPluginManager(TestCase):
|
||||
plugin_manager.hook_settings_tabs()
|
||||
|
||||
# THEN: The create_media_manager_item() method should have been called with the mocked settings form
|
||||
assert mocked_plugin.create_settings_Tab.call_count == 1, \
|
||||
assert mocked_plugin.create_settings_tab.call_count == 1, \
|
||||
u'The create_media_manager_item() method should have been called once.'
|
||||
self.assertEqual(mocked_settings_form.plugin_manager.plugins, plugin_manager.plugins,
|
||||
u'The plugins on the settings form should be the same as the plugins in the plugin manager')
|
||||
@ -135,8 +135,8 @@ class TestPluginManager(TestCase):
|
||||
# WHEN: We run hook_settings_tabs()
|
||||
plugin_manager.hook_settings_tabs()
|
||||
|
||||
# THEN: The create_settings_Tab() method should have been called
|
||||
mocked_plugin.create_settings_Tab.assert_called_with(self.mocked_settings_form)
|
||||
# THEN: The create_settings_tab() method should have been called
|
||||
mocked_plugin.create_settings_tab.assert_called_with(self.mocked_settings_form)
|
||||
|
||||
def hook_import_menu_with_disabled_plugin_test(self):
|
||||
"""
|
||||
|
@ -11,7 +11,9 @@ from PyQt4 import QtGui
|
||||
|
||||
|
||||
class TestSettings(TestCase):
|
||||
|
||||
"""
|
||||
Test the functions in the Settings module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
|
@ -6,6 +6,7 @@ from unittest import TestCase
|
||||
|
||||
from openlp.core.lib import UiStrings
|
||||
|
||||
|
||||
class TestUiStrings(TestCase):
|
||||
|
||||
def check_same_instance_test(self):
|
||||
|
@ -30,8 +30,10 @@ class TestAppLocation(TestCase):
|
||||
mocked_get_directory.return_value = u'test/dir'
|
||||
mocked_check_directory_exists.return_value = True
|
||||
mocked_os.path.normpath.return_value = u'test/dir'
|
||||
|
||||
# WHEN: we call AppLocation.get_data_path()
|
||||
data_path = AppLocation.get_data_path()
|
||||
|
||||
# THEN: check that all the correct methods were called, and the result is correct
|
||||
mocked_settings.contains.assert_called_with(u'advanced/data path')
|
||||
mocked_get_directory.assert_called_with(AppLocation.DataDir)
|
||||
@ -49,8 +51,10 @@ class TestAppLocation(TestCase):
|
||||
mocked_settings.contains.return_value = True
|
||||
mocked_settings.value.return_value.toString.return_value = u'custom/dir'
|
||||
mocked_os.path.normpath.return_value = u'custom/dir'
|
||||
|
||||
# WHEN: we call AppLocation.get_data_path()
|
||||
data_path = AppLocation.get_data_path()
|
||||
|
||||
# THEN: the mocked Settings methods were called and the value returned was our set up value
|
||||
mocked_settings.contains.assert_called_with(u'advanced/data path')
|
||||
mocked_settings.value.assert_called_with(u'advanced/data path')
|
||||
@ -100,8 +104,10 @@ class TestAppLocation(TestCase):
|
||||
# GIVEN: A mocked out AppLocation.get_data_path()
|
||||
mocked_get_data_path.return_value = u'test/dir'
|
||||
mocked_check_directory_exists.return_value = True
|
||||
|
||||
# WHEN: we call AppLocation.get_data_path()
|
||||
data_path = AppLocation.get_section_data_path(u'section')
|
||||
|
||||
# THEN: check that all the correct methods were called, and the result is correct
|
||||
mocked_check_directory_exists.assert_called_with(u'test/dir/section')
|
||||
assert data_path == u'test/dir/section', u'Result should be "test/dir/section"'
|
||||
@ -112,8 +118,10 @@ class TestAppLocation(TestCase):
|
||||
"""
|
||||
with patch(u'openlp.core.utils.applocation._get_frozen_path') as mocked_get_frozen_path:
|
||||
mocked_get_frozen_path.return_value = u'app/dir'
|
||||
|
||||
# WHEN: We call AppLocation.get_directory
|
||||
directory = AppLocation.get_directory(AppLocation.AppDir)
|
||||
|
||||
# THEN:
|
||||
assert directory == u'app/dir', u'Directory should be "app/dir"'
|
||||
|
||||
@ -130,8 +138,10 @@ class TestAppLocation(TestCase):
|
||||
mocked_get_frozen_path.return_value = u'plugins/dir'
|
||||
mocked_sys.frozen = 1
|
||||
mocked_sys.argv = ['openlp']
|
||||
|
||||
# WHEN: We call AppLocation.get_directory
|
||||
directory = AppLocation.get_directory(AppLocation.PluginsDir)
|
||||
|
||||
# THEN:
|
||||
assert directory == u'plugins/dir', u'Directory should be "plugins/dir"'
|
||||
|
||||
|
0
tests/functional/openlp_plugins/remotes/__init__.py
Normal file
0
tests/functional/openlp_plugins/remotes/__init__.py
Normal file
108
tests/functional/openlp_plugins/remotes/test_remotetab.py
Normal file
108
tests/functional/openlp_plugins/remotes/test_remotetab.py
Normal file
@ -0,0 +1,108 @@
|
||||
"""
|
||||
This module contains tests for the lib submodule of the Remotes plugin.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from tempfile import mkstemp
|
||||
from mock import patch
|
||||
|
||||
from openlp.core.lib import Settings
|
||||
from openlp.plugins.remotes.lib.remotetab import RemoteTab
|
||||
|
||||
from PyQt4 import QtGui
|
||||
|
||||
__default_settings__ = {
|
||||
u'remotes/twelve hour': True,
|
||||
u'remotes/port': 4316,
|
||||
u'remotes/https port': 4317,
|
||||
u'remotes/https enabled': False,
|
||||
u'remotes/user id': u'openlp',
|
||||
u'remotes/password': u'password',
|
||||
u'remotes/authentication enabled': False,
|
||||
u'remotes/ip address': u'0.0.0.0'
|
||||
}
|
||||
|
||||
ZERO_URL = u'0.0.0.0'
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'..', u'resources'))
|
||||
|
||||
|
||||
class TestRemoteTab(TestCase):
|
||||
"""
|
||||
Test the functions in the :mod:`lib` module.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
fd, self.ini_file = mkstemp(u'.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.application = QtGui.QApplication.instance()
|
||||
Settings().extend_default_settings(__default_settings__)
|
||||
self.parent = QtGui.QMainWindow()
|
||||
self.form = RemoteTab(self.parent, u'Remotes', None, None)
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.application
|
||||
del self.parent
|
||||
del self.form
|
||||
os.unlink(self.ini_file)
|
||||
|
||||
def set_basic_urls_test(self):
|
||||
"""
|
||||
Test the set_urls function with standard defaults
|
||||
"""
|
||||
# GIVEN: A mocked location
|
||||
with patch(u'openlp.core.utils.applocation.Settings') as mocked_class, \
|
||||
patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \
|
||||
patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \
|
||||
patch(u'openlp.core.utils.applocation.os') as mocked_os:
|
||||
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
|
||||
mocked_settings = mocked_class.return_value
|
||||
mocked_settings.contains.return_value = False
|
||||
mocked_get_directory.return_value = u'test/dir'
|
||||
mocked_check_directory_exists.return_value = True
|
||||
mocked_os.path.normpath.return_value = u'test/dir'
|
||||
|
||||
# WHEN: when the set_urls is called having reloaded the form.
|
||||
self.form.load()
|
||||
self.form.set_urls()
|
||||
# THEN: the following screen values should be set
|
||||
self.assertEqual(self.form.address_edit.text(), ZERO_URL, u'The default URL should be set on the screen')
|
||||
self.assertEqual(self.form.https_settings_group_box.isEnabled(), False,
|
||||
u'The Https box should not be enabled')
|
||||
self.assertEqual(self.form.https_settings_group_box.isChecked(), False,
|
||||
u'The Https checked box should note be Checked')
|
||||
self.assertEqual(self.form.user_login_group_box.isChecked(), False,
|
||||
u'The authentication box should not be enabled')
|
||||
|
||||
def set_certificate_urls_test(self):
|
||||
"""
|
||||
Test the set_urls function with certificate available
|
||||
"""
|
||||
# GIVEN: A mocked location
|
||||
with patch(u'openlp.core.utils.applocation.Settings') as mocked_class, \
|
||||
patch(u'openlp.core.utils.AppLocation.get_directory') as mocked_get_directory, \
|
||||
patch(u'openlp.core.utils.applocation.check_directory_exists') as mocked_check_directory_exists, \
|
||||
patch(u'openlp.core.utils.applocation.os') as mocked_os:
|
||||
# GIVEN: A mocked out Settings class and a mocked out AppLocation.get_directory()
|
||||
mocked_settings = mocked_class.return_value
|
||||
mocked_settings.contains.return_value = False
|
||||
mocked_get_directory.return_value = TEST_PATH
|
||||
mocked_check_directory_exists.return_value = True
|
||||
mocked_os.path.normpath.return_value = TEST_PATH
|
||||
|
||||
# WHEN: when the set_urls is called having reloaded the form.
|
||||
self.form.load()
|
||||
self.form.set_urls()
|
||||
# THEN: the following screen values should be set
|
||||
self.assertEqual(self.form.http_settings_group_box.isEnabled(), True,
|
||||
u'The Http group box should be enabled')
|
||||
self.assertEqual(self.form.https_settings_group_box.isChecked(), False,
|
||||
u'The Https checked box should be Checked')
|
||||
self.assertEqual(self.form.https_settings_group_box.isEnabled(), True,
|
||||
u'The Https box should be enabled')
|
99
tests/functional/openlp_plugins/remotes/test_router.py
Normal file
99
tests/functional/openlp_plugins/remotes/test_router.py
Normal file
@ -0,0 +1,99 @@
|
||||
"""
|
||||
This module contains tests for the lib submodule of the Remotes plugin.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from tempfile import mkstemp
|
||||
from mock import MagicMock
|
||||
|
||||
from openlp.core.lib import Settings
|
||||
from openlp.plugins.remotes.lib.httpserver import HttpRouter, fetch_password, make_sha_hash
|
||||
from PyQt4 import QtGui
|
||||
|
||||
__default_settings__ = {
|
||||
u'remotes/twelve hour': True,
|
||||
u'remotes/port': 4316,
|
||||
u'remotes/https port': 4317,
|
||||
u'remotes/https enabled': False,
|
||||
u'remotes/user id': u'openlp',
|
||||
u'remotes/password': u'password',
|
||||
u'remotes/authentication enabled': False,
|
||||
u'remotes/ip address': u'0.0.0.0'
|
||||
}
|
||||
|
||||
|
||||
class TestRouter(TestCase):
|
||||
"""
|
||||
Test the functions in the :mod:`lib` module.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
fd, self.ini_file = mkstemp(u'.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.application = QtGui.QApplication.instance()
|
||||
Settings().extend_default_settings(__default_settings__)
|
||||
self.router = HttpRouter()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.application
|
||||
os.unlink(self.ini_file)
|
||||
|
||||
def fetch_password_unknown_test(self):
|
||||
"""
|
||||
Test the fetch password code with an unknown userid
|
||||
"""
|
||||
# GIVEN: A default configuration
|
||||
# WHEN: called with the defined userid
|
||||
password = fetch_password(u'itwinkle')
|
||||
|
||||
# THEN: the function should return None
|
||||
self.assertEqual(password, None, u'The result for fetch_password should be None')
|
||||
|
||||
def fetch_password_known_test(self):
|
||||
"""
|
||||
Test the fetch password code with the defined userid
|
||||
"""
|
||||
# GIVEN: A default configuration
|
||||
# WHEN: called with the defined userid
|
||||
password = fetch_password(u'openlp')
|
||||
required_password = make_sha_hash(u'password')
|
||||
|
||||
# THEN: the function should return the correct password
|
||||
self.assertEqual(password, required_password, u'The result for fetch_password should be the defined password')
|
||||
|
||||
def sha_password_encrypter_test(self):
|
||||
"""
|
||||
Test hash password function
|
||||
"""
|
||||
# GIVEN: A default configuration
|
||||
# WHEN: called with the defined userid
|
||||
required_password = make_sha_hash(u'password')
|
||||
test_value = u'5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8'
|
||||
|
||||
# THEN: the function should return the correct password
|
||||
self.assertEqual(required_password, test_value,
|
||||
u'The result for make_sha_hash should return the correct encrypted password')
|
||||
|
||||
def process_http_request_test(self):
|
||||
"""
|
||||
Test the router control functionality
|
||||
"""
|
||||
# GIVEN: A testing set of Routes
|
||||
mocked_function = MagicMock()
|
||||
test_route = [
|
||||
(r'^/stage/api/poll$', mocked_function),
|
||||
]
|
||||
self.router.routes = test_route
|
||||
|
||||
# WHEN: called with a poll route
|
||||
self.router.process_http_request(u'/stage/api/poll', None)
|
||||
|
||||
# THEN: the function should have been called only once
|
||||
assert mocked_function.call_count == 1, \
|
||||
u'The mocked function should have been matched and called once.'
|
0
tests/interfaces/openlp_plugins/remotes/__init__.py
Normal file
0
tests/interfaces/openlp_plugins/remotes/__init__.py
Normal file
138
tests/interfaces/openlp_plugins/remotes/test_server.py
Normal file
138
tests/interfaces/openlp_plugins/remotes/test_server.py
Normal file
@ -0,0 +1,138 @@
|
||||
"""
|
||||
This module contains tests for the lib submodule of the Remotes plugin.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from tempfile import mkstemp
|
||||
from mock import MagicMock
|
||||
import urllib2
|
||||
import cherrypy
|
||||
|
||||
from BeautifulSoup import BeautifulSoup
|
||||
|
||||
from openlp.core.lib import Settings
|
||||
from openlp.plugins.remotes.lib.httpserver import HttpServer
|
||||
from PyQt4 import QtGui
|
||||
|
||||
__default_settings__ = {
|
||||
u'remotes/twelve hour': True,
|
||||
u'remotes/port': 4316,
|
||||
u'remotes/https port': 4317,
|
||||
u'remotes/https enabled': False,
|
||||
u'remotes/user id': u'openlp',
|
||||
u'remotes/password': u'password',
|
||||
u'remotes/authentication enabled': False,
|
||||
u'remotes/ip address': u'0.0.0.0'
|
||||
}
|
||||
|
||||
|
||||
class TestRouter(TestCase):
|
||||
"""
|
||||
Test the functions in the :mod:`lib` module.
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
fd, self.ini_file = mkstemp(u'.ini')
|
||||
Settings().set_filename(self.ini_file)
|
||||
self.application = QtGui.QApplication.instance()
|
||||
Settings().extend_default_settings(__default_settings__)
|
||||
self.server = HttpServer()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
del self.application
|
||||
os.unlink(self.ini_file)
|
||||
self.server.close()
|
||||
|
||||
def start_server(self):
|
||||
"""
|
||||
Common function to start server then mock out the router. CherryPy crashes if you mock before you start
|
||||
"""
|
||||
self.server.start_server()
|
||||
self.server.router = MagicMock()
|
||||
self.server.router.process_http_request = process_http_request
|
||||
|
||||
def start_default_server_test(self):
|
||||
"""
|
||||
Test the default server serves the correct initial page
|
||||
"""
|
||||
# GIVEN: A default configuration
|
||||
Settings().setValue(u'remotes/authentication enabled', False)
|
||||
self.start_server()
|
||||
|
||||
# WHEN: called the route location
|
||||
code, page = call_remote_server(u'http://localhost:4316')
|
||||
|
||||
# THEN: default title will be returned
|
||||
self.assertEqual(BeautifulSoup(page).title.text, u'OpenLP 2.1 Remote',
|
||||
u'The default menu should be returned')
|
||||
|
||||
def start_authenticating_server_test(self):
|
||||
"""
|
||||
Test the default server serves the correctly with authentication
|
||||
"""
|
||||
# GIVEN: A default authorised configuration
|
||||
Settings().setValue(u'remotes/authentication enabled', True)
|
||||
self.start_server()
|
||||
|
||||
# WHEN: called the route location with no user details
|
||||
code, page = call_remote_server(u'http://localhost:4316')
|
||||
|
||||
# THEN: then server will ask for details
|
||||
self.assertEqual(code, 401, u'The basic authorisation request should be returned')
|
||||
|
||||
# WHEN: called the route location with user details
|
||||
code, page = call_remote_server(u'http://localhost:4316', u'openlp', u'password')
|
||||
|
||||
# THEN: default title will be returned
|
||||
self.assertEqual(BeautifulSoup(page).title.text, u'OpenLP 2.1 Remote',
|
||||
u'The default menu should be returned')
|
||||
|
||||
# WHEN: called the route location with incorrect user details
|
||||
code, page = call_remote_server(u'http://localhost:4316', u'itwinkle', u'password')
|
||||
|
||||
# THEN: then server will ask for details
|
||||
self.assertEqual(code, 401, u'The basic authorisation request should be returned')
|
||||
|
||||
|
||||
def call_remote_server(url, username=None, password=None):
|
||||
"""
|
||||
Helper function
|
||||
|
||||
``username``
|
||||
The username.
|
||||
|
||||
``password``
|
||||
The password.
|
||||
"""
|
||||
if username:
|
||||
passman = urllib2.HTTPPasswordMgrWithDefaultRealm()
|
||||
passman.add_password(None, url, username, password)
|
||||
authhandler = urllib2.HTTPBasicAuthHandler(passman)
|
||||
opener = urllib2.build_opener(authhandler)
|
||||
urllib2.install_opener(opener)
|
||||
try:
|
||||
page = urllib2.urlopen(url)
|
||||
return 0, page.read()
|
||||
except urllib2.HTTPError, e:
|
||||
return e.code, u''
|
||||
|
||||
|
||||
def process_http_request(url_path, *args):
|
||||
"""
|
||||
Override function to make the Mock work but does nothing.
|
||||
|
||||
``Url_path``
|
||||
The url_path.
|
||||
|
||||
``*args``
|
||||
Some args.
|
||||
"""
|
||||
cherrypy.response.status = 200
|
||||
return None
|
||||
|
0
tests/resources/remotes/openlp.crt
Normal file
0
tests/resources/remotes/openlp.crt
Normal file
0
tests/resources/remotes/openlp.key
Normal file
0
tests/resources/remotes/openlp.key
Normal file
Loading…
Reference in New Issue
Block a user