From f1aadde13cd30ecf89afb8d8bd07ad9f4e0b5210 Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Wed, 6 Mar 2013 21:53:29 +0000
Subject: [PATCH 01/22] Initial Cherrypy implementation
---
openlp/core/ui/exceptionform.py | 6 +
openlp/core/ui/slidecontroller.py | 7 +-
openlp/core/utils/__init__.py | 7 +-
openlp/plugins/remotes/html/stage.js | 6 +-
openlp/plugins/remotes/lib/httpauth.py | 192 ++++++++++++++++
openlp/plugins/remotes/lib/httpserver.py | 281 +++++++++++------------
openlp/plugins/remotes/lib/remotetab.py | 271 ++++++++++++++--------
openlp/plugins/remotes/remoteplugin.py | 5 +
scripts/check_dependencies.py | 1 +
9 files changed, 534 insertions(+), 242 deletions(-)
create mode 100644 openlp/plugins/remotes/lib/httpauth.py
diff --git a/openlp/core/ui/exceptionform.py b/openlp/core/ui/exceptionform.py
index 50885b15b..c10f98d9b 100644
--- a/openlp/core/ui/exceptionform.py
+++ b/openlp/core/ui/exceptionform.py
@@ -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':
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 84ea296d2..3a2c0b582 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -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()
diff --git a/openlp/core/utils/__init__.py b/openlp/core/utils/__init__.py
index 104567039..dd611d303 100644
--- a/openlp/core/utils/__init__.py
+++ b/openlp/core/utils/__init__.py
@@ -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')
diff --git a/openlp/plugins/remotes/html/stage.js b/openlp/plugins/remotes/html/stage.js
index dcc2e4b70..dff51537c 100644
--- a/openlp/plugins/remotes/html/stage.js
+++ b/openlp/plugins/remotes/html/stage.js
@@ -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 ||
diff --git a/openlp/plugins/remotes/lib/httpauth.py b/openlp/plugins/remotes/lib/httpauth.py
new file mode 100644
index 000000000..ce3ea091e
--- /dev/null
+++ b/openlp/plugins/remotes/lib/httpauth.py
@@ -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 is in
+# 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 """
+
+ User name:
+ Password:
+
+
+
\ No newline at end of file
diff --git a/openlp/plugins/remotes/html/openlp.css b/openlp/plugins/remotes/html/openlp.css
index 4bc1bf907..60a8fe625 100644
--- a/openlp/plugins/remotes/html/openlp.css
+++ b/openlp/plugins/remotes/html/openlp.css
@@ -36,3 +36,11 @@
.ui-li .ui-btn-text a.ui-link-inherit{
white-space: normal;
}
+
+.ui-page{
+ padding: 100px 100px 100px 100px;
+ width: 300px;
+}
+.ui-input-text{
+ width: 30px;
+}
\ No newline at end of file
diff --git a/openlp/plugins/remotes/lib/httpauth.py b/openlp/plugins/remotes/lib/httpauth.py
index 7e0e2ebe5..d46620855 100644
--- a/openlp/plugins/remotes/lib/httpauth.py
+++ b/openlp/plugins/remotes/lib/httpauth.py
@@ -35,8 +35,12 @@ http://tools.cherrypy.org/wiki/AuthenticationAndAccessRestrictions
import cherrypy
import logging
+import os
+
+from mako.template import Template
from openlp.core.lib import Settings
+from openlp.core.utils import AppLocation, translate
SESSION_KEY = '_cp_openlp'
@@ -48,6 +52,7 @@ def check_credentials(user_name, password):
Verifies credentials for username and password.
Returns None on success or a string describing the error on failure
"""
+ print "check"
if user_name == Settings().value(u'remotes/user id') and password == Settings().value(u'remotes/password'):
return None
else:
@@ -70,9 +75,12 @@ def check_auth(*args, **kwargs):
for condition in conditions:
# A condition is just a callable that returns true or false
if not condition():
+ print "r1"
raise cherrypy.HTTPRedirect("/auth/login")
else:
+ print "r2"
raise cherrypy.HTTPRedirect("/auth/login")
+ print "r3"
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
@@ -100,36 +108,31 @@ class AuthController(object):
"""
Called on successful login
"""
+ pass
def on_logout(self, username):
"""
Called on logout
"""
+ pass
- def get_loginform(self, username, msg="Enter login information", from_page="/"):
+ def get_login_form(self, username, message=None, from_page="/"):
"""
Provides a login form
"""
- return """
-
-
-
- User Login
-
-
-
-
-
-
-
-
-
-
- %(msg)s
- Username:
- Password:
-
- """ % locals()
+ if not message:
+ message = translate('RemotePlugin.Mobile', 'Enter login information')
+ variables = {
+ 'title': translate('RemotePlugin.Mobile', 'OpenLP 2.1 User Login'),
+ 'from_page': from_page,
+ 'message': message,
+ 'username': username
+ }
+ directory = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
+ login_html = os.path.normpath(os.path.join(directory, u'login.html'))
+ html = Template(filename=login_html, input_encoding=u'utf-8', output_encoding=u'utf-8').render(**variables)
+ cherrypy.response.headers['Content-Type'] = u'text/html'
+ return html
@cherrypy.expose
def login(self, username=None, password=None, from_page="/"):
@@ -137,14 +140,14 @@ class AuthController(object):
Provides the actual login control
"""
if username is None or password is None:
- return self.get_loginform("", from_page=from_page)
-
+ return self.get_login_form("", from_page=from_page)
error_msg = check_credentials(username, password)
if error_msg:
- return self.get_loginform(username, error_msg, from_page)
+ return self.get_login_form(username, from_page, error_msg,)
else:
cherrypy.session[SESSION_KEY] = cherrypy.request.login = username
self.on_login(username)
+ print from_page
raise cherrypy.HTTPRedirect(from_page or "/")
@cherrypy.expose
diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py
index 211608858..db6143eb0 100644
--- a/openlp/plugins/remotes/lib/httpserver.py
+++ b/openlp/plugins/remotes/lib/httpserver.py
@@ -174,6 +174,8 @@ class HttpServer(object):
cherrypy.config.update({'environment': 'embedded'})
cherrypy.config.update({'engine.autoreload_on': False})
cherrypy.tree.mount(HttpConnection(self), '/', config=self.conf)
+ # Turn off the flood of access messages cause by poll
+ cherrypy.log.access_log.propagate = False
cherrypy.engine.start()
Registry().register_function(u'slidecontroller_live_changed', self.slide_change)
Registry().register_function(u'slidecontroller_live_started', self.item_change)
From 3c32bc75011eac3b7ed3d07d90c8f818a6dab80d Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Wed, 13 Mar 2013 19:51:56 +0000
Subject: [PATCH 05/22] Added tests
---
openlp/plugins/remotes/lib/httpauth.py | 2 +-
.../openlp_core_lib/test_settings.py | 4 +-
.../openlp_plugins/remotes/__init__.py | 1 +
.../openlp_plugins/remotes/test_auth.py | 65 +++++++++++++++++++
4 files changed, 70 insertions(+), 2 deletions(-)
create mode 100644 tests/functional/openlp_plugins/remotes/__init__.py
create mode 100644 tests/functional/openlp_plugins/remotes/test_auth.py
diff --git a/openlp/plugins/remotes/lib/httpauth.py b/openlp/plugins/remotes/lib/httpauth.py
index d46620855..6fe4197e2 100644
--- a/openlp/plugins/remotes/lib/httpauth.py
+++ b/openlp/plugins/remotes/lib/httpauth.py
@@ -56,7 +56,7 @@ def check_credentials(user_name, password):
if user_name == Settings().value(u'remotes/user id') and password == Settings().value(u'remotes/password'):
return None
else:
- return u"Incorrect username or password."
+ return translate('RemotePlugin.Mobile', 'Incorrect username or password.')
def check_auth(*args, **kwargs):
diff --git a/tests/functional/openlp_core_lib/test_settings.py b/tests/functional/openlp_core_lib/test_settings.py
index 827bfa156..b06bb4eac 100644
--- a/tests/functional/openlp_core_lib/test_settings.py
+++ b/tests/functional/openlp_core_lib/test_settings.py
@@ -11,7 +11,9 @@ from PyQt4 import QtGui
class TestSettings(TestCase):
-
+ """
+ Test the functions in the Settings module
+ """
def setUp(self):
"""
Create the UI
diff --git a/tests/functional/openlp_plugins/remotes/__init__.py b/tests/functional/openlp_plugins/remotes/__init__.py
new file mode 100644
index 000000000..f87606f07
--- /dev/null
+++ b/tests/functional/openlp_plugins/remotes/__init__.py
@@ -0,0 +1 @@
+__author__ = 'tim'
diff --git a/tests/functional/openlp_plugins/remotes/test_auth.py b/tests/functional/openlp_plugins/remotes/test_auth.py
new file mode 100644
index 000000000..a300c0127
--- /dev/null
+++ b/tests/functional/openlp_plugins/remotes/test_auth.py
@@ -0,0 +1,65 @@
+"""
+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.httpauth import check_credentials
+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 TestLib(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__)
+
+ 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)
+ os.unlink(Settings().fileName())
+
+ def check_credentials_test(self):
+ """
+ Test the clean_string() function
+ """
+ # GIVEN: A user and password
+ Settings().setValue(u'remotes/user id', u'twinkle')
+ Settings().setValue(u'remotes/password', u'mongoose')
+
+ # WHEN: We run the string through the function
+ authenticated = check_credentials(u'', u'')
+
+ # THEN: The string should be cleaned up and lower-cased
+ self.assertEqual(authenticated, u'Incorrect username or password.',
+ u'The return should be a error message string')
+
+ # WHEN: We run the string through the function
+ authenticated = check_credentials(u'twinkle', u'mongoose')
+
+ # THEN: The string should be cleaned up and lower-cased
+ self.assertEqual(authenticated, None, u'The return should be a None string')
From d04dbd791f9ef141f947ca555fbc014cf84fd614 Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Fri, 15 Mar 2013 08:40:00 +0000
Subject: [PATCH 06/22] more changes
---
openlp/plugins/remotes/html/login.html | 2 +-
openlp/plugins/remotes/html/openlp.css | 8 --------
openlp/plugins/remotes/html/openlp.js | 2 +-
openlp/plugins/remotes/lib/httpauth.py | 3 ---
openlp/plugins/remotes/lib/httpserver.py | 7 +------
5 files changed, 3 insertions(+), 19 deletions(-)
diff --git a/openlp/plugins/remotes/html/login.html b/openlp/plugins/remotes/html/login.html
index 4441958de..736d3f0ab 100644
--- a/openlp/plugins/remotes/html/login.html
+++ b/openlp/plugins/remotes/html/login.html
@@ -33,7 +33,7 @@
${title}
-
+
diff --git a/openlp/plugins/remotes/html/openlp.css b/openlp/plugins/remotes/html/openlp.css
index 60a8fe625..4bc1bf907 100644
--- a/openlp/plugins/remotes/html/openlp.css
+++ b/openlp/plugins/remotes/html/openlp.css
@@ -36,11 +36,3 @@
.ui-li .ui-btn-text a.ui-link-inherit{
white-space: normal;
}
-
-.ui-page{
- padding: 100px 100px 100px 100px;
- width: 300px;
-}
-.ui-input-text{
- width: 30px;
-}
\ No newline at end of file
diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js
index 00877e332..7c5c19e32 100644
--- a/openlp/plugins/remotes/html/openlp.js
+++ b/openlp/plugins/remotes/html/openlp.js
@@ -359,5 +359,5 @@ $.ajaxSetup({cache: false});
$("#search").live("pageinit", function (event) {
OpenLP.getSearchablePlugins();
});
-setInterval("OpenLP.pollServer();", 500);
+setInterval("OpenLP.pollServer();", 5000);
OpenLP.pollServer();
diff --git a/openlp/plugins/remotes/lib/httpauth.py b/openlp/plugins/remotes/lib/httpauth.py
index 6fe4197e2..bd3c1f911 100644
--- a/openlp/plugins/remotes/lib/httpauth.py
+++ b/openlp/plugins/remotes/lib/httpauth.py
@@ -75,12 +75,9 @@ def check_auth(*args, **kwargs):
for condition in conditions:
# A condition is just a callable that returns true or false
if not condition():
- print "r1"
raise cherrypy.HTTPRedirect("/auth/login")
else:
- print "r2"
raise cherrypy.HTTPRedirect("/auth/login")
- print "r3"
cherrypy.tools.auth = cherrypy.Tool('before_handler', check_auth)
diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py
index b8acc574f..308533b9d 100644
--- a/openlp/plugins/remotes/lib/httpserver.py
+++ b/openlp/plugins/remotes/lib/httpserver.py
@@ -144,8 +144,6 @@ class HttpServer(object):
self.plugin = plugin
self.html_dir = os.path.join(AppLocation.get_directory(AppLocation.PluginsDir), u'remotes', u'html')
self.connections = []
- self.current_item = None
- self.current_slide = None
self.conf = {'/files': {u'tools.staticdir.on': True,
u'tools.staticdir.dir': self.html_dir}}
self.start_server()
@@ -177,8 +175,6 @@ class HttpServer(object):
# Turn off the flood of access messages cause by poll
cherrypy.log.access_log.propagate = False
cherrypy.engine.start()
- Registry().register_function(u'slidecontroller_live_changed', self.slide_change)
- Registry().register_function(u'slidecontroller_live_started', self.item_change)
log.debug(u'TCP listening on port %d' % port)
def close(self):
@@ -481,8 +477,7 @@ class HttpConnection(object):
if action == u'list':
cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': {u'items': self._get_service_items()}})
- else:
- event += u'_item'
+ event += u'_item'
if self.url_params and self.url_params.get(u'data'):
try:
data = json.loads(self.url_params[u'data'][0])
From 0f6216d653f1df071556e6e57867d8c2c477ff84 Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Sun, 17 Mar 2013 21:20:40 +0000
Subject: [PATCH 07/22] More changes
---
openlp/core/ui/servicemanager.py | 5 ++++-
openlp/core/ui/slidecontroller.py | 2 +-
openlp/plugins/remotes/html/openlp.js | 4 ++--
openlp/plugins/remotes/lib/httpserver.py | 17 +++++++++++------
4 files changed, 18 insertions(+), 10 deletions(-)
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 9e99e2303..c7ab8dd77 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -270,7 +270,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):
"""
@@ -313,6 +312,9 @@ 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
+ print self
+ QtCore.QObject.connect(self, QtCore.SIGNAL(u'servicemanager_set_item'), self.on_set_item)
def set_modified(self, modified=True):
"""
@@ -1008,6 +1010,7 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
"""
Called by a signal to select a specific item.
"""
+ print "hello", message
self.set_item(int(message))
def set_item(self, index):
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 0c22a6353..3b2824fda 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -99,7 +99,7 @@ class SlideController(DisplayController):
u'delay_spin_box'
]
self.audio_list = [
- u'audio_pause_item',
+ u'audioPauseItem',
u'audio_time_label'
]
self.wide_menu = [
diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js
index 7c5c19e32..3cbe65366 100644
--- a/openlp/plugins/remotes/html/openlp.js
+++ b/openlp/plugins/remotes/html/openlp.js
@@ -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;
@@ -359,5 +359,5 @@ $.ajaxSetup({cache: false});
$("#search").live("pageinit", function (event) {
OpenLP.getSearchablePlugins();
});
-setInterval("OpenLP.pollServer();", 5000);
+setInterval("OpenLP.pollServer();", 500);
OpenLP.pollServer();
diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py
index 308533b9d..a594c1bca 100644
--- a/openlp/plugins/remotes/lib/httpserver.py
+++ b/openlp/plugins/remotes/lib/httpserver.py
@@ -199,7 +199,7 @@ class HttpConnection(object):
def __init__(self, parent):
"""
- Initialise the http connection. Listen out for socket signals.
+ Initialise the CherryPy Server
"""
self.parent = parent
self.routes = [
@@ -229,6 +229,9 @@ class HttpConnection(object):
"""
url = urlparse.urlparse(cherrypy.url())
self.url_params = urlparse.parse_qs(url.query)
+ self.request_data = None
+ if isinstance(kwargs, dict):
+ self.request_data = kwargs.get(u'data', None)
# Loop through the routes we set up earlier and execute them
return self._process_http_request(args, kwargs)
@@ -255,7 +258,7 @@ class HttpConnection(object):
Common function to process HTTP requests where secure or insecure
"""
url = urlparse.urlparse(cherrypy.url())
- self.url_params = urlparse.parse_qs(url.query)
+ self.url_params = kwargs
response = None
for route, func in self.routes:
match = re.match(route, url.path)
@@ -478,13 +481,15 @@ class HttpConnection(object):
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:
+ data = json.loads(self.request_data)[u'request'][u'id']
+ except KeyError:
return self._http_bad_request()
- Registry().execute(event, data[u'request'][u'id'])
+ print "A", event , data
+ self.service_manager.emit(QtCore.SIGNAL(event, data))
else:
+ print "B", event
Registry().execute(event)
cherrypy.response.headers['Content-Type'] = u'application/json'
return json.dumps({u'results': {u'success': True}})
From 729c93b70b6a154dfc723405dd40c75410a57c8b Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Wed, 20 Mar 2013 20:17:00 +0000
Subject: [PATCH 08/22] More changes
---
openlp/core/ui/servicemanager.py | 2 +-
openlp/plugins/remotes/lib/httpserver.py | 40 +++++++++++-------------
2 files changed, 20 insertions(+), 22 deletions(-)
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 97c6ca2db..7c3745299 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -1008,7 +1008,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.
"""
print "hello", message
self.set_item(int(message))
diff --git a/openlp/plugins/remotes/lib/httpserver.py b/openlp/plugins/remotes/lib/httpserver.py
index a594c1bca..368bf0192 100644
--- a/openlp/plugins/remotes/lib/httpserver.py
+++ b/openlp/plugins/remotes/lib/httpserver.py
@@ -155,17 +155,17 @@ class HttpServer(object):
clients. Listen out for socket connections.
"""
log.debug(u'Start CherryPy server')
- if Settings().value(self.plugin.settingsSection + u'/https enabled'):
- port = Settings().value(self.plugin.settingsSection + u'/https port')
- address = Settings().value(self.plugin.settingsSection + u'/ip address')
+ if Settings().value(self.plugin.settings_section + u'/https enabled'):
+ port = Settings().value(self.plugin.settings_section + u'/https port')
+ address = Settings().value(self.plugin.settings_section + u'/ip address')
shared_data = AppLocation.get_directory(AppLocation.SharedData)
server_config = {u'server.socket_host': str(address),
u'server.socket_port': port,
u'server.ssl_certificate': os.path.join(shared_data, u'openlp.crt'),
u'server.ssl_private_key': os.path.join(shared_data, u'openlp.key')}
else:
- port = Settings().value(self.plugin.settingsSection + u'/port')
- address = Settings().value(self.plugin.settingsSection + u'/ip address')
+ port = Settings().value(self.plugin.settings_section + u'/port')
+ address = Settings().value(self.plugin.settings_section + u'/ip address')
server_config = {u'server.socket_host': str(address),
u'server.socket_port': port}
cherrypy.config.update(server_config)
@@ -214,7 +214,7 @@ class HttpConnection(object):
(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)
@@ -456,9 +456,9 @@ 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 self._http_bad_request()
log.info(data)
@@ -486,15 +486,13 @@ class HttpConnection(object):
data = json.loads(self.request_data)[u'request'][u'id']
except KeyError:
return self._http_bad_request()
- print "A", event , data
- self.service_manager.emit(QtCore.SIGNAL(event, data))
+ self.service_manager.emit(QtCore.SIGNAL(event), data)
else:
- print "B", event
Registry().execute(event)
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.
@@ -505,8 +503,8 @@ class HttpConnection(object):
if action == u'search':
searches = []
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'])])
+ if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.hasSearch:
+ 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}})
@@ -523,8 +521,8 @@ class HttpConnection(object):
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.mediaItem and plugin.mediaItem.hasSearch:
- results = plugin.mediaItem.search(text, False)
+ if plugin.status == PluginStatus.Active and plugin.media_item and plugin.media_item.has_search:
+ results = plugin.media_item.search(text, False)
else:
results = []
cherrypy.response.headers['Content-Type'] = u'application/json'
@@ -539,8 +537,8 @@ class HttpConnection(object):
except KeyError, ValueError:
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)
+ if plugin.status == PluginStatus.Active and plugin.media_item:
+ plugin.media_item.go_live(id, remote=True)
return self._http_success()
def add_to_service(self, plugin_name):
@@ -552,9 +550,9 @@ class HttpConnection(object):
except KeyError, ValueError:
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)
+ if plugin.status == PluginStatus.Active and plugin.media_item:
+ item_id = plugin.media_item.create_item_from_id(id)
+ plugin.media_item.add_to_service(item_id, remote=True)
self._http_success()
def _http_success(self):
From 401f5ac2be3b087abfecf4bf6fbbcbca06289d7e Mon Sep 17 00:00:00 2001
From: Tim Bentley
Date: Tue, 26 Mar 2013 08:55:05 +0000
Subject: [PATCH 09/22] More updates
---
openlp/core/ui/slidecontroller.py | 2 -
openlp/plugins/remotes/html/login.html | 3 +-
openlp/plugins/remotes/lib/httpauth.py | 10 ++---
openlp/plugins/remotes/lib/httpserver.py | 14 ++-----
.../openlp_plugins/remotes/test_auth.py | 38 +++++++++++++++----
5 files changed, 40 insertions(+), 27 deletions(-)
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 518e2a715..a66d0057b 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -1080,7 +1080,6 @@ class SlideController(DisplayController):
"""
Go to the next slide.
"""
- print "next"
if not self.service_item:
return
Registry().execute(u'%s_next' % self.service_item.name.lower(), [self.service_item, self.is_live])
@@ -1108,7 +1107,6 @@ class SlideController(DisplayController):
"""
Go to the previous slide.
"""
- print "prev"
if not self.service_item:
return
Registry().execute(u'%s_previous' % self.service_item.name.lower(), [self.service_item, self.is_live])
diff --git a/openlp/plugins/remotes/html/login.html b/openlp/plugins/remotes/html/login.html
index 736d3f0ab..5d649629b 100644
--- a/openlp/plugins/remotes/html/login.html
+++ b/openlp/plugins/remotes/html/login.html
@@ -36,7 +36,6 @@
-
@@ -48,4 +47,4 @@
Password: