Add icons to see the state of the remote (on / SSL / authentication)

Stop restarting the server as this crashes it and should not be common.

bzr-revno: 2370
This commit is contained in:
Tim Bentley 2014-04-26 09:38:44 +01:00
commit 9e8f0d82de
11 changed files with 110 additions and 44 deletions

View File

@ -30,7 +30,6 @@
The actual plugin view form The actual plugin view form
""" """
import logging import logging
import os
from PyQt4 import QtGui from PyQt4 import QtGui

View File

@ -149,11 +149,11 @@ class HttpRouter(RegistryProperties):
""" """
Initialise the router stack and any other variables. Initialise the router stack and any other variables.
""" """
authcode = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password')) auth_code = "%s:%s" % (Settings().value('remotes/user id'), Settings().value('remotes/password'))
try: try:
self.auth = base64.b64encode(authcode) self.auth = base64.b64encode(auth_code)
except TypeError: except TypeError:
self.auth = base64.b64encode(authcode.encode()).decode() self.auth = base64.b64encode(auth_code.encode()).decode()
self.routes = [ self.routes = [
('^/$', {'function': self.serve_file, 'secure': False}), ('^/$', {'function': self.serve_file, 'secure': False}),
('^/(stage)$', {'function': self.serve_file, 'secure': False}), ('^/(stage)$', {'function': self.serve_file, 'secure': False}),
@ -376,7 +376,6 @@ class HttpRouter(RegistryProperties):
Examines the extension of the file and determines what the content_type should be, defaults to text/plain Examines the extension of the file and determines what the content_type should be, defaults to text/plain
Returns the extension and the content_type Returns the extension and the content_type
""" """
content_type = 'text/plain'
ext = os.path.splitext(file_name)[1] ext = os.path.splitext(file_name)[1]
content_type = FILE_TYPES.get(ext, 'text/plain') content_type = FILE_TYPES.get(ext, 'text/plain')
return ext, content_type return ext, content_type
@ -439,7 +438,7 @@ class HttpRouter(RegistryProperties):
if plugin.status == PluginStatus.Active: if plugin.status == PluginStatus.Active:
try: try:
text = json.loads(self.request_data)['request']['text'] text = json.loads(self.request_data)['request']['text']
except KeyError as ValueError: except KeyError:
return self.do_http_error() return self.do_http_error()
text = urllib.parse.unquote(text) text = urllib.parse.unquote(text)
self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text]) self.alerts_manager.emit(QtCore.SIGNAL('alerts_text'), [text])
@ -453,6 +452,7 @@ class HttpRouter(RegistryProperties):
""" """
Perform an action on the slide controller. Perform an action on the slide controller.
""" """
log.debug("controller_text var = %s" % var)
current_item = self.live_controller.service_item current_item = self.live_controller.service_item
data = [] data = []
if current_item: if current_item:
@ -488,7 +488,7 @@ class HttpRouter(RegistryProperties):
if self.request_data: if self.request_data:
try: try:
data = json.loads(self.request_data)['request']['id'] data = json.loads(self.request_data)['request']['id']
except KeyError as ValueError: except KeyError:
return self.do_http_error() return self.do_http_error()
log.info(data) log.info(data)
# This slot expects an int within a list. # This slot expects an int within a list.
@ -547,7 +547,7 @@ class HttpRouter(RegistryProperties):
""" """
try: try:
text = json.loads(self.request_data)['request']['text'] text = json.loads(self.request_data)['request']['text']
except KeyError as ValueError: except KeyError:
return self.do_http_error() return self.do_http_error()
text = urllib.parse.unquote(text) text = urllib.parse.unquote(text)
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
@ -563,12 +563,12 @@ class HttpRouter(RegistryProperties):
Go live on an item of type ``plugin``. Go live on an item of type ``plugin``.
""" """
try: try:
id = json.loads(self.request_data)['request']['id'] request_id = json.loads(self.request_data)['request']['id']
except KeyError as ValueError: except KeyError:
return self.do_http_error() return self.do_http_error()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [id, True]) plugin.media_item.emit(QtCore.SIGNAL('%s_go_live' % plugin_name), [request_id, True])
return self.do_http_success() return self.do_http_success()
def add_to_service(self, plugin_name): def add_to_service(self, plugin_name):
@ -576,11 +576,11 @@ class HttpRouter(RegistryProperties):
Add item of type ``plugin_name`` to the end of the service. Add item of type ``plugin_name`` to the end of the service.
""" """
try: try:
id = json.loads(self.request_data)['request']['id'] request_id = json.loads(self.request_data)['request']['id']
except KeyError as ValueError: except KeyError:
return self.do_http_error() return self.do_http_error()
plugin = self.plugin_manager.get_plugin_by_name(plugin_name) plugin = self.plugin_manager.get_plugin_by_name(plugin_name)
if plugin.status == PluginStatus.Active and plugin.media_item: if plugin.status == PluginStatus.Active and plugin.media_item:
item_id = plugin.media_item.create_item_from_id(id) item_id = plugin.media_item.create_item_from_id(request_id)
plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True]) plugin.media_item.emit(QtCore.SIGNAL('%s_add_to_service' % plugin_name), [item_id, True])
self.do_http_success() self.do_http_success()

