forked from openlp/openlp
head
This commit is contained in:
commit
48d2956bdd
@ -93,14 +93,14 @@ class GeneralTab(SettingsTab):
|
|||||||
self.monitor_layout.addWidget(self.custom_width_label, 3, 3)
|
self.monitor_layout.addWidget(self.custom_width_label, 3, 3)
|
||||||
self.custom_width_value_edit = QtGui.QSpinBox(self.monitor_group_box)
|
self.custom_width_value_edit = QtGui.QSpinBox(self.monitor_group_box)
|
||||||
self.custom_width_value_edit.setObjectName(u'custom_width_value_edit')
|
self.custom_width_value_edit.setObjectName(u'custom_width_value_edit')
|
||||||
self.custom_width_value_edit.setMaximum(9999)
|
self.custom_width_value_edit.setRange(1, 9999)
|
||||||
self.monitor_layout.addWidget(self.custom_width_value_edit, 4, 3)
|
self.monitor_layout.addWidget(self.custom_width_value_edit, 4, 3)
|
||||||
self.custom_height_label = QtGui.QLabel(self.monitor_group_box)
|
self.custom_height_label = QtGui.QLabel(self.monitor_group_box)
|
||||||
self.custom_height_label.setObjectName(u'custom_height_label')
|
self.custom_height_label.setObjectName(u'custom_height_label')
|
||||||
self.monitor_layout.addWidget(self.custom_height_label, 3, 4)
|
self.monitor_layout.addWidget(self.custom_height_label, 3, 4)
|
||||||
self.custom_height_value_edit = QtGui.QSpinBox(self.monitor_group_box)
|
self.custom_height_value_edit = QtGui.QSpinBox(self.monitor_group_box)
|
||||||
self.custom_height_value_edit.setObjectName(u'custom_height_value_edit')
|
self.custom_height_value_edit.setObjectName(u'custom_height_value_edit')
|
||||||
self.custom_height_value_edit.setMaximum(9999)
|
self.custom_height_value_edit.setRange(1, 9999)
|
||||||
self.monitor_layout.addWidget(self.custom_height_value_edit, 4, 4)
|
self.monitor_layout.addWidget(self.custom_height_value_edit, 4, 4)
|
||||||
self.display_on_monitor_check = QtGui.QCheckBox(self.monitor_group_box)
|
self.display_on_monitor_check = QtGui.QCheckBox(self.monitor_group_box)
|
||||||
self.display_on_monitor_check.setObjectName(u'monitor_combo_box')
|
self.display_on_monitor_check.setObjectName(u'monitor_combo_box')
|
||||||
|
@ -38,7 +38,7 @@ from openlp.core.lib import UiStrings, Registry, translate
|
|||||||
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
|
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
from openlp.core.lib.ui import critical_error_message_box
|
||||||
from openlp.core.ui import ThemeLayoutForm
|
from openlp.core.ui import ThemeLayoutForm
|
||||||
from openlp.core.utils import get_images_filter
|
from openlp.core.utils import get_images_filter, is_not_image_file
|
||||||
from themewizard import Ui_ThemeWizard
|
from themewizard import Ui_ThemeWizard
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
@ -178,7 +178,7 @@ class ThemeForm(QtGui.QWizard, Ui_ThemeWizard):
|
|||||||
"""
|
"""
|
||||||
background_image = BackgroundType.to_string(BackgroundType.Image)
|
background_image = BackgroundType.to_string(BackgroundType.Image)
|
||||||
if self.page(self.currentId()) == self.backgroundPage and \
|
if self.page(self.currentId()) == self.backgroundPage and \
|
||||||
self.theme.background_type == background_image and not self.imageFileEdit.text():
|
self.theme.background_type == background_image and is_not_image_file(self.imageFileEdit.text()):
|
||||||
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
|
QtGui.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
|
||||||
translate('OpenLP.ThemeWizard', 'You have not selected a '
|
translate('OpenLP.ThemeWizard', 'You have not selected a '
|
||||||
'background image. Please select one before continuing.'))
|
'background image. Please select one before continuing.'))
|
||||||
|
@ -246,6 +246,23 @@ def get_images_filter():
|
|||||||
return IMAGES_FILTER
|
return IMAGES_FILTER
|
||||||
|
|
||||||
|
|
||||||
|
def is_not_image_file(file_name):
|
||||||
|
"""
|
||||||
|
Validate that the file is not an image file.
|
||||||
|
|
||||||
|
``file_name``
|
||||||
|
File name to be checked.
|
||||||
|
"""
|
||||||
|
if not file_name:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
formats = [unicode(fmt).lower() for fmt in QtGui.QImageReader.supportedImageFormats()]
|
||||||
|
file_part, file_extension = os.path.splitext(unicode(file_name))
|
||||||
|
if file_extension[1:].lower() in formats and os.path.exists(file_name):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def split_filename(path):
|
def split_filename(path):
|
||||||
"""
|
"""
|
||||||
Return a list of the parts in a given path.
|
Return a list of the parts in a given path.
|
||||||
|
@ -30,10 +30,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<title>${live_title}</title>
|
<title>${live_title}</title>
|
||||||
<link rel="stylesheet" href="/files/live.css" />
|
<link rel="stylesheet" href="/files/main.css" />
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="/files/images/favicon.ico">
|
||||||
<script type="text/javascript" src="/files/jquery.js"></script>
|
<script type="text/javascript" src="/files/jquery.js"></script>
|
||||||
<script type="text/javascript" src="/files/live.js"></script>
|
<script type="text/javascript" src="/files/main.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<img id="image" class="size"/>
|
<img id="image" class="size"/>
|
@ -26,7 +26,7 @@
|
|||||||
window.OpenLP = {
|
window.OpenLP = {
|
||||||
loadSlide: function (event) {
|
loadSlide: function (event) {
|
||||||
$.getJSON(
|
$.getJSON(
|
||||||
"/live/image",
|
"/main/image",
|
||||||
function (data, status) {
|
function (data, status) {
|
||||||
var img = document.getElementById('image');
|
var img = document.getElementById('image');
|
||||||
img.src = data.results.slide_image;
|
img.src = data.results.slide_image;
|
||||||
@ -36,7 +36,7 @@ window.OpenLP = {
|
|||||||
},
|
},
|
||||||
pollServer: function () {
|
pollServer: function () {
|
||||||
$.getJSON(
|
$.getJSON(
|
||||||
"/live/poll",
|
"/main/poll",
|
||||||
function (data, status) {
|
function (data, status) {
|
||||||
if (OpenLP.slideCount != data.results.slide_count) {
|
if (OpenLP.slideCount != data.results.slide_count) {
|
||||||
OpenLP.slideCount = data.results.slide_count;
|
OpenLP.slideCount = data.results.slide_count;
|
@ -177,11 +177,11 @@ class HttpServer(object):
|
|||||||
self.root = self.Public()
|
self.root = self.Public()
|
||||||
self.root.files = self.Files()
|
self.root.files = self.Files()
|
||||||
self.root.stage = self.Stage()
|
self.root.stage = self.Stage()
|
||||||
self.root.live = self.Live()
|
self.root.main = self.Main()
|
||||||
self.root.router = self.router
|
self.root.router = self.router
|
||||||
self.root.files.router = self.router
|
self.root.files.router = self.router
|
||||||
self.root.stage.router = self.router
|
self.root.stage.router = self.router
|
||||||
self.root.live.router = self.router
|
self.root.main.router = self.router
|
||||||
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
cherrypy.tree.mount(self.root, '/', config=self.define_config())
|
||||||
# Turn off the flood of access messages cause by poll
|
# Turn off the flood of access messages cause by poll
|
||||||
cherrypy.log.access_log.propagate = False
|
cherrypy.log.access_log.propagate = False
|
||||||
@ -218,7 +218,7 @@ class HttpServer(object):
|
|||||||
u'/stage': {u'tools.staticdir.on': True,
|
u'/stage': {u'tools.staticdir.on': True,
|
||||||
u'tools.staticdir.dir': self.router.html_dir,
|
u'tools.staticdir.dir': self.router.html_dir,
|
||||||
u'tools.basic_auth.on': False},
|
u'tools.basic_auth.on': False},
|
||||||
u'/live': {u'tools.staticdir.on': True,
|
u'/main': {u'tools.staticdir.on': True,
|
||||||
u'tools.staticdir.dir': self.router.html_dir,
|
u'tools.staticdir.dir': self.router.html_dir,
|
||||||
u'tools.basic_auth.on': False}}
|
u'tools.basic_auth.on': False}}
|
||||||
return directory_config
|
return directory_config
|
||||||
@ -253,9 +253,9 @@ class HttpServer(object):
|
|||||||
url = urlparse.urlparse(cherrypy.url())
|
url = urlparse.urlparse(cherrypy.url())
|
||||||
return self.router.process_http_request(url.path, *args)
|
return self.router.process_http_request(url.path, *args)
|
||||||
|
|
||||||
class Live(object):
|
class Main(object):
|
||||||
"""
|
"""
|
||||||
Live view is read only so security is not relevant and would reduce it's usability
|
Main view is read only so security is not relevant and would reduce it's usability
|
||||||
"""
|
"""
|
||||||
@cherrypy.expose
|
@cherrypy.expose
|
||||||
def default(self, *args, **kwargs):
|
def default(self, *args, **kwargs):
|
||||||
@ -281,12 +281,12 @@ class HttpRouter(object):
|
|||||||
self.routes = [
|
self.routes = [
|
||||||
(u'^/$', self.serve_file),
|
(u'^/$', self.serve_file),
|
||||||
(u'^/(stage)$', self.serve_file),
|
(u'^/(stage)$', self.serve_file),
|
||||||
(u'^/(live)$', self.serve_file),
|
(u'^/(main)$', self.serve_file),
|
||||||
(r'^/files/(.*)$', self.serve_file),
|
(r'^/files/(.*)$', self.serve_file),
|
||||||
(r'^/api/poll$', self.poll),
|
(r'^/api/poll$', self.poll),
|
||||||
(r'^/stage/poll$', self.poll),
|
(r'^/stage/poll$', self.poll),
|
||||||
(r'^/live/poll$', self.live_poll),
|
(r'^/main/poll$', self.main_poll),
|
||||||
(r'^/live/image$', self.live_image),
|
(r'^/main/image$', self.main_image),
|
||||||
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
(r'^/api/controller/(live|preview)/(.*)$', self.controller),
|
||||||
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
|
(r'^/stage/controller/(live|preview)/(.*)$', self.controller),
|
||||||
(r'^/api/service/(.*)$', self.service),
|
(r'^/api/service/(.*)$', self.service),
|
||||||
@ -378,7 +378,7 @@ class HttpRouter(object):
|
|||||||
'slides': translate('RemotePlugin.Mobile', 'Slides')
|
'slides': translate('RemotePlugin.Mobile', 'Slides')
|
||||||
}
|
}
|
||||||
|
|
||||||
def serve_file(self, filename=None):
|
def serve_file(self, file_name=None):
|
||||||
"""
|
"""
|
||||||
Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
|
Send a file to the socket. For now, just a subset of file types and must be top level inside the html folder.
|
||||||
If subfolders requested return 404, easier for security for the present.
|
If subfolders requested return 404, easier for security for the present.
|
||||||
@ -386,17 +386,17 @@ class HttpRouter(object):
|
|||||||
Ultimately for i18n, this could first look for xx/file.html before falling back to file.html.
|
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'
|
where xx is the language, e.g. 'en'
|
||||||
"""
|
"""
|
||||||
log.debug(u'serve file request %s' % filename)
|
log.debug(u'serve file request %s' % file_name)
|
||||||
if not filename:
|
if not file_name:
|
||||||
filename = u'index.html'
|
file_name = u'index.html'
|
||||||
elif filename == u'stage':
|
elif file_name == u'stage':
|
||||||
filename = u'stage.html'
|
file_name = u'stage.html'
|
||||||
elif filename == u'live':
|
elif file_name == u'main':
|
||||||
filename = u'live.html'
|
file_name = u'main.html'
|
||||||
path = os.path.normpath(os.path.join(self.html_dir, filename))
|
path = os.path.normpath(os.path.join(self.html_dir, file_name))
|
||||||
if not path.startswith(self.html_dir):
|
if not path.startswith(self.html_dir):
|
||||||
return self._http_not_found()
|
return self._http_not_found()
|
||||||
ext = os.path.splitext(filename)[1]
|
ext = os.path.splitext(file_name)[1]
|
||||||
html = None
|
html = None
|
||||||
if ext == u'.html':
|
if ext == u'.html':
|
||||||
mimetype = u'text/html'
|
mimetype = u'text/html'
|
||||||
@ -447,7 +447,7 @@ class HttpRouter(object):
|
|||||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||||
return json.dumps({u'results': result}).encode()
|
return json.dumps({u'results': result}).encode()
|
||||||
|
|
||||||
def live_poll(self):
|
def main_poll(self):
|
||||||
"""
|
"""
|
||||||
Poll OpenLP to determine the current slide count.
|
Poll OpenLP to determine the current slide count.
|
||||||
"""
|
"""
|
||||||
@ -457,7 +457,7 @@ class HttpRouter(object):
|
|||||||
cherrypy.response.headers['Content-Type'] = u'application/json'
|
cherrypy.response.headers['Content-Type'] = u'application/json'
|
||||||
return json.dumps({u'results': result}).encode()
|
return json.dumps({u'results': result}).encode()
|
||||||
|
|
||||||
def live_image(self):
|
def main_image(self):
|
||||||
"""
|
"""
|
||||||
Return the latest display image as a byte stream.
|
Return the latest display image as a byte stream.
|
||||||
"""
|
"""
|
||||||
|
@ -83,7 +83,6 @@ MODULES = [
|
|||||||
'bs4',
|
'bs4',
|
||||||
'mako',
|
'mako',
|
||||||
'cherrypy',
|
'cherrypy',
|
||||||
'migrate',
|
|
||||||
'uno',
|
'uno',
|
||||||
'icu',
|
'icu',
|
||||||
'bs4',
|
'bs4',
|
||||||
|
0
tests/interfaces/openlp_core_utils/__init__.py
Normal file
0
tests/interfaces/openlp_core_utils/__init__.py
Normal file
52
tests/interfaces/openlp_core_utils/test_utils.py
Normal file
52
tests/interfaces/openlp_core_utils/test_utils.py
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
"""
|
||||||
|
Functional tests to test the AppLocation class and related methods.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from openlp.core.utils import is_not_image_file
|
||||||
|
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||||
|
|
||||||
|
|
||||||
|
class TestUtils(TestCase):
|
||||||
|
"""
|
||||||
|
A test suite to test out various methods around the Utils functions.
|
||||||
|
"""
|
||||||
|
def is_not_image_empty_test(self):
|
||||||
|
"""
|
||||||
|
Test the method handles an empty string
|
||||||
|
"""
|
||||||
|
# Given and empty string
|
||||||
|
file_name = ""
|
||||||
|
|
||||||
|
# WHEN testing for it
|
||||||
|
result = is_not_image_file(file_name)
|
||||||
|
|
||||||
|
# THEN the result is false
|
||||||
|
assert result is True, u'The missing file test should return True'
|
||||||
|
|
||||||
|
def is_not_image_with_image_file_test(self):
|
||||||
|
"""
|
||||||
|
Test the method handles an image file
|
||||||
|
"""
|
||||||
|
# Given and empty string
|
||||||
|
file_name = os.path.join(TEST_RESOURCES_PATH, u'church.jpg')
|
||||||
|
|
||||||
|
# WHEN testing for it
|
||||||
|
result = is_not_image_file(file_name)
|
||||||
|
|
||||||
|
# THEN the result is false
|
||||||
|
assert result is False, u'The file is present so the test should return False'
|
||||||
|
|
||||||
|
def is_not_image_with_none_image_file_test(self):
|
||||||
|
"""
|
||||||
|
Test the method handles a non image file
|
||||||
|
"""
|
||||||
|
# Given and empty string
|
||||||
|
file_name = os.path.join(TEST_RESOURCES_PATH, u'serviceitem_custom_1.osj')
|
||||||
|
|
||||||
|
# WHEN testing for it
|
||||||
|
result = is_not_image_file(file_name)
|
||||||
|
|
||||||
|
# THEN the result is false
|
||||||
|
assert result is True, u'The file is not an image file so the test should return True'
|
Loading…
Reference in New Issue
Block a user