forked from openlp/openlp
Initial Cherrypy implementation
This commit is contained in:
parent
44ebc1fe27
commit
f1aadde13c
@ -69,6 +69,11 @@ try:
|
||||
MAKO_VERSION = mako.__version__
|
||||
except ImportError:
|
||||
MAKO_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')
|
||||
@ -138,6 +143,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'pyUNO bridge: %s\n' % UNO_VERSION
|
||||
if platform.system() == u'Linux':
|
||||
if os.environ.get(u'KDE_FULL_SESSION') == u'true':
|
||||
|
@ -362,8 +362,9 @@ class SlideController(DisplayController):
|
||||
# Signals
|
||||
QtCore.QObject.connect(self.previewListWidget, QtCore.SIGNAL(u'clicked(QModelIndex)'), self.onSlideSelected)
|
||||
if self.isLive:
|
||||
# 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.setWidgetVisible(self.loopList, False)
|
||||
self.toolbar.setWidgetVisible(self.wideMenu, False)
|
||||
else:
|
||||
@ -867,9 +868,9 @@ class SlideController(DisplayController):
|
||||
"""
|
||||
Go to the requested slide
|
||||
"""
|
||||
index = int(message[0])
|
||||
if not self.serviceItem:
|
||||
if not self.serviceItem or not message[0]:
|
||||
return
|
||||
index = int(message[0])
|
||||
if self.serviceItem.is_command():
|
||||
Registry().execute(u'%s_slide' % self.serviceItem.name.lower(), [self.serviceItem, self.isLive, index])
|
||||
self.updatePreview()
|
||||
|
@ -90,6 +90,7 @@ class AppLocation(object):
|
||||
VersionDir = 5
|
||||
CacheDir = 6
|
||||
LanguageDir = 7
|
||||
SharedData = 8
|
||||
|
||||
# Base path where data/config/cache dir is located
|
||||
BaseDir = None
|
||||
@ -150,18 +151,18 @@ def _get_os_dir_path(dir_type):
|
||||
if sys.platform == u'win32':
|
||||
if dir_type == AppLocation.DataDir:
|
||||
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp', u'data')
|
||||
elif dir_type == AppLocation.LanguageDir:
|
||||
elif dir_type == AppLocation.LanguageDir or dir_type == AppLocation.SharedData:
|
||||
return os.path.split(openlp.__file__)[0]
|
||||
return os.path.join(unicode(os.getenv(u'APPDATA'), encoding), u'openlp')
|
||||
elif sys.platform == u'darwin':
|
||||
if dir_type == AppLocation.DataDir:
|
||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding),
|
||||
u'Library', u'Application Support', u'openlp', u'Data')
|
||||
elif dir_type == AppLocation.LanguageDir:
|
||||
elif dir_type == AppLocation.LanguageDir or dir_type == AppLocation.SharedData:
|
||||
return os.path.split(openlp.__file__)[0]
|
||||
return os.path.join(unicode(os.getenv(u'HOME'), encoding), u'Library', u'Application Support', u'openlp')
|
||||
else:
|
||||
if dir_type == AppLocation.LanguageDir:
|
||||
if dir_type == AppLocation.LanguageDir or dir_type == AppLocation.SharedData:
|
||||
prefixes = [u'/usr/local', u'/usr']
|
||||
for prefix in prefixes:
|
||||
directory = os.path.join(prefix, u'share', u'openlp')
|
||||
|
@ -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 ||
|
||||
|
192
openlp/plugins/remotes/lib/httpauth.py
Normal file
192
openlp/plugins/remotes/lib/httpauth.py
Normal file
@ -0,0 +1,192 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
|
||||
"""
|
||||
The :mod:`http` module manages the HTTP authorisation logic. This code originates from
|
||||
http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions
|
||||
|
||||
"""
|
||||
|
||||
import cherrypy
|
||||
import urlparse
|
||||
|
||||
SESSION_KEY = '_cp_openlp'
|
||||
|
||||
|
||||
def check_credentials(user_name, password):
|
||||
"""
|
||||
Verifies credentials for username and password.
|
||||
Returns None on success or a string describing the error on failure
|
||||
"""
|
||||
# @todo make from config
|
||||
if user_name == 'openlp' and password == 'openlp':
|
||||
return None
|
||||
else:
|
||||
return u"Incorrect username or password."
|
||||
# if u.password != md5.new(password).hexdigest():
|
||||
# return u"Incorrect password"
|
||||
|
||||
|
||||
def check_auth(*args, **kwargs):
|
||||
"""
|
||||
A tool that looks in config for 'auth.require'. If found and it
|
||||
is not None, a login is required and the entry is evaluated as a list of
|
||||
conditions that the user must fulfill
|
||||
"""
|
||||
print "check"
|
||||
conditions = cherrypy.request.config.get('auth.require', None)
|
||||
print conditions
|
||||
print args, kwargs
|
||||
print urlparse.urlparse(cherrypy.url())
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
print urlparse.parse_qs(url.query)
|
||||
if conditions is not None:
|
||||
username = cherrypy.session.get(SESSION_KEY)
|
||||
if username:
|
||||
cherrypy.request.login = username
|
||||
for condition in conditions:
|
||||
# A condition is just a callable that returns true or false
|
||||
if not condition():
|
||||
raise cherrypy.HTTPRedirect("/auth/login")
|
||||
else:
|
||||
raise cherrypy.HTTPRedirect("/auth/login")
|
||||
|
||||
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
|
||||
|
||||
|
||||
def require_auth(*conditions):
|
||||
"""
|
||||
A decorator that appends conditions to the auth.require config variable.
|
||||
"""
|
||||
print conditions
|
||||
def decorate(f):
|
||||
if not hasattr(f, '_cp_config'):
|
||||
f._cp_config = dict()
|
||||
if 'auth.require' not in f._cp_config:
|
||||
f._cp_config['auth.require'] = []
|
||||
f._cp_config['auth.require'].extend(conditions)
|
||||
print "a ", [f]
|
||||
return f
|
||||
return decorate
|
||||
|
||||
|
||||
# Conditions are callables that return True
|
||||
# if the user fulfills the conditions they define, False otherwise
|
||||
#
|
||||
# They can access the current username as cherrypy.request.login
|
||||
#
|
||||
# Define those at will however suits the application.
|
||||
|
||||
#def member_of(groupname):
|
||||
# def check():
|
||||
# # replace with actual check if <username> is in <groupname>
|
||||
# return cherrypy.request.login == 'joe' and groupname == 'admin'
|
||||
# return check
|
||||
|
||||
|
||||
#def name_is(reqd_username):
|
||||
# return lambda: reqd_username == cherrypy.request.login
|
||||
|
||||
#def any_of(*conditions):
|
||||
# """
|
||||
# Returns True if any of the conditions match
|
||||
# """
|
||||
# def check():
|
||||
# for c in conditions:
|
||||
# if c():
|
||||
# return True
|
||||
# return False
|
||||
# return check
|
||||
|
||||
# By default all conditions are required, but this might still be
|
||||
# needed if you want to use it inside of an any_of(...) condition
|
||||
#def all_of(*conditions):
|
||||
# """
|
||||
# Returns True if all of the conditions match
|
||||
# """
|
||||
# def check():
|
||||
# for c in conditions:
|
||||
# if not c():
|
||||
# return False
|
||||
# return True
|
||||
# return check
|
||||
# Controller to provide login and logout actions
|
||||
|
||||
|
||||
class AuthController(object):
|
||||
|
||||
def on_login(self, username):
|
||||
"""
|
||||
Called on successful login
|
||||
"""
|
||||
|
||||
def on_logout(self, username):
|
||||
"""
|
||||
Called on logout
|
||||
"""
|
||||
|
||||
def get_loginform(self, username, msg="Enter login information", from_page="/"):
|
||||
"""
|
||||
Provides a login form
|
||||
"""
|
||||
return """<html><body>
|
||||
<form method="post" action="/auth/login">
|
||||
<input type="hidden" name="from_page" value="%(from_page)s" />
|
||||
%(msg)s<br />
|
||||
Username: <input type="text" name="username" value="%(username)s" /><br />
|
||||
Password: <input type="password" name="password" /><br />
|
||||
<input type="submit" value="Log in" />
|
||||
</body></html>""" % locals()
|
||||
|
||||
@cherrypy.expose
|
||||
def login(self, username=None, password=None, from_page="/"):
|
||||
"""
|
||||
Provides the actual login control
|
||||
"""
|
||||
if username is None or password is None:
|
||||
return self.get_loginform("", from_page=from_page)
|
||||
|
||||
error_msg = check_credentials(username, password)
|
||||
if error_msg:
|
||||
return self.get_loginform(username, error_msg, from_page)
|
||||
else:
|
||||
cherrypy.session[SESSION_KEY] = cherrypy.request.login = username
|
||||
self.on_login(username)
|
||||
raise cherrypy.HTTPRedirect(from_page or "/")
|
||||
|
||||
@cherrypy.expose
|
||||
def logout(self, from_page="/"):
|
||||
sess = cherrypy.session
|
||||
username = sess.get(SESSION_KEY, None)
|
||||
sess[SESSION_KEY] = None
|
||||
if username:
|
||||
cherrypy.request.login = None
|
||||
self.on_logout(username)
|
||||
raise cherrypy.HTTPRedirect(from_page or "/")
|
||||
|
@ -119,40 +119,23 @@ 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 openlp.plugins.remotes.lib.httpauth import AuthController, require_auth
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
class HttpResponse(object):
|
||||
"""
|
||||
A simple object to encapsulate a pseudo-http response.
|
||||
"""
|
||||
code = '200 OK'
|
||||
content = ''
|
||||
headers = {
|
||||
'Content-Type': 'text/html; charset="utf-8"\r\n'
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
|
||||
class HttpServer(object):
|
||||
"""
|
||||
Ability to control OpenLP via a web browser.
|
||||
"""
|
||||
|
||||
def __init__(self, plugin):
|
||||
"""
|
||||
Initialise the httpserver, and start the server.
|
||||
@ -163,22 +146,28 @@ class HttpServer(object):
|
||||
self.connections = []
|
||||
self.current_item = None
|
||||
self.current_slide = None
|
||||
self.start_tcp()
|
||||
self.conf = {'/files': {u'tools.staticdir.on': True,
|
||||
u'tools.staticdir.dir': self.html_dir}}
|
||||
self.start_server()
|
||||
|
||||
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.
|
||||
"""
|
||||
log.debug(u'Start TCP server')
|
||||
log.debug(u'Start CherryPy server')
|
||||
port = Settings().value(self.plugin.settingsSection + u'/port')
|
||||
address = Settings().value(self.plugin.settingsSection + u'/ip address')
|
||||
self.server = QtNetwork.QTcpServer()
|
||||
self.server.listen(QtNetwork.QHostAddress(address), port)
|
||||
server_config = {u'server.socket_host': str(address),
|
||||
u'server.socket_port': port}
|
||||
cherrypy.config.update(server_config)
|
||||
cherrypy.config.update({'environment': 'embedded'})
|
||||
cherrypy.config.update({'engine.autoreload_on': False})
|
||||
cherrypy.tree.mount(HttpConnection(self), '/', config=self.conf)
|
||||
cherrypy.engine.start()
|
||||
Registry().register_function(u'slidecontroller_live_changed', self.slide_change)
|
||||
Registry().register_function(u'slidecontroller_live_started', self.item_change)
|
||||
QtCore.QObject.connect(self.server, QtCore.SIGNAL(u'newConnection()'), self.new_connection)
|
||||
log.debug(u'TCP listening on port %d' % port)
|
||||
|
||||
def slide_change(self, row):
|
||||
@ -193,50 +182,41 @@ class HttpServer(object):
|
||||
"""
|
||||
self.current_item = items[0]
|
||||
|
||||
def new_connection(self):
|
||||
"""
|
||||
A new http connection has been made. Create a client object to handle
|
||||
communication.
|
||||
"""
|
||||
log.debug(u'new http connection')
|
||||
socket = self.server.nextPendingConnection()
|
||||
if socket:
|
||||
self.connections.append(HttpConnection(self, socket))
|
||||
|
||||
def close_connection(self, connection):
|
||||
"""
|
||||
The connection has been closed. Clean up
|
||||
"""
|
||||
log.debug(u'close http connection')
|
||||
if connection in self.connections:
|
||||
self.connections.remove(connection)
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
Close down the http server.
|
||||
"""
|
||||
log.debug(u'close http server')
|
||||
self.server.close()
|
||||
cherrypy.engine.exit()
|
||||
cherrypy.engine.stop()
|
||||
|
||||
|
||||
class HttpConnection(object):
|
||||
"""
|
||||
A single connection, this handles communication between the server
|
||||
and the client.
|
||||
A single connection, this handles communication between the server and the client.
|
||||
"""
|
||||
def __init__(self, parent, socket):
|
||||
_cp_config = {
|
||||
'tools.sessions.on': True,
|
||||
'tools.auth.on': True
|
||||
}
|
||||
|
||||
auth = AuthController()
|
||||
|
||||
def __init__(self, parent):
|
||||
"""
|
||||
Initialise the http connection. Listen out for socket signals.
|
||||
"""
|
||||
log.debug(u'Initialise HttpConnection: %s' % socket.peerAddress())
|
||||
self.socket = socket
|
||||
#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/(.*)$', self.controller),
|
||||
(r'^/api/service/(.*)$', self.service),
|
||||
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
|
||||
(r'^/api/alert$', self.alert),
|
||||
@ -245,17 +225,79 @@ class HttpConnection(object):
|
||||
(r'^/api/(.*)/live$', self.go_live),
|
||||
(r'^/api/(.*)/add$', self.add_to_service)
|
||||
]
|
||||
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'readyRead()'), self.ready_read)
|
||||
QtCore.QObject.connect(self.socket, QtCore.SIGNAL(u'disconnected()'), self.disconnected)
|
||||
self.translate()
|
||||
|
||||
@cherrypy.expose
|
||||
#@require_auth(auth)
|
||||
def default(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the main url. This is secure depending on settings.
|
||||
"""
|
||||
# Loop through the routes we set up earlier and execute them
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def stage(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the stage url. This is not secure.
|
||||
"""
|
||||
print "Stage"
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
self.url_params = urlparse.parse_qs(url.query)
|
||||
print url
|
||||
print [self.url_params]
|
||||
#return self.serve_file(u'stage')
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def files(self, *args, **kwargs):
|
||||
"""
|
||||
Handles the requests for the stage url. This is not secure.
|
||||
"""
|
||||
print "files"
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
self.url_params = urlparse.parse_qs(url.query)
|
||||
print url
|
||||
print [self.url_params]
|
||||
print args
|
||||
#return self.serve_file(args)
|
||||
return self._process_http_request(args, kwargs)
|
||||
|
||||
def _process_http_request(self, args, kwargs):
|
||||
"""
|
||||
Common function to process HTTP requests where secure or insecure
|
||||
"""
|
||||
print "common handler"
|
||||
url = urlparse.urlparse(cherrypy.url())
|
||||
self.url_params = urlparse.parse_qs(url.query)
|
||||
print url
|
||||
print [self.url_params]
|
||||
response = None
|
||||
for route, func in self.routes:
|
||||
match = re.match(route, url.path)
|
||||
if match:
|
||||
print 'Route "%s" matched "%s"', route, url.path
|
||||
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.parent.current_item:
|
||||
current_unique_identifier = self.parent.current_item.unique_identifier
|
||||
else:
|
||||
current_unique_identifier = None
|
||||
for item in self.service_manager.serviceItems:
|
||||
for item in self.service_manager.service_items:
|
||||
service_item = item[u'service_item']
|
||||
service_items.append({
|
||||
u'id': unicode(service_item.unique_identifier),
|
||||
@ -296,40 +338,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
|
||||
@ -339,6 +347,7 @@ class HttpConnection(object):
|
||||
Ultimately for i18n, this could first look for xx/file.html before
|
||||
falling back to file.html... where xx is the language, e.g. 'en'
|
||||
"""
|
||||
print "serve_file", filename
|
||||
log.debug(u'serve file request %s' % filename)
|
||||
if not filename:
|
||||
filename = u'index.html'
|
||||
@ -346,7 +355,7 @@ class HttpConnection(object):
|
||||
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')
|
||||
return self._http_not_found()
|
||||
ext = os.path.splitext(filename)[1]
|
||||
html = None
|
||||
if ext == u'.html':
|
||||
@ -375,11 +384,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):
|
||||
"""
|
||||
@ -389,24 +399,25 @@ class HttpConnection(object):
|
||||
u'service': self.service_manager.service_id,
|
||||
u'slide': self.parent.current_slide or 0,
|
||||
u'item': self.parent.current_item.unique_identifier if self.parent.current_item else u'',
|
||||
u'twelve':Settings().value(u'remotes/twelve hour'),
|
||||
u'twelve': Settings().value(u'remotes/twelve hour'),
|
||||
u'blank': self.live_controller.blankScreen.isChecked(),
|
||||
u'theme': self.live_controller.themeScreen.isChecked(),
|
||||
u'display': self.live_controller.desktopScreen.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):
|
||||
"""
|
||||
@ -417,14 +428,14 @@ class HttpConnection(object):
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[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])
|
||||
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, type, action):
|
||||
"""
|
||||
@ -465,34 +476,37 @@ class HttpConnection(object):
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
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])
|
||||
else:
|
||||
Registry().execute(event)
|
||||
Registry().execute(event, [0])
|
||||
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):
|
||||
"""
|
||||
List details of the Service and update the UI
|
||||
"""
|
||||
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'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': self._get_service_items()}})
|
||||
else:
|
||||
event += u'_item'
|
||||
if self.url_params and self.url_params.get(u'data'):
|
||||
try:
|
||||
data = json.loads(self.url_params[u'data'][0])
|
||||
except KeyError, ValueError:
|
||||
return HttpResponse(code=u'400 Bad Request')
|
||||
return self._http_bad_request()
|
||||
Registry().execute(event, data[u'request'][u'id'])
|
||||
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):
|
||||
"""
|
||||
@ -507,9 +521,8 @@ class HttpConnection(object):
|
||||
for plugin in self.plugin_manager.plugins:
|
||||
if plugin.status == PluginStatus.Active and plugin.mediaItem and plugin.mediaItem.hasSearch:
|
||||
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'})
|
||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||
return json.dumps({u'results': {u'items': searches}})
|
||||
|
||||
def search(self, type):
|
||||
"""
|
||||
@ -521,15 +534,15 @@ class HttpConnection(object):
|
||||
try:
|
||||
text = json.loads(self.url_params[u'data'][0])[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(type)
|
||||
if plugin.status == PluginStatus.Active and plugin.mediaItem and plugin.mediaItem.hasSearch:
|
||||
results = plugin.mediaItem.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, type):
|
||||
"""
|
||||
@ -538,11 +551,11 @@ class HttpConnection(object):
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[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(type)
|
||||
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
||||
plugin.mediaItem.goLive(id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
return self._http_success()
|
||||
|
||||
def add_to_service(self, type):
|
||||
"""
|
||||
@ -551,38 +564,22 @@ class HttpConnection(object):
|
||||
try:
|
||||
id = json.loads(self.url_params[u'data'][0])[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(type)
|
||||
if plugin.status == PluginStatus.Active and plugin.mediaItem:
|
||||
item_id = plugin.mediaItem.createItemFromId(id)
|
||||
plugin.mediaItem.addToService(item_id, remote=True)
|
||||
return HttpResponse(code=u'200 OK')
|
||||
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):
|
||||
cherrypy.response.status = 200
|
||||
|
||||
def disconnected(self):
|
||||
"""
|
||||
The client has disconnected. Tidy up
|
||||
"""
|
||||
log.debug(u'socket disconnected')
|
||||
self.close()
|
||||
def _http_bad_request(self):
|
||||
cherrypy.response.status = 400
|
||||
|
||||
def close(self):
|
||||
"""
|
||||
The server has closed the connection. Tidy up
|
||||
"""
|
||||
if not self.socket:
|
||||
return
|
||||
log.debug(u'close socket')
|
||||
self.socket.close()
|
||||
self.socket = None
|
||||
self.parent.close_connection(self)
|
||||
def _http_not_found(self):
|
||||
cherrypy.response.status = 404
|
||||
cherrypy.response.body = ["<html><body>Sorry, an error occured</body></html>"]
|
||||
|
||||
def _get_service_manager(self):
|
||||
"""
|
||||
|
@ -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.utils import AppLocation
|
||||
|
||||
|
||||
ZERO_URL = u'0.0.0.0'
|
||||
@ -45,117 +48,203 @@ class RemoteTab(SettingsTab):
|
||||
def setupUi(self):
|
||||
self.setObjectName(u'RemoteTab')
|
||||
SettingsTab.setupUi(self)
|
||||
self.serverSettingsGroupBox = QtGui.QGroupBox(self.leftColumn)
|
||||
self.serverSettingsGroupBox.setObjectName(u'serverSettingsGroupBox')
|
||||
self.serverSettingsLayout = QtGui.QFormLayout(self.serverSettingsGroupBox)
|
||||
self.serverSettingsLayout.setObjectName(u'serverSettingsLayout')
|
||||
self.addressLabel = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.addressLabel.setObjectName(u'addressLabel')
|
||||
self.addressEdit = QtGui.QLineEdit(self.serverSettingsGroupBox)
|
||||
self.addressEdit.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Fixed)
|
||||
self.addressEdit.setValidator(QtGui.QRegExpValidator(QtCore.QRegExp(
|
||||
u'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}'), self))
|
||||
self.addressEdit.setObjectName(u'addressEdit')
|
||||
QtCore.QObject.connect(self.addressEdit, QtCore.SIGNAL(u'textChanged(const QString&)'), self.setUrls)
|
||||
self.serverSettingsLayout.addRow(self.addressLabel, self.addressEdit)
|
||||
self.twelveHourCheckBox = QtGui.QCheckBox(self.serverSettingsGroupBox)
|
||||
self.twelveHourCheckBox.setObjectName(u'twelveHourCheckBox')
|
||||
self.serverSettingsLayout.addRow(self.twelveHourCheckBox)
|
||||
self.portLabel = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.portLabel.setObjectName(u'portLabel')
|
||||
self.portSpinBox = QtGui.QSpinBox(self.serverSettingsGroupBox)
|
||||
self.portSpinBox.setMaximum(32767)
|
||||
self.portSpinBox.setObjectName(u'portSpinBox')
|
||||
QtCore.QObject.connect(self.portSpinBox, QtCore.SIGNAL(u'valueChanged(int)'), self.setUrls)
|
||||
self.serverSettingsLayout.addRow(self.portLabel, self.portSpinBox)
|
||||
self.remoteUrlLabel = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.remoteUrlLabel.setObjectName(u'remoteUrlLabel')
|
||||
self.remoteUrl = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.remoteUrl.setObjectName(u'remoteUrl')
|
||||
self.remoteUrl.setOpenExternalLinks(True)
|
||||
self.serverSettingsLayout.addRow(self.remoteUrlLabel, self.remoteUrl)
|
||||
self.stageUrlLabel = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.stageUrlLabel.setObjectName(u'stageUrlLabel')
|
||||
self.stageUrl = QtGui.QLabel(self.serverSettingsGroupBox)
|
||||
self.stageUrl.setObjectName(u'stageUrl')
|
||||
self.stageUrl.setOpenExternalLinks(True)
|
||||
self.serverSettingsLayout.addRow(self.stageUrlLabel, self.stageUrl)
|
||||
self.leftLayout.addWidget(self.serverSettingsGroupBox)
|
||||
self.androidAppGroupBox = QtGui.QGroupBox(self.rightColumn)
|
||||
self.androidAppGroupBox.setObjectName(u'androidAppGroupBox')
|
||||
self.rightLayout.addWidget(self.androidAppGroupBox)
|
||||
self.qrLayout = QtGui.QVBoxLayout(self.androidAppGroupBox)
|
||||
self.qrLayout.setObjectName(u'qrLayout')
|
||||
self.qrCodeLabel = QtGui.QLabel(self.androidAppGroupBox)
|
||||
self.qrCodeLabel.setPixmap(QtGui.QPixmap(u':/remotes/android_app_qr.png'))
|
||||
self.qrCodeLabel.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.qrCodeLabel.setObjectName(u'qrCodeLabel')
|
||||
self.qrLayout.addWidget(self.qrCodeLabel)
|
||||
self.qrDescriptionLabel = QtGui.QLabel(self.androidAppGroupBox)
|
||||
self.qrDescriptionLabel.setObjectName(u'qrDescriptionLabel')
|
||||
self.qrDescriptionLabel.setOpenExternalLinks(True)
|
||||
self.qrDescriptionLabel.setWordWrap(True)
|
||||
self.qrLayout.addWidget(self.qrDescriptionLabel)
|
||||
self.server_settings_group_box = QtGui.QGroupBox(self.leftColumn)
|
||||
self.server_settings_group_box.setObjectName(u'server_settings_group_box')
|
||||
self.server_settings_layout = QtGui.QFormLayout(self.server_settings_group_box)
|
||||
self.server_settings_layout.setObjectName(u'server_settings_layout')
|
||||
self.address_label = QtGui.QLabel(self.server_settings_group_box)
|
||||
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.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.leftLayout.addWidget(self.server_settings_group_box)
|
||||
self.http_settings_group_box = QtGui.QGroupBox(self.leftColumn)
|
||||
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.http_settings_group_box)
|
||||
self.port_spin_box.setMaximum(32767)
|
||||
self.port_spin_box.setObjectName(u'port_spin_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.http_settings_group_box)
|
||||
self.remote_url.setObjectName(u'remote_url')
|
||||
self.remote_url.setOpenExternalLinks(True)
|
||||
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.http_settings_group_box)
|
||||
self.stage_url.setObjectName(u'stage_url')
|
||||
self.stage_url.setOpenExternalLinks(True)
|
||||
self.http_setting_layout.addRow(self.stage_url_label, self.stage_url)
|
||||
self.leftLayout.addWidget(self.http_settings_group_box)
|
||||
self.https_settings_group_box = QtGui.QGroupBox(self.leftColumn)
|
||||
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.leftLayout.addWidget(self.https_settings_group_box)
|
||||
self.user_login_group_box = QtGui.QGroupBox(self.leftColumn)
|
||||
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.leftLayout.addWidget(self.user_login_group_box)
|
||||
self.android_app_group_box = QtGui.QGroupBox(self.rightColumn)
|
||||
self.android_app_group_box.setObjectName(u'android_app_group_box')
|
||||
self.rightLayout.addWidget(self.android_app_group_box)
|
||||
self.qr_layout = QtGui.QVBoxLayout(self.android_app_group_box)
|
||||
self.qr_layout.setObjectName(u'qr_layout')
|
||||
self.qr_code_label = QtGui.QLabel(self.android_app_group_box)
|
||||
self.qr_code_label.setPixmap(QtGui.QPixmap(u':/remotes/android_app_qr.png'))
|
||||
self.qr_code_label.setAlignment(QtCore.Qt.AlignCenter)
|
||||
self.qr_code_label.setObjectName(u'qr_code_label')
|
||||
self.qr_layout.addWidget(self.qr_code_label)
|
||||
self.qr_description_label = QtGui.QLabel(self.android_app_group_box)
|
||||
self.qr_description_label.setObjectName(u'qr_description_label')
|
||||
self.qr_description_label.setOpenExternalLinks(True)
|
||||
self.qr_description_label.setWordWrap(True)
|
||||
self.qr_layout.addWidget(self.qr_description_label)
|
||||
self.leftLayout.addStretch()
|
||||
self.rightLayout.addStretch()
|
||||
QtCore.QObject.connect(self.twelveHourCheckBox, QtCore.SIGNAL(u'stateChanged(int)'),
|
||||
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)
|
||||
|
||||
def retranslateUi(self):
|
||||
self.serverSettingsGroupBox.setTitle(
|
||||
translate('RemotePlugin.RemoteTab', 'Server Settings'))
|
||||
self.addressLabel.setText(translate('RemotePlugin.RemoteTab', 'Serve on IP address:'))
|
||||
self.portLabel.setText(translate('RemotePlugin.RemoteTab', 'Port number:'))
|
||||
self.remoteUrlLabel.setText(translate('RemotePlugin.RemoteTab', 'Remote URL:'))
|
||||
self.stageUrlLabel.setText(translate('RemotePlugin.RemoteTab', 'Stage view URL:'))
|
||||
self.twelveHourCheckBox.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
|
||||
self.androidAppGroupBox.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
|
||||
self.qrDescriptionLabel.setText(translate('RemotePlugin.RemoteTab',
|
||||
'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.server_settings_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Server Settings'))
|
||||
self.address_label.setText(translate('RemotePlugin.RemoteTab', 'Serve on IP address:'))
|
||||
self.port_label.setText(translate('RemotePlugin.RemoteTab', 'Port number:'))
|
||||
self.remote_url_label.setText(translate('RemotePlugin.RemoteTab', 'Remote URL:'))
|
||||
self.stage_url_label.setText(translate('RemotePlugin.RemoteTab', 'Stage view URL:'))
|
||||
self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
|
||||
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
|
||||
self.qr_description_label.setText(translate('RemotePlugin.RemoteTab',
|
||||
'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 setUrls(self):
|
||||
def set_urls(self):
|
||||
ipAddress = u'localhost'
|
||||
if self.addressEdit.text() == ZERO_URL:
|
||||
ifaces = QtNetwork.QNetworkInterface.allInterfaces()
|
||||
for iface in ifaces:
|
||||
if not iface.isValid():
|
||||
if self.address_edit.text() == ZERO_URL:
|
||||
interfaces = QtNetwork.QNetworkInterface.allInterfaces()
|
||||
for interface in interfaces:
|
||||
if not interface.isValid():
|
||||
continue
|
||||
if not (iface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)):
|
||||
if not (interface.flags() & (QtNetwork.QNetworkInterface.IsUp | QtNetwork.QNetworkInterface.IsRunning)):
|
||||
continue
|
||||
for addr in iface.addressEntries():
|
||||
ip = addr.ip()
|
||||
for address in interface.addressEntries():
|
||||
ip = address.ip()
|
||||
if ip.protocol() == 0 and ip != QtNetwork.QHostAddress.LocalHost:
|
||||
ipAddress = ip
|
||||
break
|
||||
else:
|
||||
ipAddress = self.addressEdit.text()
|
||||
url = u'http://%s:%s/' % (ipAddress, self.portSpinBox.value())
|
||||
self.remoteUrl.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
url += u'stage'
|
||||
self.stageUrl.setText(u'<a href="%s">%s</a>' % (url, url))
|
||||
ipAddress = self.address_edit.text()
|
||||
http_url = u'http://%s:%s/' % (ipAddress, self.port_spin_box.value())
|
||||
https_url = u'https://%s:%s/' % (ipAddress, 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):
|
||||
self.portSpinBox.setValue(Settings().value(self.settingsSection + u'/port'))
|
||||
self.addressEdit.setText(Settings().value(self.settingsSection + u'/ip address'))
|
||||
self.twelveHour = Settings().value(self.settingsSection + u'/twelve hour')
|
||||
self.twelveHourCheckBox.setChecked(self.twelveHour)
|
||||
self.setUrls()
|
||||
self.port_spin_box.setValue(Settings().value(self.settingsSection + u'/port'))
|
||||
self.https_port_spin_box.setValue(Settings().value(self.settingsSection + u'/https port'))
|
||||
self.address_edit.setText(Settings().value(self.settingsSection + u'/ip address'))
|
||||
self.twelve_hour = Settings().value(self.settingsSection + u'/twelve hour')
|
||||
self.twelve_hour_check_box.setChecked(self.twelve_hour)
|
||||
shared_data = AppLocation.get_directory(AppLocation.SharedData)
|
||||
if not os.path.exists(os.path.join(shared_data, u'openlp.crt')) or \
|
||||
not os.path.exists(os.path.join(shared_data, 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.settingsSection + 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.settingsSection + u'/authentication enabled'))
|
||||
self.user_id.setText(Settings().value(self.settingsSection + u'/user id'))
|
||||
self.password.setText(Settings().value(self.settingsSection + u'/password'))
|
||||
self.set_urls()
|
||||
|
||||
def save(self):
|
||||
changed = False
|
||||
if Settings().value(self.settingsSection + u'/ip address') != self.addressEdit.text() or \
|
||||
Settings().value(self.settingsSection + u'/port') != self.portSpinBox.value():
|
||||
if Settings().value(self.settingsSection + u'/ip address') != self.address_edit.text() or \
|
||||
Settings().value(self.settingsSection + u'/port') != self.port_spin_box.value() or \
|
||||
Settings().value(self.settingsSection + u'/https port') != self.https_port_spin_box.value() or \
|
||||
Settings().value(self.settingsSection + u'/https enabled') != self.https_settings_group_box.isChecked():
|
||||
changed = True
|
||||
Settings().setValue(self.settingsSection + u'/port', self.portSpinBox.value())
|
||||
Settings().setValue(self.settingsSection + u'/ip address', self.addressEdit.text())
|
||||
Settings().setValue(self.settingsSection + u'/twelve hour', self.twelveHour)
|
||||
Settings().setValue(self.settingsSection + u'/port', self.port_spin_box.value())
|
||||
Settings().setValue(self.settingsSection + u'/https port', self.https_port_spin_box.value())
|
||||
Settings().setValue(self.settingsSection + u'/https enabled', self.https_settings_group_box.isChecked())
|
||||
Settings().setValue(self.settingsSection + u'/ip address', self.address_edit.text())
|
||||
Settings().setValue(self.settingsSection + u'/twelve hour', self.twelve_hour)
|
||||
Settings().setValue(self.settingsSection + u'/authentication enabled', self.user_login_group_box.isChecked())
|
||||
Settings().setValue(self.settingsSection + u'/user id', self.user_id.text())
|
||||
Settings().setValue(self.settingsSection + u'/password', self.password.text())
|
||||
if changed:
|
||||
Registry().register_function(u'remotes_config_updated')
|
||||
|
||||
def onTwelveHourCheckBoxChanged(self, check_state):
|
||||
self.twelveHour = False
|
||||
def on_twelve_hour_check_box_changed(self, check_state):
|
||||
self.twelve_hour = False
|
||||
# we have a set value convert to True/False
|
||||
if check_state == QtCore.Qt.Checked:
|
||||
self.twelveHour = True
|
||||
self.twelve_hour = True
|
||||
|
@ -37,6 +37,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'
|
||||
}
|
||||
|
||||
|
@ -81,6 +81,7 @@ MODULES = [
|
||||
'enchant',
|
||||
'BeautifulSoup',
|
||||
'mako',
|
||||
'cherrypy',
|
||||
'migrate',
|
||||
'uno',
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user