View File

@ -40,7 +40,7 @@ import time
from PyQt4 import QtCore from PyQt4 import QtCore
from openlp.core.common import AppLocation, Settings from openlp.core.common import AppLocation, Settings, RegistryProperties
from openlp.plugins.remotes.lib import HttpRouter from openlp.plugins.remotes.lib import HttpRouter
@ -94,13 +94,18 @@ class HttpThread(QtCore.QThread):
""" """
self.http_server.start_server() self.http_server.start_server()
def stop(self):
log.debug("stop called")
self.http_server.stop = True
class OpenLPServer():
class OpenLPServer(RegistryProperties):
def __init__(self): def __init__(self):
""" """
Initialise the http server, and start the server of the correct type http / https Initialise the http server, and start the server of the correct type http / https
""" """
log.debug('Initialise httpserver') super(OpenLPServer, self).__init__()
log.debug('Initialise OpenLP')
self.settings_section = 'remotes' self.settings_section = 'remotes'
self.http_thread = HttpThread(self) self.http_thread = HttpThread(self)
self.http_thread.start() self.http_thread.start()
@ -110,32 +115,49 @@ class OpenLPServer():
Start the correct server and save the handler Start the correct server and save the handler
""" """
address = Settings().value(self.settings_section + '/ip address') address = Settings().value(self.settings_section + '/ip address')
if Settings().value(self.settings_section + '/https enabled'): self.address = address
self.is_secure = Settings().value(self.settings_section + '/https enabled')
self.needs_authentication = Settings().value(self.settings_section + '/authentication enabled')
if self.is_secure:
port = Settings().value(self.settings_section + '/https port') port = Settings().value(self.settings_section + '/https port')
self.httpd = HTTPSServer((address, port), CustomHandler) self.port = port
log.debug('Started ssl httpd...') self.start_server_instance(address, port, HTTPSServer)
else: else:
port = Settings().value(self.settings_section + '/port') port = Settings().value(self.settings_section + '/port')
loop = 1 self.port = port
while loop < 3: self.start_server_instance(address, port, ThreadingHTTPServer)
try:
self.httpd = ThreadingHTTPServer((address, port), CustomHandler)
except OSError:
loop += 1
time.sleep(0.1)
except:
log.error('Failed to start server ')
log.debug('Started non ssl httpd...')
if hasattr(self, 'httpd') and self.httpd: if hasattr(self, 'httpd') and self.httpd:
self.httpd.serve_forever() self.httpd.serve_forever()
else: else:
log.debug('Failed to start server') log.debug('Failed to start server')
def start_server_instance(self, address, port, server_class):
"""
Start the server
:param address: The server address
:param port: The run port
:param server_class: the class to start
"""
loop = 1
while loop < 4:
try:
self.httpd = server_class((address, port), CustomHandler)
log.debug("Server started for class %s %s %d" % (server_class, address, port))
except OSError:
log.debug("failed to start http server thread state %d %s" %
(loop, self.http_thread.isRunning()))
loop += 1
time.sleep(0.1)
except:
log.error('Failed to start server ')
def stop_server(self): def stop_server(self):
""" """
Stop the server Stop the server
""" """
self.http_thread.exit(0) if self.http_thread.isRunning():
self.http_thread.stop()
self.httpd = None self.httpd = None
log.debug('Stopped the server.') log.debug('Stopped the server.')

