forked from openlp/openlp
merge w/ trunk
This commit is contained in:
commit
a95894fba4
@ -190,31 +190,32 @@ def verify_ip_address(addr):
|
|||||||
return True if verify_ipv4(addr) else verify_ipv6(addr)
|
return True if verify_ipv4(addr) else verify_ipv6(addr)
|
||||||
|
|
||||||
|
|
||||||
def md5_hash(salt, data):
|
def md5_hash(salt, data=None):
|
||||||
"""
|
"""
|
||||||
Returns the hashed output of md5sum on salt,data
|
Returns the hashed output of md5sum on salt,data
|
||||||
using Python3 hashlib
|
using Python3 hashlib
|
||||||
|
|
||||||
:param salt: Initial salt
|
:param salt: Initial salt
|
||||||
:param data: Data to hash
|
:param data: OPTIONAL Data to hash
|
||||||
:returns: str
|
:returns: str
|
||||||
"""
|
"""
|
||||||
log.debug('md5_hash(salt="%s")' % salt)
|
log.debug('md5_hash(salt="%s")' % salt)
|
||||||
hash_obj = hashlib.new('md5')
|
hash_obj = hashlib.new('md5')
|
||||||
hash_obj.update(salt.encode('ascii'))
|
hash_obj.update(salt)
|
||||||
hash_obj.update(data.encode('ascii'))
|
if data:
|
||||||
|
hash_obj.update(data)
|
||||||
hash_value = hash_obj.hexdigest()
|
hash_value = hash_obj.hexdigest()
|
||||||
log.debug('md5_hash() returning "%s"' % hash_value)
|
log.debug('md5_hash() returning "%s"' % hash_value)
|
||||||
return hash_value
|
return hash_value
|
||||||
|
|
||||||
|
|
||||||
def qmd5_hash(salt, data):
|
def qmd5_hash(salt, data=None):
|
||||||
"""
|
"""
|
||||||
Returns the hashed output of MD5Sum on salt, data
|
Returns the hashed output of MD5Sum on salt, data
|
||||||
using PyQt4.QCryptographicHash.
|
using PyQt4.QCryptographicHash.
|
||||||
|
|
||||||
:param salt: Initial salt
|
:param salt: Initial salt
|
||||||
:param data: Data to hash
|
:param data: OPTIONAL Data to hash
|
||||||
:returns: str
|
:returns: str
|
||||||
"""
|
"""
|
||||||
log.debug('qmd5_hash(salt="%s"' % salt)
|
log.debug('qmd5_hash(salt="%s"' % salt)
|
||||||
@ -223,7 +224,7 @@ def qmd5_hash(salt, data):
|
|||||||
hash_obj.addData(data)
|
hash_obj.addData(data)
|
||||||
hash_value = hash_obj.result().toHex()
|
hash_value = hash_obj.result().toHex()
|
||||||
log.debug('qmd5_hash() returning "%s"' % hash_value)
|
log.debug('qmd5_hash() returning "%s"' % hash_value)
|
||||||
return decode(hash_value.data(), 'ascii')
|
return hash_value.data()
|
||||||
|
|
||||||
|
|
||||||
def clean_button_text(button_text):
|
def clean_button_text(button_text):
|
||||||
|
@ -213,13 +213,6 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties):
|
|||||||
icon=':/general/general_edit.png',
|
icon=':/general/general_edit.png',
|
||||||
triggers=self.on_edit_click)
|
triggers=self.on_edit_click)
|
||||||
create_widget_action(self.list_view, separator=True)
|
create_widget_action(self.list_view, separator=True)
|
||||||
if self.has_delete_icon:
|
|
||||||
create_widget_action(self.list_view,
|
|
||||||
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
|
|
||||||
text=self.plugin.get_string(StringContent.Delete)['title'],
|
|
||||||
icon=':/general/general_delete.png',
|
|
||||||
can_shortcuts=True, triggers=self.on_delete_click)
|
|
||||||
create_widget_action(self.list_view, separator=True)
|
|
||||||
create_widget_action(self.list_view,
|
create_widget_action(self.list_view,
|
||||||
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
|
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Preview.title()),
|
||||||
text=self.plugin.get_string(StringContent.Preview)['title'],
|
text=self.plugin.get_string(StringContent.Preview)['title'],
|
||||||
@ -238,6 +231,13 @@ class MediaManagerItem(QtGui.QWidget, RegistryProperties):
|
|||||||
text=self.plugin.get_string(StringContent.Service)['title'],
|
text=self.plugin.get_string(StringContent.Service)['title'],
|
||||||
icon=':/general/general_add.png',
|
icon=':/general/general_add.png',
|
||||||
triggers=self.on_add_click)
|
triggers=self.on_add_click)
|
||||||
|
if self.has_delete_icon:
|
||||||
|
create_widget_action(self.list_view, separator=True)
|
||||||
|
create_widget_action(self.list_view,
|
||||||
|
'listView%s%sItem' % (self.plugin.name.title(), StringContent.Delete.title()),
|
||||||
|
text=self.plugin.get_string(StringContent.Delete)['title'],
|
||||||
|
icon=':/general/general_delete.png',
|
||||||
|
can_shortcuts=True, triggers=self.on_delete_click)
|
||||||
if self.add_to_service_item:
|
if self.add_to_service_item:
|
||||||
create_widget_action(self.list_view, separator=True)
|
create_widget_action(self.list_view, separator=True)
|
||||||
create_widget_action(self.list_view,
|
create_widget_action(self.list_view,
|
||||||
|
@ -343,7 +343,7 @@ class PJLink1(QTcpSocket):
|
|||||||
# Authenticated login with salt
|
# Authenticated login with salt
|
||||||
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
|
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
|
||||||
log.debug('(%s) pin="%s"' % (self.ip, self.pin))
|
log.debug('(%s) pin="%s"' % (self.ip, self.pin))
|
||||||
salt = qmd5_hash(salt=data_check[2], data=self.pin)
|
salt = qmd5_hash(salt=data_check[2].endcode('ascii'), data=self.pin.encode('ascii'))
|
||||||
else:
|
else:
|
||||||
salt = None
|
salt = None
|
||||||
# We're connected at this point, so go ahead and do regular I/O
|
# We're connected at this point, so go ahead and do regular I/O
|
||||||
|
@ -268,9 +268,11 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.web = 'http://openlp.org/files/frw/'
|
self.web = 'http://openlp.org/files/frw/'
|
||||||
self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
|
self.cancel_button.clicked.connect(self.on_cancel_button_clicked)
|
||||||
self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked)
|
self.no_internet_finish_button.clicked.connect(self.on_no_internet_finish_button_clicked)
|
||||||
|
self.no_internet_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked)
|
||||||
self.currentIdChanged.connect(self.on_current_id_changed)
|
self.currentIdChanged.connect(self.on_current_id_changed)
|
||||||
Registry().register_function('config_screen_changed', self.update_screen_list_combo)
|
Registry().register_function('config_screen_changed', self.update_screen_list_combo)
|
||||||
self.no_internet_finish_button.setVisible(False)
|
self.no_internet_finish_button.setVisible(False)
|
||||||
|
self.no_internet_cancel_button.setVisible(False)
|
||||||
# Check if this is a re-run of the wizard.
|
# Check if this is a re-run of the wizard.
|
||||||
self.has_run_wizard = Settings().value('core/has run wizard')
|
self.has_run_wizard = Settings().value('core/has run wizard')
|
||||||
check_directory_exists(os.path.join(gettempdir(), 'openlp'))
|
check_directory_exists(os.path.join(gettempdir(), 'openlp'))
|
||||||
@ -327,6 +329,10 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
self.next_button.setVisible(False)
|
self.next_button.setVisible(False)
|
||||||
self.cancel_button.setVisible(False)
|
self.cancel_button.setVisible(False)
|
||||||
self.no_internet_finish_button.setVisible(True)
|
self.no_internet_finish_button.setVisible(True)
|
||||||
|
if self.has_run_wizard:
|
||||||
|
self.no_internet_cancel_button.setVisible(False)
|
||||||
|
else:
|
||||||
|
self.no_internet_cancel_button.setVisible(True)
|
||||||
elif page_id == FirstTimePage.Plugins:
|
elif page_id == FirstTimePage.Plugins:
|
||||||
self.back_button.setVisible(False)
|
self.back_button.setVisible(False)
|
||||||
elif page_id == FirstTimePage.Progress:
|
elif page_id == FirstTimePage.Progress:
|
||||||
@ -372,6 +378,13 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
|
|||||||
Settings().setValue('core/has run wizard', True)
|
Settings().setValue('core/has run wizard', True)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
|
def on_no_internet_cancel_button_clicked(self):
|
||||||
|
"""
|
||||||
|
Process the triggering of the "Cancel" button on the No Internet page.
|
||||||
|
"""
|
||||||
|
self.was_cancelled = True
|
||||||
|
self.close()
|
||||||
|
|
||||||
def url_get_file(self, url, f_path):
|
def url_get_file(self, url, f_path):
|
||||||
""""
|
""""
|
||||||
Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
|
Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
|
||||||
|
@ -59,7 +59,8 @@ class UiFirstTimeWizard(object):
|
|||||||
first_time_wizard.resize(550, 386)
|
first_time_wizard.resize(550, 386)
|
||||||
first_time_wizard.setModal(True)
|
first_time_wizard.setModal(True)
|
||||||
first_time_wizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage |
|
first_time_wizard.setOptions(QtGui.QWizard.IndependentPages | QtGui.QWizard.NoBackButtonOnStartPage |
|
||||||
QtGui.QWizard.NoBackButtonOnLastPage | QtGui.QWizard.HaveCustomButton1)
|
QtGui.QWizard.NoBackButtonOnLastPage | QtGui.QWizard.HaveCustomButton1 |
|
||||||
|
QtGui.QWizard.HaveCustomButton2)
|
||||||
if is_macosx():
|
if is_macosx():
|
||||||
first_time_wizard.setPixmap(QtGui.QWizard.BackgroundPixmap,
|
first_time_wizard.setPixmap(QtGui.QWizard.BackgroundPixmap,
|
||||||
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
|
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
|
||||||
@ -69,6 +70,7 @@ class UiFirstTimeWizard(object):
|
|||||||
self.finish_button = self.button(QtGui.QWizard.FinishButton)
|
self.finish_button = self.button(QtGui.QWizard.FinishButton)
|
||||||
self.no_internet_finish_button = self.button(QtGui.QWizard.CustomButton1)
|
self.no_internet_finish_button = self.button(QtGui.QWizard.CustomButton1)
|
||||||
self.cancel_button = self.button(QtGui.QWizard.CancelButton)
|
self.cancel_button = self.button(QtGui.QWizard.CancelButton)
|
||||||
|
self.no_internet_cancel_button = self.button(QtGui.QWizard.CustomButton2)
|
||||||
self.next_button = self.button(QtGui.QWizard.NextButton)
|
self.next_button = self.button(QtGui.QWizard.NextButton)
|
||||||
self.back_button = self.button(QtGui.QWizard.BackButton)
|
self.back_button = self.button(QtGui.QWizard.BackButton)
|
||||||
add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
|
add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
|
||||||
@ -271,3 +273,4 @@ class UiFirstTimeWizard(object):
|
|||||||
'and OpenLP is configured.'))
|
'and OpenLP is configured.'))
|
||||||
self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
|
self.progress_label.setText(translate('OpenLP.FirstTimeWizard', 'Starting configuration process...'))
|
||||||
first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish'))
|
first_time_wizard.setButtonText(QtGui.QWizard.CustomButton1, translate('OpenLP.FirstTimeWizard', 'Finish'))
|
||||||
|
first_time_wizard.setButtonText(QtGui.QWizard.CustomButton2, translate('OpenLP.FirstTimeWizard', 'Cancel'))
|
||||||
|
@ -33,7 +33,12 @@ import cgi
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
|
from PyQt4 import QtCore, QtGui, QtWebKit, QtOpenGL
|
||||||
from PyQt4.phonon import Phonon
|
|
||||||
|
PHONON_AVAILABLE = True
|
||||||
|
try:
|
||||||
|
from PyQt4.phonon import Phonon
|
||||||
|
except ImportError:
|
||||||
|
PHONON_AVAILABLE = False
|
||||||
|
|
||||||
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx
|
from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, Settings, translate, is_macosx
|
||||||
from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
|
from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, expand_tags, image_to_byte
|
||||||
@ -139,7 +144,7 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
self.override = {}
|
self.override = {}
|
||||||
self.retranslateUi()
|
self.retranslateUi()
|
||||||
self.media_object = None
|
self.media_object = None
|
||||||
if self.is_live:
|
if self.is_live and PHONON_AVAILABLE:
|
||||||
self.audio_player = AudioPlayer(self)
|
self.audio_player = AudioPlayer(self)
|
||||||
else:
|
else:
|
||||||
self.audio_player = None
|
self.audio_player = None
|
||||||
|
@ -22,11 +22,11 @@
|
|||||||
"""
|
"""
|
||||||
The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player
|
The :mod:`~openlp.core.ui.media.webkit` module contains our WebKit video player
|
||||||
"""
|
"""
|
||||||
from PyQt4 import QtGui
|
from PyQt4 import QtGui, QtWebKit
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from openlp.core.common import Settings, is_macosx
|
from openlp.core.common import Settings
|
||||||
from openlp.core.lib import translate
|
from openlp.core.lib import translate
|
||||||
from openlp.core.ui.media import MediaState
|
from openlp.core.ui.media import MediaState
|
||||||
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
from openlp.core.ui.media.mediaplayer import MediaPlayer
|
||||||
@ -222,13 +222,15 @@ class WebkitPlayer(MediaPlayer):
|
|||||||
|
|
||||||
def check_available(self):
|
def check_available(self):
|
||||||
"""
|
"""
|
||||||
Check the availability of the media player
|
Check the availability of the media player.
|
||||||
|
|
||||||
|
:return: boolean. True if available
|
||||||
"""
|
"""
|
||||||
# At the moment we don't have support for webkitplayer on Mac OS X
|
web = QtWebKit.QWebPage()
|
||||||
if is_macosx():
|
# This script should return '[object HTMLVideoElement]' if the html5 video is available in webkit. Otherwise it
|
||||||
return False
|
# should return '[object HTMLUnknownElement]'
|
||||||
else:
|
return web.mainFrame().evaluateJavaScript(
|
||||||
return True
|
"Object.prototype.toString.call(document.createElement('video'));") == '[object HTMLVideoElement]'
|
||||||
|
|
||||||
def load(self, display):
|
def load(self, display):
|
||||||
"""
|
"""
|
||||||
|
@ -332,8 +332,7 @@ class SourceSelectTabs(QDialog):
|
|||||||
msg = QtGui.QMessageBox()
|
msg = QtGui.QMessageBox()
|
||||||
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
||||||
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
||||||
'Are you sure you want to delete ALL user-defined '),
|
'Are you sure you want to delete ALL user-defined '
|
||||||
translate('OpenLP.SourceSelectForm',
|
|
||||||
'source input text for this projector?'))
|
'source input text for this projector?'))
|
||||||
msg.setStandardButtons(msg.Cancel | msg.Ok)
|
msg.setStandardButtons(msg.Cancel | msg.Ok)
|
||||||
msg.setDefaultButton(msg.Cancel)
|
msg.setDefaultButton(msg.Cancel)
|
||||||
@ -471,8 +470,7 @@ class SourceSelectSingle(QDialog):
|
|||||||
msg = QtGui.QMessageBox()
|
msg = QtGui.QMessageBox()
|
||||||
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
msg.setText(translate('OpenLP.SourceSelectForm', 'Delete entries for this projector'))
|
||||||
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
msg.setInformativeText(translate('OpenLP.SourceSelectForm',
|
||||||
'Are you sure you want to delete ALL user-defined '),
|
'Are you sure you want to delete ALL user-defined '
|
||||||
translate('OpenLP.SourceSelectForm',
|
|
||||||
'source input text for this projector?'))
|
'source input text for this projector?'))
|
||||||
msg.setStandardButtons(msg.Cancel | msg.Ok)
|
msg.setStandardButtons(msg.Cancel | msg.Ok)
|
||||||
msg.setDefaultButton(msg.Cancel)
|
msg.setDefaultButton(msg.Cancel)
|
||||||
|
@ -580,6 +580,7 @@ class SlideController(DisplayController, RegistryProperties):
|
|||||||
self.display.setup()
|
self.display.setup()
|
||||||
if self.is_live:
|
if self.is_live:
|
||||||
self.__add_actions_to_widget(self.display)
|
self.__add_actions_to_widget(self.display)
|
||||||
|
if self.display.audio_player:
|
||||||
self.display.audio_player.connectSlot(QtCore.SIGNAL('tick(qint64)'), self.on_audio_time_remaining)
|
self.display.audio_player.connectSlot(QtCore.SIGNAL('tick(qint64)'), self.on_audio_time_remaining)
|
||||||
# The SlidePreview's ratio.
|
# The SlidePreview's ratio.
|
||||||
try:
|
try:
|
||||||
@ -834,26 +835,28 @@ class SlideController(DisplayController, RegistryProperties):
|
|||||||
self.slide_list = {}
|
self.slide_list = {}
|
||||||
if self.is_live:
|
if self.is_live:
|
||||||
self.song_menu.menu().clear()
|
self.song_menu.menu().clear()
|
||||||
self.display.audio_player.reset()
|
if self.display.audio_player:
|
||||||
self.set_audio_items_visibility(False)
|
self.display.audio_player.reset()
|
||||||
self.audio_pause_item.setChecked(False)
|
self.set_audio_items_visibility(False)
|
||||||
# If the current item has background audio
|
self.audio_pause_item.setChecked(False)
|
||||||
if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
# If the current item has background audio
|
||||||
self.log_debug('Starting to play...')
|
if self.service_item.is_capable(ItemCapabilities.HasBackgroundAudio):
|
||||||
self.display.audio_player.add_to_playlist(self.service_item.background_audio)
|
self.log_debug('Starting to play...')
|
||||||
self.track_menu.clear()
|
self.display.audio_player.add_to_playlist(self.service_item.background_audio)
|
||||||
for counter in range(len(self.service_item.background_audio)):
|
self.track_menu.clear()
|
||||||
action = self.track_menu.addAction(os.path.basename(self.service_item.background_audio[counter]))
|
for counter in range(len(self.service_item.background_audio)):
|
||||||
action.setData(counter)
|
action = self.track_menu.addAction(
|
||||||
action.triggered.connect(self.on_track_triggered)
|
os.path.basename(self.service_item.background_audio[counter]))
|
||||||
self.display.audio_player.repeat = \
|
action.setData(counter)
|
||||||
Settings().value(self.main_window.general_settings_section + '/audio repeat list')
|
action.triggered.connect(self.on_track_triggered)
|
||||||
if Settings().value(self.main_window.general_settings_section + '/audio start paused'):
|
self.display.audio_player.repeat = \
|
||||||
self.audio_pause_item.setChecked(True)
|
Settings().value(self.main_window.general_settings_section + '/audio repeat list')
|
||||||
self.display.audio_player.pause()
|
if Settings().value(self.main_window.general_settings_section + '/audio start paused'):
|
||||||
else:
|
self.audio_pause_item.setChecked(True)
|
||||||
self.display.audio_player.play()
|
self.display.audio_player.pause()
|
||||||
self.set_audio_items_visibility(True)
|
else:
|
||||||
|
self.display.audio_player.play()
|
||||||
|
self.set_audio_items_visibility(True)
|
||||||
row = 0
|
row = 0
|
||||||
width = self.main_window.control_splitter.sizes()[self.split]
|
width = self.main_window.control_splitter.sizes()[self.split]
|
||||||
for frame_number, frame in enumerate(self.service_item.get_frames()):
|
for frame_number, frame in enumerate(self.service_item.get_frames()):
|
||||||
|
@ -178,7 +178,7 @@ def update_reference_separators():
|
|||||||
default_separators = [
|
default_separators = [
|
||||||
'|'.join([
|
'|'.join([
|
||||||
translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'),
|
translate('BiblesPlugin', ':', 'Verse identifier e.g. Genesis 1 : 1 = Genesis Chapter 1 Verse 1'),
|
||||||
translate('BiblesPlugin', 'v','Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'),
|
translate('BiblesPlugin', 'v', 'Verse identifier e.g. Genesis 1 v 1 = Genesis Chapter 1 Verse 1'),
|
||||||
translate('BiblesPlugin', 'V', 'Verse identifier e.g. Genesis 1 V 1 = Genesis Chapter 1 Verse 1'),
|
translate('BiblesPlugin', 'V', 'Verse identifier e.g. Genesis 1 V 1 = Genesis Chapter 1 Verse 1'),
|
||||||
translate('BiblesPlugin', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'),
|
translate('BiblesPlugin', 'verse', 'Verse identifier e.g. Genesis 1 verse 1 = Genesis Chapter 1 Verse 1'),
|
||||||
translate('BiblesPlugin', 'verses',
|
translate('BiblesPlugin', 'verses',
|
||||||
@ -371,7 +371,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
|||||||
from_chapter = from_verse
|
from_chapter = from_verse
|
||||||
from_verse = None
|
from_verse = None
|
||||||
if to_chapter:
|
if to_chapter:
|
||||||
if to_chapter < from_chapter:
|
if from_chapter and to_chapter < from_chapter:
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
chapter = to_chapter
|
chapter = to_chapter
|
||||||
@ -387,7 +387,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
|
|||||||
from_verse = 1
|
from_verse = 1
|
||||||
if not to_verse:
|
if not to_verse:
|
||||||
to_verse = -1
|
to_verse = -1
|
||||||
if to_chapter > from_chapter:
|
if to_chapter and to_chapter > from_chapter:
|
||||||
ref_list.append((book_ref_id, from_chapter, from_verse, -1))
|
ref_list.append((book_ref_id, from_chapter, from_verse, -1))
|
||||||
for i in range(from_chapter + 1, to_chapter):
|
for i in range(from_chapter + 1, to_chapter):
|
||||||
ref_list.append((book_ref_id, i, 1, -1))
|
ref_list.append((book_ref_id, i, 1, -1))
|
||||||
|
@ -314,6 +314,16 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
return True
|
return True
|
||||||
return return_value
|
return return_value
|
||||||
|
|
||||||
|
def generate_thumbnail_path(self, image):
|
||||||
|
"""
|
||||||
|
Generate a path to the thumbnail
|
||||||
|
|
||||||
|
:param image: An instance of ImageFileNames
|
||||||
|
:return: A path to the thumbnail of type str
|
||||||
|
"""
|
||||||
|
ext = os.path.splitext(image.filename)[1].lower()
|
||||||
|
return os.path.join(self.service_path, '{}{}'.format(str(image.id), ext))
|
||||||
|
|
||||||
def load_full_list(self, images, initial_load=False, open_group=None):
|
def load_full_list(self, images, initial_load=False, open_group=None):
|
||||||
"""
|
"""
|
||||||
Replace the list of images and groups in the interface.
|
Replace the list of images and groups in the interface.
|
||||||
@ -335,27 +345,26 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
# Sort the images by its filename considering language specific.
|
# Sort the images by its filename considering language specific.
|
||||||
# characters.
|
# characters.
|
||||||
images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1]))
|
images.sort(key=lambda image_object: get_locale_key(os.path.split(str(image_object.filename))[1]))
|
||||||
for imageFile in images:
|
for image_file in images:
|
||||||
log.debug('Loading image: %s', imageFile.filename)
|
log.debug('Loading image: %s', image_file.filename)
|
||||||
filename = os.path.split(imageFile.filename)[1]
|
filename = os.path.split(image_file.filename)[1]
|
||||||
ext = os.path.splitext(imageFile.filename)[1].lower()
|
thumb = self.generate_thumbnail_path(image_file)
|
||||||
thumb = os.path.join(self.service_path, "%s%s" % (str(imageFile.id), ext))
|
if not os.path.exists(image_file.filename):
|
||||||
if not os.path.exists(imageFile.filename):
|
|
||||||
icon = build_icon(':/general/general_delete.png')
|
icon = build_icon(':/general/general_delete.png')
|
||||||
else:
|
else:
|
||||||
if validate_thumb(imageFile.filename, thumb):
|
if validate_thumb(image_file.filename, thumb):
|
||||||
icon = build_icon(thumb)
|
icon = build_icon(thumb)
|
||||||
else:
|
else:
|
||||||
icon = create_thumb(imageFile.filename, thumb)
|
icon = create_thumb(image_file.filename, thumb)
|
||||||
item_name = QtGui.QTreeWidgetItem([filename])
|
item_name = QtGui.QTreeWidgetItem([filename])
|
||||||
item_name.setText(0, filename)
|
item_name.setText(0, filename)
|
||||||
item_name.setIcon(0, icon)
|
item_name.setIcon(0, icon)
|
||||||
item_name.setToolTip(0, imageFile.filename)
|
item_name.setToolTip(0, image_file.filename)
|
||||||
item_name.setData(0, QtCore.Qt.UserRole, imageFile)
|
item_name.setData(0, QtCore.Qt.UserRole, image_file)
|
||||||
if imageFile.group_id == 0:
|
if image_file.group_id == 0:
|
||||||
self.list_view.addTopLevelItem(item_name)
|
self.list_view.addTopLevelItem(item_name)
|
||||||
else:
|
else:
|
||||||
group_items[imageFile.group_id].addChild(item_name)
|
group_items[image_file.group_id].addChild(item_name)
|
||||||
if not initial_load:
|
if not initial_load:
|
||||||
self.main_window.increment_progress_bar()
|
self.main_window.increment_progress_bar()
|
||||||
if not initial_load:
|
if not initial_load:
|
||||||
@ -549,24 +558,24 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
# force a nonexistent theme
|
# force a nonexistent theme
|
||||||
service_item.theme = -1
|
service_item.theme = -1
|
||||||
missing_items_file_names = []
|
missing_items_file_names = []
|
||||||
images_file_names = []
|
images = []
|
||||||
# Expand groups to images
|
# Expand groups to images
|
||||||
for bitem in items:
|
for bitem in items:
|
||||||
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
|
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
|
||||||
for index in range(0, bitem.childCount()):
|
for index in range(0, bitem.childCount()):
|
||||||
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
|
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
images_file_names.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename)
|
images.append(bitem.child(index).data(0, QtCore.Qt.UserRole))
|
||||||
elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
|
||||||
images_file_names.append(bitem.data(0, QtCore.Qt.UserRole).filename)
|
images.append(bitem.data(0, QtCore.Qt.UserRole))
|
||||||
# Don't try to display empty groups
|
# Don't try to display empty groups
|
||||||
if not images_file_names:
|
if not images:
|
||||||
return False
|
return False
|
||||||
# Find missing files
|
# Find missing files
|
||||||
for filename in images_file_names:
|
for image in images:
|
||||||
if not os.path.exists(filename):
|
if not os.path.exists(image.filename):
|
||||||
missing_items_file_names.append(filename)
|
missing_items_file_names.append(image.filename)
|
||||||
# We cannot continue, as all images do not exist.
|
# We cannot continue, as all images do not exist.
|
||||||
if not images_file_names:
|
if not images:
|
||||||
if not remote:
|
if not remote:
|
||||||
critical_error_message_box(
|
critical_error_message_box(
|
||||||
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
|
||||||
@ -582,9 +591,10 @@ class ImageMediaItem(MediaManagerItem):
|
|||||||
QtGui.QMessageBox.No:
|
QtGui.QMessageBox.No:
|
||||||
return False
|
return False
|
||||||
# Continue with the existing images.
|
# Continue with the existing images.
|
||||||
for filename in images_file_names:
|
for image in images:
|
||||||
name = os.path.split(filename)[1]
|
name = os.path.split(image.filename)[1]
|
||||||
service_item.add_from_image(filename, name, background, os.path.join(self.service_path, name))
|
thumbnail = self.generate_thumbnail_path(image)
|
||||||
|
service_item.add_from_image(image.filename, name, background, thumbnail)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_group_exists(self, new_group):
|
def check_group_exists(self, new_group):
|
||||||
|
@ -225,10 +225,10 @@ class PresentationMediaItem(MediaManagerItem):
|
|||||||
self.clean_up_thumbnails(filepath)
|
self.clean_up_thumbnails(filepath)
|
||||||
self.main_window.increment_progress_bar()
|
self.main_window.increment_progress_bar()
|
||||||
self.main_window.finished_progress_bar()
|
self.main_window.finished_progress_bar()
|
||||||
self.application.set_busy_cursor()
|
|
||||||
for row in row_list:
|
for row in row_list:
|
||||||
self.list_view.takeItem(row)
|
self.list_view.takeItem(row)
|
||||||
Settings().setValue(self.settings_section + '/presentations files', self.get_file_list())
|
Settings().setValue(self.settings_section + '/presentations files', self.get_file_list())
|
||||||
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
def clean_up_thumbnails(self, filepath):
|
def clean_up_thumbnails(self, filepath):
|
||||||
"""
|
"""
|
||||||
|
@ -134,7 +134,7 @@ class PresentationDocument(object):
|
|||||||
"""
|
"""
|
||||||
# TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
# TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
||||||
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
||||||
folder = md5_hash('', self.file_path)
|
folder = md5_hash(self.file_path)
|
||||||
else:
|
else:
|
||||||
folder = self.get_file_name()
|
folder = self.get_file_name()
|
||||||
return os.path.join(self.controller.thumbnail_folder, folder)
|
return os.path.join(self.controller.thumbnail_folder, folder)
|
||||||
@ -145,7 +145,7 @@ class PresentationDocument(object):
|
|||||||
"""
|
"""
|
||||||
# TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
# TODO: If statment can be removed when the upgrade path from 2.0.x to 2.2.x is no longer needed
|
||||||
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
if Settings().value('presentations/thumbnail_scheme') == 'md5':
|
||||||
folder = md5_hash('', self.file_path)
|
folder = md5_hash(self.file_path)
|
||||||
else:
|
else:
|
||||||
folder = folder = self.get_file_name()
|
folder = folder = self.get_file_name()
|
||||||
return os.path.join(self.controller.temp_folder, folder)
|
return os.path.join(self.controller.temp_folder, folder)
|
||||||
|
@ -143,7 +143,10 @@ class PresentationPlugin(Plugin):
|
|||||||
super().app_startup()
|
super().app_startup()
|
||||||
files_from_config = Settings().value('presentations/presentations files')
|
files_from_config = Settings().value('presentations/presentations files')
|
||||||
for file in files_from_config:
|
for file in files_from_config:
|
||||||
self.media_item.clean_up_thumbnails(file)
|
try:
|
||||||
|
self.media_item.clean_up_thumbnails(file)
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
self.media_item.list_view.clear()
|
self.media_item.list_view.clear()
|
||||||
Settings().setValue('presentations/thumbnail_scheme', 'md5')
|
Settings().setValue('presentations/thumbnail_scheme', 'md5')
|
||||||
self.media_item.validate_and_load(files_from_config)
|
self.media_item.validate_and_load(files_from_config)
|
||||||
|
@ -25,7 +25,9 @@ Presentationmanager song files into the current database.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from lxml import objectify
|
import re
|
||||||
|
import chardet
|
||||||
|
from lxml import objectify, etree
|
||||||
|
|
||||||
from openlp.core.ui.wizard import WizardStrings
|
from openlp.core.ui.wizard import WizardStrings
|
||||||
from .songimport import SongImport
|
from .songimport import SongImport
|
||||||
@ -42,7 +44,18 @@ class PresentationManagerImport(SongImport):
|
|||||||
if self.stop_import_flag:
|
if self.stop_import_flag:
|
||||||
return
|
return
|
||||||
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
self.import_wizard.increment_progress_bar(WizardStrings.ImportingType % os.path.basename(file_path))
|
||||||
root = objectify.parse(open(file_path, 'rb')).getroot()
|
try:
|
||||||
|
tree = etree.parse(file_path, parser=etree.XMLParser(recover=True))
|
||||||
|
except etree.XMLSyntaxError:
|
||||||
|
# Try to detect encoding and use it
|
||||||
|
file = open(file_path, mode='rb')
|
||||||
|
encoding = chardet.detect(file.read())['encoding']
|
||||||
|
file.close()
|
||||||
|
# Open file with detected encoding and remove encoding declaration
|
||||||
|
text = open(file_path, mode='r', encoding=encoding).read()
|
||||||
|
text = re.sub('.+\?>\n', '', text)
|
||||||
|
tree = etree.fromstring(text, parser=etree.XMLParser(recover=True))
|
||||||
|
root = objectify.fromstring(etree.tostring(tree))
|
||||||
self.process_song(root)
|
self.process_song(root)
|
||||||
|
|
||||||
def process_song(self, root):
|
def process_song(self, root):
|
||||||
|
@ -86,6 +86,7 @@
|
|||||||
<file>openlp-logo-64x64.png</file>
|
<file>openlp-logo-64x64.png</file>
|
||||||
<file>openlp-logo-128x128.png</file>
|
<file>openlp-logo-128x128.png</file>
|
||||||
<file>openlp-logo-256x256.png</file>
|
<file>openlp-logo-256x256.png</file>
|
||||||
|
<file>openlp-logo.svg</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="graphics">
|
<qresource prefix="graphics">
|
||||||
<file>exception.png</file>
|
<file>exception.png</file>
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
Package to test the openlp.core.ui.projector.networkutils package.
|
Package to test the openlp.core.ui.projector.networkutils package.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
|
from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
|
||||||
@ -30,6 +32,8 @@ from openlp.core.common import verify_ip_address, md5_hash, qmd5_hash
|
|||||||
salt = '498e4a67'
|
salt = '498e4a67'
|
||||||
pin = 'JBMIAProjectorLink'
|
pin = 'JBMIAProjectorLink'
|
||||||
test_hash = '5d8409bc1c3fa39749434aa3a5c38682'
|
test_hash = '5d8409bc1c3fa39749434aa3a5c38682'
|
||||||
|
test_non_ascii_string = '이것은 한국어 시험 문자열'
|
||||||
|
test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201'
|
||||||
|
|
||||||
ip4_loopback = '127.0.0.1'
|
ip4_loopback = '127.0.0.1'
|
||||||
ip4_local = '192.168.1.1'
|
ip4_local = '192.168.1.1'
|
||||||
@ -120,7 +124,7 @@ class testProjectorUtilities(TestCase):
|
|||||||
Test MD5 hash from salt+data pass (python)
|
Test MD5 hash from salt+data pass (python)
|
||||||
"""
|
"""
|
||||||
# WHEN: Given a known salt+data
|
# WHEN: Given a known salt+data
|
||||||
hash_ = md5_hash(salt=salt, data=pin)
|
hash_ = md5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))
|
||||||
|
|
||||||
# THEN: Validate return has is same
|
# THEN: Validate return has is same
|
||||||
self.assertEquals(hash_, test_hash, 'MD5 should have returned a good hash')
|
self.assertEquals(hash_, test_hash, 'MD5 should have returned a good hash')
|
||||||
@ -130,7 +134,7 @@ class testProjectorUtilities(TestCase):
|
|||||||
Test MD5 hash from salt+data fail (python)
|
Test MD5 hash from salt+data fail (python)
|
||||||
"""
|
"""
|
||||||
# WHEN: Given a different salt+hash
|
# WHEN: Given a different salt+hash
|
||||||
hash_ = md5_hash(salt=pin, data=salt)
|
hash_ = md5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))
|
||||||
|
|
||||||
# THEN: return data is different
|
# THEN: return data is different
|
||||||
self.assertNotEquals(hash_, test_hash, 'MD5 should have returned a bad hash')
|
self.assertNotEquals(hash_, test_hash, 'MD5 should have returned a bad hash')
|
||||||
@ -140,17 +144,37 @@ class testProjectorUtilities(TestCase):
|
|||||||
Test MD5 hash from salt+data pass (Qt)
|
Test MD5 hash from salt+data pass (Qt)
|
||||||
"""
|
"""
|
||||||
# WHEN: Given a known salt+data
|
# WHEN: Given a known salt+data
|
||||||
hash_ = qmd5_hash(salt=salt, data=pin)
|
hash_ = qmd5_hash(salt=salt.encode('ascii'), data=pin.encode('ascii'))
|
||||||
|
|
||||||
# THEN: Validate return has is same
|
# THEN: Validate return has is same
|
||||||
self.assertEquals(hash_, test_hash, 'Qt-MD5 should have returned a good hash')
|
self.assertEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a good hash')
|
||||||
|
|
||||||
def test_qmd5_hash_bad(self):
|
def test_qmd5_hash_bad(self):
|
||||||
"""
|
"""
|
||||||
Test MD5 hash from salt+hash fail (Qt)
|
Test MD5 hash from salt+hash fail (Qt)
|
||||||
"""
|
"""
|
||||||
# WHEN: Given a different salt+hash
|
# WHEN: Given a different salt+hash
|
||||||
hash_ = qmd5_hash(salt=pin, data=salt)
|
hash_ = qmd5_hash(salt=pin.encode('ascii'), data=salt.encode('ascii'))
|
||||||
|
|
||||||
# THEN: return data is different
|
# THEN: return data is different
|
||||||
self.assertNotEquals(hash_, test_hash, 'Qt-MD5 should have returned a bad hash')
|
self.assertNotEquals(hash_.decode('ascii'), test_hash, 'Qt-MD5 should have returned a bad hash')
|
||||||
|
|
||||||
|
def test_md5_non_ascii_string(self):
|
||||||
|
"""
|
||||||
|
Test MD5 hash with non-ascii string - bug 1417809
|
||||||
|
"""
|
||||||
|
# WHEN: Non-ascii string is hashed
|
||||||
|
hash_ = md5_hash(salt=test_non_ascii_string.encode('utf-8'), data=None)
|
||||||
|
|
||||||
|
# THEN: Valid MD5 hash should be returned
|
||||||
|
self.assertEqual(hash_, test_non_ascii_hash, 'MD5 should have returned a valid hash')
|
||||||
|
|
||||||
|
def test_qmd5_non_ascii_string(self):
|
||||||
|
"""
|
||||||
|
Test MD5 hash with non-ascii string - bug 1417809
|
||||||
|
"""
|
||||||
|
# WHEN: Non-ascii string is hashed
|
||||||
|
hash_ = md5_hash(salt=test_non_ascii_string.encode('utf-8'), data=None)
|
||||||
|
|
||||||
|
# THEN: Valid MD5 hash should be returned
|
||||||
|
self.assertEqual(hash_, test_non_ascii_hash, 'Qt-MD5 should have returned a valid hash')
|
||||||
|
@ -128,3 +128,21 @@ class TestMainWindow(TestCase, TestMixin):
|
|||||||
# THEN the main window's title should be set to the
|
# THEN the main window's title should be set to the
|
||||||
self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'),
|
self.assertEqual(self.main_window.windowTitle(), '%s - %s' % (UiStrings().OLPV2x, 'test.osz'),
|
||||||
'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - test.osz"')
|
'The main window\'s title should be set to "<the contents of UiStrings().OLPV2x> - test.osz"')
|
||||||
|
|
||||||
|
def mainwindow_configuration_test(self):
|
||||||
|
"""
|
||||||
|
Check that the Main Window initialises the Registry Correctly
|
||||||
|
"""
|
||||||
|
# GIVEN: A built main window
|
||||||
|
|
||||||
|
# WHEN: you check the started functions
|
||||||
|
|
||||||
|
# THEN: the following registry functions should have been registered
|
||||||
|
self.assertEqual(len(self.registry.service_list), 6, 'The registry should have 6 services.')
|
||||||
|
self.assertEqual(len(self.registry.functions_list), 16, 'The registry should have 16 functions')
|
||||||
|
self.assertTrue('application' in self.registry.service_list, 'The application should have been registered.')
|
||||||
|
self.assertTrue('main_window' in self.registry.service_list, 'The main_window should have been registered.')
|
||||||
|
self.assertTrue('media_controller' in self.registry.service_list, 'The media_controller should have been '
|
||||||
|
'registered.')
|
||||||
|
self.assertTrue('plugin_manager' in self.registry.service_list,
|
||||||
|
'The plugin_manager should have been registered.')
|
||||||
|
@ -479,6 +479,34 @@ class TestSlideController(TestCase):
|
|||||||
mocked_preview_widget.current_slide_number.assert_called_with()
|
mocked_preview_widget.current_slide_number.assert_called_with()
|
||||||
mocked_process_item.assert_called_once_with(mocked_item, 7)
|
mocked_process_item.assert_called_once_with(mocked_item, 7)
|
||||||
|
|
||||||
|
def on_slide_blank_test(self):
|
||||||
|
"""
|
||||||
|
Test on_slide_blank
|
||||||
|
"""
|
||||||
|
# GIVEN: An instance of SlideController and a mocked on_blank_display
|
||||||
|
slide_controller = SlideController(None)
|
||||||
|
slide_controller.on_blank_display = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: Calling on_slide_blank
|
||||||
|
slide_controller.on_slide_blank()
|
||||||
|
|
||||||
|
# THEN: on_blank_display should have been called with True
|
||||||
|
slide_controller.on_blank_display.assert_called_once_with(True)
|
||||||
|
|
||||||
|
def on_slide_unblank_test(self):
|
||||||
|
"""
|
||||||
|
Test on_slide_unblank
|
||||||
|
"""
|
||||||
|
# GIVEN: An instance of SlideController and a mocked on_blank_display
|
||||||
|
slide_controller = SlideController(None)
|
||||||
|
slide_controller.on_blank_display = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: Calling on_slide_unblank
|
||||||
|
slide_controller.on_slide_unblank()
|
||||||
|
|
||||||
|
# THEN: on_blank_display should have been called with False
|
||||||
|
slide_controller.on_blank_display.assert_called_once_with(False)
|
||||||
|
|
||||||
def on_slide_selected_index_no_service_item_test(self):
|
def on_slide_selected_index_no_service_item_test(self):
|
||||||
"""
|
"""
|
||||||
Test that when there is no service item, the on_slide_selected_index() method returns immediately
|
Test that when there is no service item, the on_slide_selected_index() method returns immediately
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
Package to test the openlp.core.ui.media.webkitplayer package.
|
Package to test the openlp.core.ui.media.webkitplayer package.
|
||||||
"""
|
"""
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from tests.functional import patch
|
from tests.functional import MagicMock, patch
|
||||||
|
|
||||||
from openlp.core.ui.media.webkitplayer import WebkitPlayer
|
from openlp.core.ui.media.webkitplayer import WebkitPlayer
|
||||||
|
|
||||||
@ -33,32 +33,36 @@ class TestWebkitPlayer(TestCase):
|
|||||||
Test the functions in the :mod:`webkitplayer` module.
|
Test the functions in the :mod:`webkitplayer` module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def check_available_mac_test(self):
|
def check_available_video_disabled_test(self):
|
||||||
"""
|
"""
|
||||||
Simple test of webkitplayer availability on Mac OS X
|
Test of webkit video unavailability
|
||||||
"""
|
"""
|
||||||
# GIVEN: A WebkitPlayer and a mocked is_macosx
|
# GIVEN: A WebkitPlayer instance and a mocked QWebPage
|
||||||
with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx:
|
mocked_qwebpage = MagicMock()
|
||||||
mocked_is_macosx.return_value = True
|
mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLUnknownElement]'
|
||||||
|
with patch('openlp.core.ui.media.webkitplayer.QtWebKit.QWebPage', **{'return_value': mocked_qwebpage}):
|
||||||
webkit_player = WebkitPlayer(None)
|
webkit_player = WebkitPlayer(None)
|
||||||
|
|
||||||
# WHEN: An checking if the player is available
|
# WHEN: An checking if the player is available
|
||||||
available = webkit_player.check_available()
|
available = webkit_player.check_available()
|
||||||
|
|
||||||
# THEN: The player should not be available on Mac OS X
|
# THEN: The player should not be available when '[object HTMLUnknownElement]' is returned
|
||||||
self.assertEqual(False, available, 'The WebkitPlayer should not be available on Mac OS X.')
|
self.assertEqual(False, available,
|
||||||
|
'The WebkitPlayer should not be available when video feature detection fails')
|
||||||
|
|
||||||
def check_available_non_mac_test(self):
|
def check_available_video_enabled_test(self):
|
||||||
"""
|
"""
|
||||||
Simple test of webkitplayer availability when not on Mac OS X
|
Test of webkit video availability
|
||||||
"""
|
"""
|
||||||
# GIVEN: A WebkitPlayer and a mocked is_macosx
|
# GIVEN: A WebkitPlayer instance and a mocked QWebPage
|
||||||
with patch('openlp.core.ui.media.webkitplayer.is_macosx') as mocked_is_macosx:
|
mocked_qwebpage = MagicMock()
|
||||||
mocked_is_macosx.return_value = False
|
mocked_qwebpage.mainFrame().evaluateJavaScript.return_value = '[object HTMLVideoElement]'
|
||||||
|
with patch('openlp.core.ui.media.webkitplayer.QtWebKit.QWebPage', **{'return_value': mocked_qwebpage}):
|
||||||
webkit_player = WebkitPlayer(None)
|
webkit_player = WebkitPlayer(None)
|
||||||
|
|
||||||
# WHEN: An checking if the player is available
|
# WHEN: An checking if the player is available
|
||||||
available = webkit_player.check_available()
|
available = webkit_player.check_available()
|
||||||
|
|
||||||
# THEN: The player should be available when not on Mac OS X
|
# THEN: The player should be available when '[object HTMLVideoElement]' is returned
|
||||||
self.assertEqual(True, available, 'The WebkitPlayer should be available when not on Mac OS X.')
|
self.assertEqual(True, available,
|
||||||
|
'The WebkitPlayer should be available when video feature detection passes')
|
||||||
|
@ -25,7 +25,7 @@ Functional tests to test the AppLocation class and related methods.
|
|||||||
import os
|
import os
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
from openlp.core.utils import clean_filename, get_filesystem_encoding, get_locale_key, \
|
from openlp.core.utils import clean_filename, delete_file, get_filesystem_encoding, get_locale_key, \
|
||||||
get_natural_key, split_filename, _get_user_agent, get_web_page, get_uno_instance, add_actions
|
get_natural_key, split_filename, _get_user_agent, get_web_page, get_uno_instance, add_actions
|
||||||
from tests.functional import MagicMock, patch
|
from tests.functional import MagicMock, patch
|
||||||
|
|
||||||
@ -184,6 +184,59 @@ class TestUtils(TestCase):
|
|||||||
# THEN: The file name should be cleaned.
|
# THEN: The file name should be cleaned.
|
||||||
self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.')
|
self.assertEqual(wanted_name, result, 'The file name should not contain any special characters.')
|
||||||
|
|
||||||
|
def delete_file_no_path_test(self):
|
||||||
|
""""
|
||||||
|
Test the delete_file function when called with out a valid path
|
||||||
|
"""
|
||||||
|
# GIVEN: A blank path
|
||||||
|
# WEHN: Calling delete_file
|
||||||
|
result = delete_file('')
|
||||||
|
|
||||||
|
# THEN: delete_file should return False
|
||||||
|
self.assertFalse(result, "delete_file should return False when called with ''")
|
||||||
|
|
||||||
|
def delete_file_path_success_test(self):
|
||||||
|
""""
|
||||||
|
Test the delete_file function when it successfully deletes a file
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked os which returns True when os.path.exists is called
|
||||||
|
with patch('openlp.core.utils.os', **{'path.exists.return_value': False}):
|
||||||
|
|
||||||
|
# WHEN: Calling delete_file with a file path
|
||||||
|
result = delete_file('path/file.ext')
|
||||||
|
|
||||||
|
# THEN: delete_file should return True
|
||||||
|
self.assertTrue(result, 'delete_file should return True when it successfully deletes a file')
|
||||||
|
|
||||||
|
def delete_file_path_no_file_exists_test(self):
|
||||||
|
""""
|
||||||
|
Test the delete_file function when the file to remove does not exist
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked os which returns False when os.path.exists is called
|
||||||
|
with patch('openlp.core.utils.os', **{'path.exists.return_value': False}):
|
||||||
|
|
||||||
|
# WHEN: Calling delete_file with a file path
|
||||||
|
result = delete_file('path/file.ext')
|
||||||
|
|
||||||
|
# THEN: delete_file should return True
|
||||||
|
self.assertTrue(result, 'delete_file should return True when the file doesnt exist')
|
||||||
|
|
||||||
|
def delete_file_path_exception_test(self):
|
||||||
|
""""
|
||||||
|
Test the delete_file function when os.remove raises an exception
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked os which returns True when os.path.exists is called and raises an OSError when os.remove is
|
||||||
|
# called.
|
||||||
|
with patch('openlp.core.utils.os', **{'path.exists.return_value': True, 'path.exists.side_effect': OSError}), \
|
||||||
|
patch('openlp.core.utils.log') as mocked_log:
|
||||||
|
|
||||||
|
# WHEN: Calling delete_file with a file path
|
||||||
|
result = delete_file('path/file.ext')
|
||||||
|
|
||||||
|
# THEN: delete_file should log and exception and return False
|
||||||
|
self.assertEqual(mocked_log.exception.call_count, 1)
|
||||||
|
self.assertFalse(result, 'delete_file should return False when os.remove raises an OSError')
|
||||||
|
|
||||||
def get_locale_key_test(self):
|
def get_locale_key_test(self):
|
||||||
"""
|
"""
|
||||||
Test the get_locale_key(string) function
|
Test the get_locale_key(string) function
|
||||||
|
@ -46,3 +46,5 @@ class TestPresentationManagerFileImport(SongImportTestHelper):
|
|||||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
|
||||||
self.file_import([os.path.join(TEST_PATH, 'Agnus Dei.sng')],
|
self.file_import([os.path.join(TEST_PATH, 'Agnus Dei.sng')],
|
||||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Agnus Dei.json')))
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Agnus Dei.json')))
|
||||||
|
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.sng')],
|
||||||
|
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
|
||||||
|
@ -24,6 +24,7 @@ Package to test the openlp.core.__init__ package.
|
|||||||
"""
|
"""
|
||||||
from optparse import Values
|
from optparse import Values
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
from unittest.mock import MagicMock, patch
|
from unittest.mock import MagicMock, patch
|
||||||
@ -118,12 +119,27 @@ class TestInit(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
Test that parse_options parses short options correctly
|
Test that parse_options parses short options correctly
|
||||||
"""
|
"""
|
||||||
# GIVEN: A list of vaild short options
|
# GIVEN: A list of valid short options
|
||||||
options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args']
|
options = ['-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args']
|
||||||
|
|
||||||
# WHEN: Calling parse_options
|
# WHEN: Calling parse_options
|
||||||
resluts = parse_options(options)
|
results = parse_options(options)
|
||||||
|
|
||||||
# THEN: A tuple should be returned with the parsed options and left over args
|
# THEN: A tuple should be returned with the parsed options and left over args
|
||||||
self.assertEqual(resluts, (Values({'no_error_form': True, 'dev_version': True, 'portable': True,
|
self.assertEqual(results, (Values({'no_error_form': True, 'dev_version': True, 'portable': True,
|
||||||
|
'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args']))
|
||||||
|
|
||||||
|
def parse_options_valid_argv_short_options_test(self):
|
||||||
|
"""
|
||||||
|
Test that parse_options parses valid short options correctly when passed through sys.argv
|
||||||
|
"""
|
||||||
|
# GIVEN: A list of valid options
|
||||||
|
options = ['openlp.py', '-e', '-l', 'debug', '-pd', '-s', 'style', 'extra', 'qt', 'args']
|
||||||
|
|
||||||
|
# WHEN: Passing in the options through sys.argv and calling parse_args with None
|
||||||
|
with patch.object(sys, 'argv', options):
|
||||||
|
results = parse_options(None)
|
||||||
|
|
||||||
|
# THEN: parse_args should return a tuple of valid options and of left over options that OpenLP does not use
|
||||||
|
self.assertEqual(results, (Values({'no_error_form': True, 'dev_version': True, 'portable': True,
|
||||||
'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args']))
|
'style': 'style', 'loglevel': 'debug'}), ['extra', 'qt', 'args']))
|
||||||
|
@ -29,11 +29,18 @@ log = logging.getLogger(__name__)
|
|||||||
log.debug('test_projectorsourceform loaded')
|
log.debug('test_projectorsourceform loaded')
|
||||||
|
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
from PyQt4 import QtGui
|
||||||
|
from PyQt4.QtGui import QDialog
|
||||||
|
|
||||||
|
from tests.functional import patch
|
||||||
|
from tests.functional.openlp_core_lib.test_projectordb import tmpfile
|
||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES
|
from tests.resources.projector.data import TEST_DB, TEST1_DATA
|
||||||
|
|
||||||
from openlp.core.ui.projector.sourceselectform import source_group
|
from openlp.core.common import Registry, Settings
|
||||||
|
from openlp.core.lib.projector.db import ProjectorDB
|
||||||
|
from openlp.core.lib.projector.constants import PJLINK_DEFAULT_CODES, PJLINK_DEFAULT_SOURCES
|
||||||
|
from openlp.core.ui.projector.sourceselectform import source_group, SourceSelectSingle
|
||||||
|
|
||||||
|
|
||||||
def build_source_dict():
|
def build_source_dict():
|
||||||
@ -54,6 +61,37 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
|
|||||||
"""
|
"""
|
||||||
Test class for the Projector Source Select form module
|
Test class for the Projector Source Select form module
|
||||||
"""
|
"""
|
||||||
|
@patch('openlp.core.lib.projector.db.init_url')
|
||||||
|
def setUp(self, mocked_init_url):
|
||||||
|
"""
|
||||||
|
Set up anything necessary for all tests
|
||||||
|
"""
|
||||||
|
mocked_init_url.start()
|
||||||
|
mocked_init_url.return_value = 'sqlite:///{}'.format(tmpfile)
|
||||||
|
self.build_settings()
|
||||||
|
self.setup_application()
|
||||||
|
Registry.create()
|
||||||
|
# Do not try to recreate if we've already been created from a previous test
|
||||||
|
if not hasattr(self, 'projectordb'):
|
||||||
|
self.projectordb = ProjectorDB()
|
||||||
|
# Retrieve/create a database record
|
||||||
|
self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip)
|
||||||
|
if not self.projector:
|
||||||
|
self.projectordb.add_projector(projector=TEST1_DATA)
|
||||||
|
self.projector = self.projectordb.get_projector_by_ip(TEST1_DATA.ip)
|
||||||
|
self.projector.dbid = self.projector.id
|
||||||
|
self.projector.db_item = self.projector
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
"""
|
||||||
|
Close database session.
|
||||||
|
Delete all C++ objects at end so we don't segfault.
|
||||||
|
"""
|
||||||
|
self.projectordb.session.close()
|
||||||
|
del(self.projectordb)
|
||||||
|
del(self.projector)
|
||||||
|
self.destroy_settings()
|
||||||
|
|
||||||
def source_dict_test(self):
|
def source_dict_test(self):
|
||||||
"""
|
"""
|
||||||
Test that source list dict returned from sourceselectform module is a valid dict with proper entries
|
Test that source list dict returned from sourceselectform module is a valid dict with proper entries
|
||||||
@ -70,3 +108,43 @@ class ProjectorSourceFormTest(TestCase, TestMixin):
|
|||||||
# THEN: return dictionary should match test dictionary
|
# THEN: return dictionary should match test dictionary
|
||||||
self.assertEquals(check, build_source_dict(),
|
self.assertEquals(check, build_source_dict(),
|
||||||
"Source group dictionary should match test dictionary")
|
"Source group dictionary should match test dictionary")
|
||||||
|
|
||||||
|
@patch.object(QDialog, 'exec_')
|
||||||
|
def source_select_edit_button_test(self, mocked_qdialog):
|
||||||
|
"""
|
||||||
|
Test source select form edit has Ok, Cancel, Reset, and Revert buttons
|
||||||
|
"""
|
||||||
|
# GIVEN: Initial setup and mocks
|
||||||
|
self.projector.source_available = ['11', ]
|
||||||
|
self.projector.source = '11'
|
||||||
|
|
||||||
|
# WHEN we create a source select widget and set edit=True
|
||||||
|
select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb)
|
||||||
|
select_form.edit = True
|
||||||
|
select_form.exec_(projector=self.projector)
|
||||||
|
projector = select_form.projector
|
||||||
|
|
||||||
|
# THEN: Verify all 4 buttons are available
|
||||||
|
self.assertEquals(len(select_form.button_box.buttons()), 4,
|
||||||
|
'SourceSelect dialog box should have "OK", "Cancel" '
|
||||||
|
'"Rest", and "Revert" buttons available')
|
||||||
|
|
||||||
|
@patch.object(QDialog, 'exec_')
|
||||||
|
def source_select_noedit_button_test(self, mocked_qdialog):
|
||||||
|
"""
|
||||||
|
Test source select form view has OK and Cancel buttons only
|
||||||
|
"""
|
||||||
|
# GIVEN: Initial setup and mocks
|
||||||
|
self.projector.source_available = ['11', ]
|
||||||
|
self.projector.source = '11'
|
||||||
|
|
||||||
|
# WHEN we create a source select widget and set edit=False
|
||||||
|
select_form = SourceSelectSingle(parent=None, projectordb=self.projectordb)
|
||||||
|
select_form.edit = False
|
||||||
|
select_form.exec_(projector=self.projector)
|
||||||
|
projector = select_form.projector
|
||||||
|
|
||||||
|
# THEN: Verify only 2 buttons are available
|
||||||
|
self.assertEquals(len(select_form.button_box.buttons()), 2,
|
||||||
|
'SourceSelect dialog box should only have "OK" '
|
||||||
|
'and "Cancel" buttons available')
|
||||||
|
@ -109,3 +109,13 @@ class TestBibleManager(TestCase, TestMixin):
|
|||||||
results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())
|
results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())
|
||||||
# THEN a verse array should be returned
|
# THEN a verse array should be returned
|
||||||
self.assertEqual(False, results, "The bible Search should return False")
|
self.assertEqual(False, results, "The bible Search should return False")
|
||||||
|
|
||||||
|
def parse_reference_five_test(self):
|
||||||
|
"""
|
||||||
|
Test the parse_reference method with 1 Timothy 1:3-end
|
||||||
|
"""
|
||||||
|
# GIVEN given a bible in the bible manager
|
||||||
|
# WHEN asking to parse the bible reference
|
||||||
|
results = parse_reference('1 Timothy 1:3-end', self.manager.db_cache['tests'], MagicMock(), 54)
|
||||||
|
# THEN a verse array should be returned
|
||||||
|
self.assertEqual([(54, 1, 3, -1)], results, "The bible verses should matches the expected results")
|
||||||
|
29
tests/resources/presentationmanagersongs/Amazing Grace.json
Normal file
29
tests/resources/presentationmanagersongs/Amazing Grace.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"title": "Amazing Grace",
|
||||||
|
"authors": [
|
||||||
|
"John Newton"
|
||||||
|
],
|
||||||
|
"verse_order_list": ["v1", "v2", "v3", "v4", "v5"],
|
||||||
|
"verses": [
|
||||||
|
[
|
||||||
|
"Amazing grace! How sweet the sound!\nThat saved a wretch like me!\nI once was lost, but now am found;\nWas blind, but now I see.",
|
||||||
|
"v1"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"'Twas grace that taught my heart to fear,\nAnd grace my fears relieved.\nHow precious did that grace appear,\nThe hour I first believed.",
|
||||||
|
"v2"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"The Lord has promised good to me,\nHis Word my hope secures.\nHe will my shield and portion be\nAs long as life endures.",
|
||||||
|
"v3"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"Thro' many dangers, toils and snares\nI have already come.\n'Tis grace that brought me safe thus far,\nAnd grace will lead me home.",
|
||||||
|
"v4"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"When we've been there ten thousand years,\nBright shining as the sun,\nWe've no less days to sing God's praise,\nThan when we first begun.",
|
||||||
|
"v5"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
}
|
BIN
tests/resources/presentationmanagersongs/Amazing Grace.sng
Normal file
BIN
tests/resources/presentationmanagersongs/Amazing Grace.sng
Normal file
Binary file not shown.
@ -23,9 +23,12 @@
|
|||||||
The :mod:`tests.resources.projector.data file contains test data
|
The :mod:`tests.resources.projector.data file contains test data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import os
|
||||||
from openlp.core.lib.projector.db import Projector
|
from openlp.core.lib.projector.db import Projector
|
||||||
|
|
||||||
# Test data
|
# Test data
|
||||||
|
TEST_DB = os.path.join('tmp', 'openlp-test-projectordb.sql')
|
||||||
|
|
||||||
TEST1_DATA = Projector(ip='111.111.111.111',
|
TEST1_DATA = Projector(ip='111.111.111.111',
|
||||||
port='1111',
|
port='1111',
|
||||||
pin='1111',
|
pin='1111',
|
||||||
|
Loading…
Reference in New Issue
Block a user