forked from openlp/openlp
move remote to api and add waitress
This commit is contained in:
parent
fad8b35660
commit
32fad35f79
@ -178,29 +178,3 @@ class Registry(object):
|
||||
"""
|
||||
if key in self.working_flags:
|
||||
del self.working_flags[key]
|
||||
|
||||
def remote_api(self, path, function, secure=False):
|
||||
"""
|
||||
Sets a working_flag based on the key passed in.
|
||||
|
||||
:param path: The working_flag to be created this is usually a major class like "renderer" or "main_window" .
|
||||
:param function: The data to be saved.
|
||||
:param secure: The data to be saved.
|
||||
"""
|
||||
self.remote_apis[path] = {'function': function, 'secure': secure}
|
||||
|
||||
def remote_execute(self, url_path):
|
||||
"""
|
||||
Execute all the handlers associated with the event and return an array of results.
|
||||
|
||||
:param url_path: The url path to be found
|
||||
"""
|
||||
for url, funcs in self.remote_apis.items():
|
||||
a = url
|
||||
match = re.match(url, url_path)
|
||||
if match:
|
||||
log.debug('Route "{route}" matched "{path}"'.format(route=url_path, path=url_path))
|
||||
args = []
|
||||
for param in match.groups():
|
||||
args.append(param)
|
||||
return funcs, args
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
from .poll import OpenLPPoll
|
||||
from .wsserver import OpenWSServer
|
||||
from .httpserver import OpenLPHttpServer
|
||||
from .remotecontroller import RemoteController
|
||||
|
||||
__all__ = ['OpenLPPoll', 'RemoteController']
|
||||
__all__ = ['OpenLPPoll', 'RemoteController', 'OpenLPHttpServer']
|
72
openlp/core/lib/api/httpserver.py
Normal file
72
openlp/core/lib/api/httpserver.py
Normal file
@ -0,0 +1,72 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2016 OpenLP Developers #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# 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 contains the API web server. This is a lightweight web server used by remotes to interact
|
||||
with OpenLP. It uses JSON to communicate with the remotes.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from waitress import serve
|
||||
|
||||
from PyQt5 import QtCore
|
||||
|
||||
from openlp.core.common import RegistryProperties, OpenLPMixin
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class HttpThread(QtCore.QThread):
|
||||
"""
|
||||
A special Qt thread class to allow the HTTP server to run at the same time as the UI.
|
||||
"""
|
||||
def __init__(self, server):
|
||||
"""
|
||||
Constructor for the thread class.
|
||||
|
||||
:param server: The http server class.
|
||||
"""
|
||||
super(HttpThread, self).__init__(None)
|
||||
self.http_server = server
|
||||
|
||||
def run(self):
|
||||
"""
|
||||
Run the thread.
|
||||
"""
|
||||
wsgiapp = object()
|
||||
serve(wsgiapp, host='0.0.0.0', port=4317)
|
||||
|
||||
def stop(self):
|
||||
self.http_server.stop = True
|
||||
|
||||
|
||||
class OpenLPHttpServer(RegistryProperties, OpenLPMixin):
|
||||
"""
|
||||
Wrapper round a server instance
|
||||
"""
|
||||
def __init__(self, secure=False):
|
||||
"""
|
||||
Initialise the http server, and start the server of the correct type http / https
|
||||
"""
|
||||
super(OpenLPHttpServer, self).__init__()
|
||||
self.http_thread = HttpThread(self)
|
||||
self.http_thread.start()
|
@ -22,7 +22,7 @@
|
||||
import logging
|
||||
|
||||
from openlp.core.common import OpenLPMixin, Registry, RegistryMixin, RegistryProperties
|
||||
from openlp.core.lib.remote import OpenWSServer, OpenLPPoll
|
||||
from openlp.core.lib.api import OpenWSServer, OpenLPPoll, OpenLPHttpServer
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@ -54,3 +54,4 @@ class RemoteController(RegistryMixin, OpenLPMixin, RegistryProperties):
|
||||
self.poll = OpenLPPoll()
|
||||
Registry().register('OpenLPPoll', self.poll)
|
||||
self.wsserver = OpenWSServer()
|
||||
self.httpserver = OpenLPHttpServer()
|
@ -453,7 +453,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||
:param service_item: The service Item to be processed
|
||||
:param item: The database item to be used to build the service item
|
||||
:param xml_version:
|
||||
:param remote: Was this remote triggered (False)
|
||||
:param remote: Was this api triggered (False)
|
||||
:param context: The service context
|
||||
"""
|
||||
raise NotImplementedError('MediaManagerItem.generate_slide_data needs to be defined by the plugin')
|
||||
@ -580,7 +580,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||
|
||||
:param item: Item to be processed
|
||||
:param replace: Replace the existing item
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param position: Position to place item
|
||||
"""
|
||||
service_item = self.build_service_item(item, True, remote=remote, context=ServiceItemContext.Service)
|
||||
@ -607,7 +607,7 @@ class MediaManagerItem(QtWidgets.QWidget, RegistryProperties):
|
||||
self.generate_slide_data(service_item)
|
||||
self.service_manager.add_service_item(service_item, replace=True)
|
||||
else:
|
||||
# Turn off the remote edit update message indicator
|
||||
# Turn off the api edit update message indicator
|
||||
QtWidgets.QMessageBox.information(self, translate('OpenLP.MediaManagerItem', 'Invalid Service Item'),
|
||||
translate('OpenLP.MediaManagerItem',
|
||||
'You must select a {title} '
|
||||
|
@ -229,7 +229,7 @@ ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
|
||||
E_CONNECTION_REFUSED: translate('OpenLP.ProjectorConstants',
|
||||
'The connection was refused by the peer (or timed out)'),
|
||||
E_REMOTE_HOST_CLOSED_CONNECTION: translate('OpenLP.ProjectorConstants',
|
||||
'The remote host closed the connection'),
|
||||
'The api host closed the connection'),
|
||||
E_HOST_NOT_FOUND: translate('OpenLP.ProjectorConstants', 'The host address was not found'),
|
||||
E_SOCKET_ACCESS: translate('OpenLP.ProjectorConstants',
|
||||
'The socket operation failed because the application '
|
||||
|
@ -396,7 +396,7 @@ class PJLink1(QTcpSocket):
|
||||
return
|
||||
log.debug('({ip}) get_data(): Checking new data "{data}"'.format(ip=self.ip, data=data))
|
||||
if data.upper().startswith('PJLINK'):
|
||||
# Reconnected from remote host disconnect ?
|
||||
# Reconnected from api host disconnect ?
|
||||
self.check_login(data)
|
||||
self.send_busy = False
|
||||
self.projectorReceivedData.emit()
|
||||
|
@ -286,7 +286,7 @@ class ServiceItem(RegistryProperties):
|
||||
:param path: The directory in which the image file is located.
|
||||
:param title: A title for the slide in the service item.
|
||||
:param background:
|
||||
:param thumbnail: Optional alternative thumbnail, used for remote thumbnails.
|
||||
:param thumbnail: Optional alternative thumbnail, used for api thumbnails.
|
||||
"""
|
||||
if background:
|
||||
self.image_border = background
|
||||
|
@ -40,7 +40,7 @@ from openlp.core.common.actions import ActionList, CategoryOrder
|
||||
from openlp.core.common.versionchecker import get_application_version
|
||||
from openlp.core.lib import Renderer, PluginManager, ImageManager, PluginStatus, ScreenList, build_icon
|
||||
from openlp.core.lib.ui import UiStrings, create_action
|
||||
from openlp.core.lib.remote import RemoteController
|
||||
from openlp.core.lib.api import RemoteController
|
||||
from openlp.core.ui import AboutForm, SettingsForm, ServiceManager, ThemeManager, LiveController, PluginForm, \
|
||||
ShortcutListForm, FormattingTagForm, PreviewController
|
||||
from openlp.core.ui.firsttimeform import FirstTimeForm
|
||||
|
@ -344,7 +344,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
||||
"""
|
||||
Setter for property "modified". Sets whether or not the current service has been modified.
|
||||
|
||||
:param modified: Indicates if the service has new or removed items. Used to trigger a remote update.
|
||||
:param modified: Indicates if the service has new or removed items. Used to trigger a api update.
|
||||
"""
|
||||
if modified:
|
||||
self.service_id += 1
|
||||
@ -1079,7 +1079,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
||||
|
||||
def on_set_item(self, message, field=None):
|
||||
"""
|
||||
Called by a signal to select a specific item and make it live usually from remote.
|
||||
Called by a signal to select a specific item and make it live usually from api.
|
||||
|
||||
:param field:
|
||||
:param message: The data passed in from a remove message
|
||||
@ -1523,7 +1523,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtWidgets.QWidget, Ui_ServiceMa
|
||||
|
||||
def remote_edit(self, field=None):
|
||||
"""
|
||||
Triggers a remote edit to a plugin to allow item to be edited.
|
||||
Triggers a api edit to a plugin to allow item to be edited.
|
||||
:param field:
|
||||
"""
|
||||
item = self.find_service_item()[0]
|
||||
|
@ -531,7 +531,7 @@ class SlideController(DisplayController, RegistryProperties):
|
||||
|
||||
def toggle_display(self, action):
|
||||
"""
|
||||
Toggle the display settings triggered from remote messages.
|
||||
Toggle the display settings triggered from api messages.
|
||||
|
||||
:param action: The blank action to be processed.
|
||||
"""
|
||||
@ -797,7 +797,7 @@ class SlideController(DisplayController, RegistryProperties):
|
||||
|
||||
def replace_service_manager_item(self, item):
|
||||
"""
|
||||
Replacement item following a remote edit
|
||||
Replacement item following a api edit
|
||||
|
||||
:param item: The current service item
|
||||
"""
|
||||
@ -841,7 +841,7 @@ class SlideController(DisplayController, RegistryProperties):
|
||||
"""
|
||||
self.on_stop_loop()
|
||||
old_item = self.service_item
|
||||
# rest to allow the remote pick up verse 1 if large imaged
|
||||
# rest to allow the api pick up verse 1 if large imaged
|
||||
self.selected_row = 0
|
||||
# take a copy not a link to the servicemanager copy.
|
||||
self.service_item = copy.copy(service_item)
|
||||
@ -931,7 +931,7 @@ class SlideController(DisplayController, RegistryProperties):
|
||||
"""
|
||||
Go to the requested slide
|
||||
|
||||
:param message: remote message to be processed.
|
||||
:param message: api message to be processed.
|
||||
"""
|
||||
index = int(message[0])
|
||||
if not self.service_item:
|
||||
|
@ -798,7 +798,7 @@ class BibleMediaItem(MediaManagerItem):
|
||||
:param service_item: The service item to be built on
|
||||
:param item: The Song item to be used
|
||||
:param xml_version: The xml version (not used)
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param context: Why is it being generated
|
||||
"""
|
||||
log.debug('generating slide data')
|
||||
|
@ -129,7 +129,7 @@ class CustomMediaItem(MediaManagerItem):
|
||||
self.list_view.setCurrentItem(custom_name)
|
||||
self.auto_select_id = -1
|
||||
# Called to redisplay the custom list screen edith from a search
|
||||
# or from the exit of the Custom edit dialog. If remote editing is
|
||||
# or from the exit of the Custom edit dialog. If api editing is
|
||||
# active trigger it and clean up so it will not update again.
|
||||
self.check_search_result()
|
||||
|
||||
|
@ -543,7 +543,7 @@ class ImageMediaItem(MediaManagerItem):
|
||||
:param service_item: The service item to be built on
|
||||
:param item: The Song item to be used
|
||||
:param xml_version: The xml version (not used)
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param context: Why is it being generated
|
||||
"""
|
||||
background = QtGui.QColor(Settings().value(self.settings_section + '/background color'))
|
||||
|
@ -242,7 +242,7 @@ class MediaMediaItem(MediaManagerItem, RegistryProperties):
|
||||
:param service_item: The service item to be built on
|
||||
:param item: The Song item to be used
|
||||
:param xml_version: The xml version (not used)
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param context: Why is it being generated
|
||||
"""
|
||||
if item is None:
|
||||
|
@ -266,7 +266,7 @@ class PresentationMediaItem(MediaManagerItem):
|
||||
:param service_item: The service item to be built on
|
||||
:param item: The Song item to be used
|
||||
:param xml_version: The xml version (not used)
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param context: Why is it being generated
|
||||
"""
|
||||
if item:
|
||||
|
2
openlp/plugins/remotes/html/assets/jquery.js
vendored
2
openlp/plugins/remotes/html/assets/jquery.js
vendored
@ -7158,7 +7158,7 @@ jQuery.fn.extend({
|
||||
|
||||
var self = this;
|
||||
|
||||
// Request the remote document
|
||||
// Request the api document
|
||||
jQuery.ajax({
|
||||
url: url,
|
||||
type: type,
|
||||
|
@ -151,7 +151,7 @@ class RemoteTab(SettingsTab):
|
||||
self.live_url_label.setText(translate('RemotePlugin.RemoteTab', 'Live view URL:'))
|
||||
self.twelve_hour_check_box.setText(translate('RemotePlugin.RemoteTab', 'Display stage time in 12h format'))
|
||||
self.thumbnails_check_box.setText(translate('RemotePlugin.RemoteTab',
|
||||
'Show thumbnails of non-text slides in remote and stage view.'))
|
||||
'Show thumbnails of non-text slides in api and stage view.'))
|
||||
self.android_app_group_box.setTitle(translate('RemotePlugin.RemoteTab', 'Android App'))
|
||||
self.android_qr_description_label.setText(
|
||||
translate('RemotePlugin.RemoteTab',
|
||||
@ -252,10 +252,10 @@ class RemoteTab(SettingsTab):
|
||||
Generate icon for main window
|
||||
"""
|
||||
self.remote_server_icon.hide()
|
||||
icon = QtGui.QImage(':/remote/network_server.png')
|
||||
icon = QtGui.QImage(':/api/network_server.png')
|
||||
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
if Settings().value(self.settings_section + '/authentication enabled'):
|
||||
overlay = QtGui.QImage(':/remote/network_auth.png')
|
||||
overlay = QtGui.QImage(':/api/network_auth.png')
|
||||
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
|
||||
painter = QtGui.QPainter(icon)
|
||||
painter.drawImage(20, 0, overlay)
|
||||
|
@ -92,9 +92,9 @@ class RemotesPlugin(Plugin, OpenLPMixin):
|
||||
Information about this plugin
|
||||
"""
|
||||
about_text = translate('RemotePlugin', '<strong>Remote Plugin</strong>'
|
||||
'<br />The remote plugin provides the ability to send messages to '
|
||||
'<br />The api plugin provides the ability to send messages to '
|
||||
'a running version of OpenLP on a different computer via a web '
|
||||
'browser or through the remote API.')
|
||||
'browser or through the api API.')
|
||||
return about_text
|
||||
|
||||
def set_plugin_text_strings(self):
|
||||
@ -115,7 +115,7 @@ class RemotesPlugin(Plugin, OpenLPMixin):
|
||||
"""
|
||||
Called when Config is changed to requests a restart with the server on new address or port
|
||||
"""
|
||||
log.debug('remote config changed')
|
||||
log.debug('api config changed')
|
||||
QtWidgets.QMessageBox.information(self.main_window,
|
||||
translate('RemotePlugin', 'Server Config Change'),
|
||||
translate('RemotePlugin',
|
||||
|
@ -242,11 +242,11 @@ class SongMediaItem(MediaManagerItem):
|
||||
|
||||
def on_song_list_load(self):
|
||||
"""
|
||||
Handle the exit from the edit dialog and trigger remote updates of songs
|
||||
Handle the exit from the edit dialog and trigger api updates of songs
|
||||
"""
|
||||
log.debug('on_song_list_load - start')
|
||||
# Called to redisplay the song list screen edit from a search or from the exit of the Song edit dialog. If
|
||||
# remote editing is active Trigger it and clean up so it will not update again. Push edits to the service
|
||||
# api editing is active Trigger it and clean up so it will not update again. Push edits to the service
|
||||
# manager to update items
|
||||
if self.edit_item and self.update_service_on_edit and not self.remote_triggered:
|
||||
item = self.build_service_item(self.edit_item)
|
||||
@ -555,7 +555,7 @@ class SongMediaItem(MediaManagerItem):
|
||||
:param service_item: The service item to be built on
|
||||
:param item: The Song item to be used
|
||||
:param xml_version: The xml version (not used)
|
||||
:param remote: Triggered from remote
|
||||
:param remote: Triggered from api
|
||||
:param context: Why is it being generated
|
||||
"""
|
||||
log.debug('generate_slide_data: {service}, {item}, {remote}'.format(service=service_item, item=item,
|
||||
|
@ -171,7 +171,7 @@ def get_repo_name():
|
||||
# Determine the branch's name
|
||||
repo_name = ''
|
||||
for line in output_list:
|
||||
# Check if it is remote branch.
|
||||
# Check if it is api branch.
|
||||
if 'push branch' in line:
|
||||
match = re.match(REPO_REGEX, line)
|
||||
if match:
|
||||
|
@ -149,24 +149,3 @@ class TestRegistry(TestCase):
|
||||
|
||||
def dummy_function_2(self):
|
||||
return "function_2"
|
||||
|
||||
def test_registry_remote_apis(self):
|
||||
"""
|
||||
Test the registry working flags creation and its usage
|
||||
"""
|
||||
# GIVEN: A new registry
|
||||
Registry.create()
|
||||
|
||||
# WHEN: I register a API to function
|
||||
func = MagicMock()
|
||||
func1 = MagicMock()
|
||||
Registry().remote_api(r'^/api/poll$', func)
|
||||
Registry().remote_api(r'^/main/poll$', func1, True)
|
||||
|
||||
# THEN: When I look to a function
|
||||
call_structure, args = Registry().remote_execute('/api/poll')
|
||||
self.assertEqual(call_structure['function'], func, 'Calling function should match')
|
||||
self.assertEqual(call_structure['secure'], False, 'Calling function should not require security')
|
||||
call_structure, args = Registry().remote_execute('/main/poll')
|
||||
self.assertEqual(call_structure['function'], func1, 'Calling function should match')
|
||||
self.assertEqual(call_structure['secure'], True, 'Calling function should not require security')
|
||||
|
@ -28,7 +28,7 @@ from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Settings, Registry
|
||||
from openlp.core.ui import ServiceManager
|
||||
from openlp.core.lib.remote import OpenLPPoll
|
||||
from openlp.core.lib.api import OpenLPPoll
|
||||
from openlp.plugins.remotes.lib.httpserver import HttpRouter
|
||||
from tests.functional import MagicMock, patch, mock_open
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
@ -327,7 +327,7 @@ class TestRouter(TestCase, TestMixin):
|
||||
|
||||
def remote_next_test(self):
|
||||
"""
|
||||
Test service manager receives remote next click properly (bug 1407445)
|
||||
Test service manager receives api next click properly (bug 1407445)
|
||||
"""
|
||||
# GIVEN: initial setup and mocks
|
||||
self.router.routes = [(r'^/api/service/(.*)$', {'function': self.router.service, 'secure': False})]
|
||||
@ -348,7 +348,7 @@ class TestRouter(TestCase, TestMixin):
|
||||
|
||||
def remote_previous_test(self):
|
||||
"""
|
||||
Test service manager receives remote previous click properly (bug 1407445)
|
||||
Test service manager receives api previous click properly (bug 1407445)
|
||||
"""
|
||||
# GIVEN: initial setup and mocks
|
||||
self.router.routes = [(r'^/api/service/(.*)$', {'function': self.router.service, 'secure': False})]
|
||||
|
@ -502,7 +502,7 @@ class TestMediaItem(TestCase, TestMixin):
|
||||
|
||||
def build_remote_search_test(self):
|
||||
"""
|
||||
Test results for the remote search api
|
||||
Test results for the api search api
|
||||
"""
|
||||
# GIVEN: A Song and a search a JSON array should be returned.
|
||||
mock_song = MagicMock()
|
||||
|
Loading…
Reference in New Issue
Block a user