View File

@ -32,7 +32,7 @@ import os.path
from PyQt4 import QtCore, QtGui, QtNetwork from PyQt4 import QtCore, QtGui, QtNetwork
from openlp.core.common import AppLocation, Settings, translate from openlp.core.common import AppLocation, Settings, translate
from openlp.core.lib import SettingsTab from openlp.core.lib import SettingsTab, build_icon
ZERO_URL = '0.0.0.0' ZERO_URL = '0.0.0.0'
@ -234,6 +234,7 @@ class RemoteTab(SettingsTab):
""" """
Load the configuration and update the server configuration if necessary Load the configuration and update the server configuration if necessary
""" """
self.is_secure = Settings().value(self.settings_section + '/https enabled')
self.port_spin_box.setValue(Settings().value(self.settings_section + '/port')) self.port_spin_box.setValue(Settings().value(self.settings_section + '/port'))
self.https_port_spin_box.setValue(Settings().value(self.settings_section + '/https port')) self.https_port_spin_box.setValue(Settings().value(self.settings_section + '/https port'))
self.address_edit.setText(Settings().value(self.settings_section + '/ip address')) self.address_edit.setText(Settings().value(self.settings_section + '/ip address'))
@ -263,9 +264,7 @@ class RemoteTab(SettingsTab):
Settings().value(self.settings_section + '/port') != self.port_spin_box.value() or \ Settings().value(self.settings_section + '/port') != self.port_spin_box.value() or \
Settings().value(self.settings_section + '/https port') != self.https_port_spin_box.value() or \ Settings().value(self.settings_section + '/https port') != self.https_port_spin_box.value() or \
Settings().value(self.settings_section + '/https enabled') != \ Settings().value(self.settings_section + '/https enabled') != \
self.https_settings_group_box.isChecked() or \ self.https_settings_group_box.isChecked():
Settings().value(self.settings_section + '/authentication enabled') != \
self.user_login_group_box.isChecked():
self.settings_form.register_post_process('remotes_config_updated') self.settings_form.register_post_process('remotes_config_updated')
Settings().setValue(self.settings_section + '/port', self.port_spin_box.value()) Settings().setValue(self.settings_section + '/port', self.port_spin_box.value())
Settings().setValue(self.settings_section + '/https port', self.https_port_spin_box.value()) Settings().setValue(self.settings_section + '/https port', self.https_port_spin_box.value())
@ -275,6 +274,7 @@ class RemoteTab(SettingsTab):
Settings().setValue(self.settings_section + '/authentication enabled', self.user_login_group_box.isChecked()) Settings().setValue(self.settings_section + '/authentication enabled', self.user_login_group_box.isChecked())
Settings().setValue(self.settings_section + '/user id', self.user_id.text()) Settings().setValue(self.settings_section + '/user id', self.user_id.text())
Settings().setValue(self.settings_section + '/password', self.password.text()) Settings().setValue(self.settings_section + '/password', self.password.text())
self.generate_icon()
def on_twelve_hour_check_box_changed(self, check_state): def on_twelve_hour_check_box_changed(self, check_state):
""" """
@ -290,3 +290,25 @@ class RemoteTab(SettingsTab):
Invert the HTTP group box based on Https group settings Invert the HTTP group box based on Https group settings
""" """
self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked()) self.http_settings_group_box.setEnabled(not self.https_settings_group_box.isChecked())
def generate_icon(self):
"""
Generate icon for main window
"""
self.remote_server_icon.hide()
icon = QtGui.QImage(':/remote/network_server.png')
icon = icon.scaled(80, 80, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
if self.is_secure:
overlay = QtGui.QImage(':/remote/network_ssl.png')
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(0, 0, overlay)
painter.end()
if Settings().value(self.settings_section + '/authentication enabled'):
overlay = QtGui.QImage(':/remote/network_auth.png')
overlay = overlay.scaled(60, 60, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
painter = QtGui.QPainter(icon)
painter.drawImage(20, 0, overlay)
painter.end()
self.remote_server_icon.setPixmap(QtGui.QPixmap.fromImage(icon))
self.remote_server_icon.show()

View File

@ -28,7 +28,8 @@
############################################################################### ###############################################################################
import logging import logging
import time
from PyQt4 import QtGui
from openlp.core.lib import Plugin, StringContent, translate, build_icon from openlp.core.lib import Plugin, StringContent, translate, build_icon
from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer from openlp.plugins.remotes.lib import RemoteTab, OpenLPServer
@ -67,6 +68,21 @@ class RemotesPlugin(Plugin):
log.debug('initialise') log.debug('initialise')
super(RemotesPlugin, self).initialise() super(RemotesPlugin, self).initialise()
self.server = OpenLPServer() self.server = OpenLPServer()
if not hasattr(self, 'remote_server_icon'):
self.remote_server_icon = QtGui.QLabel(self.main_window.status_bar)
size_policy = QtGui.QSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed)
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
size_policy.setHeightForWidth(self.remote_server_icon.sizePolicy().hasHeightForWidth())
self.remote_server_icon.setSizePolicy(size_policy)
self.remote_server_icon.setFrameShadow(QtGui.QFrame.Plain)
self.remote_server_icon.setLineWidth(1)
self.remote_server_icon.setScaledContents(True)
self.remote_server_icon.setFixedSize(20, 20)
self.remote_server_icon.setObjectName('remote_server_icon')
self.main_window.status_bar.insertPermanentWidget(2, self.remote_server_icon)
self.settings_tab.remote_server_icon = self.remote_server_icon
self.settings_tab.generate_icon()
def finalise(self): def finalise(self):
""" """
@ -104,9 +120,11 @@ class RemotesPlugin(Plugin):
def config_update(self): def config_update(self):
""" """
Called when Config is changed to restart the server on new address or port Called when Config is changed to requests a restart with the server on new address or port
""" """
log.debug('remote config changed') log.debug('remote config changed')
self.finalise() QtGui.QMessageBox.information(self.main_window,
time.sleep(0.5) translate('RemotePlugin', 'Server Config Change'),
self.initialise() translate('RemotePlugin', 'Server configuration changes will require a restart '
'to take effect.'),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))

Binary file not shown.

After

Width:  |  Height:  |  Size: 608 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 B

View File

@ -149,6 +149,11 @@
<file>messagebox_info.png</file> <file>messagebox_info.png</file>
<file>messagebox_warning.png</file> <file>messagebox_warning.png</file>
</qresource> </qresource>
<qresource prefix="remote">
<file>network_server.png</file>
<file>network_ssl.png</file>
<file>network_auth.png</file>
</qresource>
<qresource prefix="songusage"> <qresource prefix="songusage">
<file>song_usage_active.png</file> <file>song_usage_active.png</file>
<file>song_usage_inactive.png</file> <file>song_usage_inactive.png</file>

View File

@ -148,7 +148,7 @@ class JenkinsTrigger(object):
def get_repo_name(): def get_repo_name():
""" """
This returns the name of branch of the wokring directory. For example it returns *lp:~googol/openlp/render*. This returns the name of branch of the working directory. For example it returns *lp:~googol/openlp/render*.
""" """
# Run the bzr command. # Run the bzr command.
bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE) bzr = Popen(('bzr', 'info'), stdout=PIPE, stderr=PIPE)
@ -198,7 +198,7 @@ def main():
jenkins_trigger = JenkinsTrigger(token) jenkins_trigger = JenkinsTrigger(token)
try: try:
jenkins_trigger.trigger_build() jenkins_trigger.trigger_build()
except HTTPError as e: except HTTPError:
print('Wrong token.') print('Wrong token.')
return return
# Open the browser before printing the output. # Open the browser before printing the output.

View File

@ -53,8 +53,8 @@ class TestFileDialog(TestCase):
self.mocked_os.rest() self.mocked_os.rest()
self.mocked_qt_gui.reset() self.mocked_qt_gui.reset()
# GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid # GIVEN: A List of known values as a return value from QFileDialog.getOpenFileNames and a list of valid file
# file names. # names.
self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [ self.mocked_qt_gui.QFileDialog.getOpenFileNames.return_value = [
'/Valid File', '/url%20encoded%20file%20%231', '/non-existing'] '/Valid File', '/url%20encoded%20file%20%231', '/non-existing']
self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [ self.mocked_os.path.exists.side_effect = lambda file_name: file_name in [