diff --git a/openlp/core/common/__init__.py b/openlp/core/common/__init__.py
index 1b16ecab7..0ae62e0a4 100644
--- a/openlp/core/common/__init__.py
+++ b/openlp/core/common/__init__.py
@@ -190,31 +190,32 @@ def verify_ip_address(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
using Python3 hashlib
:param salt: Initial salt
- :param data: Data to hash
+ :param data: OPTIONAL Data to hash
:returns: str
"""
log.debug('md5_hash(salt="%s")' % salt)
hash_obj = hashlib.new('md5')
- hash_obj.update(salt.encode('ascii'))
- hash_obj.update(data.encode('ascii'))
+ hash_obj.update(salt)
+ if data:
+ hash_obj.update(data)
hash_value = hash_obj.hexdigest()
log.debug('md5_hash() returning "%s"' % 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
using PyQt4.QCryptographicHash.
:param salt: Initial salt
- :param data: Data to hash
+ :param data: OPTIONAL Data to hash
:returns: str
"""
log.debug('qmd5_hash(salt="%s"' % salt)
@@ -223,7 +224,7 @@ def qmd5_hash(salt, data):
hash_obj.addData(data)
hash_value = hash_obj.result().toHex()
log.debug('qmd5_hash() returning "%s"' % hash_value)
- return decode(hash_value.data(), 'ascii')
+ return hash_value.data()
def clean_button_text(button_text):
diff --git a/openlp/core/lib/projector/constants.py b/openlp/core/lib/projector/constants.py
index db2b66878..b7f8afe99 100644
--- a/openlp/core/lib/projector/constants.py
+++ b/openlp/core/lib/projector/constants.py
@@ -271,8 +271,8 @@ ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants',
'The proxy address set with setProxy() was not found'),
E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
- 'The connection negotiation with the proxy server because the response '
- 'from the proxy server could not be understood'),
+ 'The connection negotiation with the proxy server failed because the '
+ 'response from the proxy server could not be understood'),
E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
diff --git a/openlp/core/lib/projector/pjlink1.py b/openlp/core/lib/projector/pjlink1.py
index 65a728b35..a82c600e6 100644
--- a/openlp/core/lib/projector/pjlink1.py
+++ b/openlp/core/lib/projector/pjlink1.py
@@ -343,7 +343,7 @@ class PJLink1(QTcpSocket):
# Authenticated login with salt
log.debug('(%s) Setting hash with salt="%s"' % (self.ip, data_check[2]))
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:
salt = None
# We're connected at this point, so go ahead and do regular I/O
diff --git a/openlp/core/ui/firsttimeform.py b/openlp/core/ui/firsttimeform.py
index 3f3403d19..d92866bc4 100644
--- a/openlp/core/ui/firsttimeform.py
+++ b/openlp/core/ui/firsttimeform.py
@@ -268,9 +268,11 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
self.web = 'http://openlp.org/files/frw/'
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_cancel_button.clicked.connect(self.on_no_internet_cancel_button_clicked)
self.currentIdChanged.connect(self.on_current_id_changed)
Registry().register_function('config_screen_changed', self.update_screen_list_combo)
self.no_internet_finish_button.setVisible(False)
+ self.no_internet_cancel_button.setVisible(False)
# Check if this is a re-run of the wizard.
self.has_run_wizard = Settings().value('core/has run wizard')
check_directory_exists(os.path.join(gettempdir(), 'openlp'))
@@ -327,6 +329,10 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
self.next_button.setVisible(False)
self.cancel_button.setVisible(False)
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:
self.back_button.setVisible(False)
elif page_id == FirstTimePage.Progress:
@@ -372,6 +378,13 @@ class FirstTimeForm(QtGui.QWizard, UiFirstTimeWizard, RegistryProperties):
Settings().setValue('core/has run wizard', True)
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):
""""
Download a file given a URL. The file is retrieved in chunks, giving the ability to cancel the download at any
diff --git a/openlp/core/ui/firsttimewizard.py b/openlp/core/ui/firsttimewizard.py
index 63695a09a..041cfc050 100644
--- a/openlp/core/ui/firsttimewizard.py
+++ b/openlp/core/ui/firsttimewizard.py
@@ -59,7 +59,8 @@ class UiFirstTimeWizard(object):
first_time_wizard.resize(550, 386)
first_time_wizard.setModal(True)
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():
first_time_wizard.setPixmap(QtGui.QWizard.BackgroundPixmap,
QtGui.QPixmap(':/wizards/openlp-osx-wizard.png'))
@@ -69,6 +70,7 @@ class UiFirstTimeWizard(object):
self.finish_button = self.button(QtGui.QWizard.FinishButton)
self.no_internet_finish_button = self.button(QtGui.QWizard.CustomButton1)
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.back_button = self.button(QtGui.QWizard.BackButton)
add_welcome_page(first_time_wizard, ':/wizards/wizard_firsttime.bmp')
@@ -271,3 +273,4 @@ class UiFirstTimeWizard(object):
'and OpenLP is configured.'))
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.CustomButton2, translate('OpenLP.FirstTimeWizard', 'Cancel'))
diff --git a/openlp/core/ui/mainwindow.py b/openlp/core/ui/mainwindow.py
index 2feab127e..0e919871a 100644
--- a/openlp/core/ui/mainwindow.py
+++ b/openlp/core/ui/mainwindow.py
@@ -989,15 +989,21 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
# Read the temp file and output the user's CONF file with blanks to
# make it more readable.
temp_conf = open(temp_file, 'r')
- export_conf = open(export_file_name, 'w')
- for file_record in temp_conf:
- # Get rid of any invalid entries.
- if file_record.find('@Invalid()') == -1:
- file_record = file_record.replace('%20', ' ')
- export_conf.write(file_record)
- temp_conf.close()
- export_conf.close()
- os.remove(temp_file)
+ try:
+ export_conf = open(export_file_name, 'w')
+ for file_record in temp_conf:
+ # Get rid of any invalid entries.
+ if file_record.find('@Invalid()') == -1:
+ file_record = file_record.replace('%20', ' ')
+ export_conf.write(file_record)
+ temp_conf.close()
+ export_conf.close()
+ os.remove(temp_file)
+ except OSError as ose:
+ QtGui.QMessageBox.critical(self, translate('OpenLP.MainWindow', 'Export setting error'),
+ translate('OpenLP.MainWindow', 'An error occurred while exporting the '
+ 'settings: %s') % ose.strerror,
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
def on_mode_default_item_clicked(self):
"""
diff --git a/openlp/core/ui/servicemanager.py b/openlp/core/ui/servicemanager.py
index 8bdf9c383..0213fb02f 100644
--- a/openlp/core/ui/servicemanager.py
+++ b/openlp/core/ui/servicemanager.py
@@ -601,6 +601,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
shutil.copy(temp_file_name, path_file_name)
except shutil.Error:
return self.save_file_as()
+ except OSError as ose:
+ QtGui.QMessageBox.critical(self, translate('OpenLP.ServiceManager', 'Error Saving File'),
+ translate('OpenLP.ServiceManager', 'An error occurred while writing the '
+ 'service file: %s') % ose.strerror,
+ QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.Ok))
+ success = False
self.main_window.add_recent_file(path_file_name)
self.set_modified(False)
delete_file(temp_file_name)
diff --git a/openlp/core/ui/thememanager.py b/openlp/core/ui/thememanager.py
index 5ced57ea7..030516821 100644
--- a/openlp/core/ui/thememanager.py
+++ b/openlp/core/ui/thememanager.py
@@ -377,17 +377,11 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
self.application.set_busy_cursor()
if path:
Settings().setValue(self.settings_section + '/last directory export', path)
- try:
- self._export_theme(path, theme)
+ if self._export_theme(path, theme):
QtGui.QMessageBox.information(self,
translate('OpenLP.ThemeManager', 'Theme Exported'),
translate('OpenLP.ThemeManager',
'Your theme has been successfully exported.'))
- except (IOError, OSError):
- self.log_exception('Export Theme Failed')
- critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
- translate('OpenLP.ThemeManager',
- 'Your theme could not be exported due to an error.'))
self.application.set_normal_cursor()
def _export_theme(self, path, theme):
@@ -397,19 +391,24 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
:param theme: The name of the theme to be exported
"""
theme_path = os.path.join(path, theme + '.otz')
+ theme_zip = None
try:
theme_zip = zipfile.ZipFile(theme_path, 'w')
source = os.path.join(self.path, theme)
for files in os.walk(source):
for name in files[2]:
theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
- except (IOError, OSError):
+ theme_zip.close()
+ return True
+ except OSError as ose:
+ self.log_exception('Export Theme Failed')
+ critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
+ translate('OpenLP.ThemeManager', 'The theme export failed because this error '
+ 'occurred: %s') % ose.strerror)
if theme_zip:
theme_zip.close()
shutil.rmtree(theme_path, True)
- raise
- else:
- theme_zip.close()
+ return False
def on_import_theme(self, field=None):
"""
diff --git a/openlp/plugins/bibles/lib/__init__.py b/openlp/plugins/bibles/lib/__init__.py
index a8becc041..df72514fa 100644
--- a/openlp/plugins/bibles/lib/__init__.py
+++ b/openlp/plugins/bibles/lib/__init__.py
@@ -371,7 +371,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
from_chapter = from_verse
from_verse = None
if to_chapter:
- if to_chapter < from_chapter:
+ if from_chapter and to_chapter < from_chapter:
continue
else:
chapter = to_chapter
@@ -387,7 +387,7 @@ def parse_reference(reference, bible, language_selection, book_ref_id=False):
from_verse = 1
if not to_verse:
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))
for i in range(from_chapter + 1, to_chapter):
ref_list.append((book_ref_id, i, 1, -1))
diff --git a/openlp/plugins/bibles/lib/http.py b/openlp/plugins/bibles/lib/http.py
index 2f46efebd..7334dd259 100644
--- a/openlp/plugins/bibles/lib/http.py
+++ b/openlp/plugins/bibles/lib/http.py
@@ -365,31 +365,20 @@ class CWExtract(RegistryProperties):
if not soup:
return None
self.application.process_events()
- html_verses = soup.find_all('span', 'versetext')
- if not html_verses:
+ verses_div = soup.find_all('div', 'verse')
+ if not verses_div:
log.error('No verses found in the CrossWalk response.')
send_error_message('parse')
return None
verses = {}
- for verse in html_verses:
+ for verse in verses_div:
self.application.process_events()
- verse_number = int(verse.contents[0].contents[0])
- verse_text = ''
- for part in verse.contents:
- self.application.process_events()
- if isinstance(part, NavigableString):
- verse_text += part
- elif part and part.attrMap and \
- (part.attrMap['class'] == 'WordsOfChrist' or part.attrMap['class'] == 'strongs'):
- for subpart in part.contents:
- self.application.process_events()
- if isinstance(subpart, NavigableString):
- verse_text += subpart
- elif subpart and subpart.attrMap and subpart.attrMap['class'] == 'strongs':
- for subsub in subpart.contents:
- self.application.process_events()
- if isinstance(subsub, NavigableString):
- verse_text += subsub
+ verse_number = int(verse.find('strong').contents[0])
+ verse_span = verse.find('span')
+ tags_to_remove = verse_span.find_all(['a', 'sup'])
+ for tag in tags_to_remove:
+ tag.decompose()
+ verse_text = verse_span.get_text()
self.application.process_events()
# Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and .
verse_text = verse_text.strip('\n\r\t ')
@@ -409,16 +398,13 @@ class CWExtract(RegistryProperties):
soup = get_soup_for_bible_ref(chapter_url)
if not soup:
return None
- content = soup.find('div', {'class': 'Body'})
- content = content.find('ul', {'class': 'parent'})
+ content = soup.find_all(('h4', {'class': 'small-header'}))
if not content:
log.error('No books found in the Crosswalk response.')
send_error_message('parse')
return None
- content = content.find_all('li')
books = []
for book in content:
- book = book.find('a')
books.append(book.contents[0])
return books
diff --git a/openlp/plugins/bibles/lib/osis.py b/openlp/plugins/bibles/lib/osis.py
index 52e232ac9..07ec4ab23 100644
--- a/openlp/plugins/bibles/lib/osis.py
+++ b/openlp/plugins/bibles/lib/osis.py
@@ -58,7 +58,7 @@ class OSISBible(BibleDB):
# NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
# detection, and the two mechanisms together interfere with each other.
import_file = open(self.filename, 'rb')
- osis_bible_tree = etree.parse(import_file)
+ osis_bible_tree = etree.parse(import_file, parser=etree.XMLParser(recover=True))
namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
# Find bible language
language_id = None
diff --git a/openlp/plugins/bibles/lib/zefania.py b/openlp/plugins/bibles/lib/zefania.py
index 355fb82cf..ad8f96648 100644
--- a/openlp/plugins/bibles/lib/zefania.py
+++ b/openlp/plugins/bibles/lib/zefania.py
@@ -57,7 +57,7 @@ class ZefaniaBible(BibleDB):
# NOTE: We don't need to do any of the normal encoding detection here, because lxml does it's own encoding
# detection, and the two mechanisms together interfere with each other.
import_file = open(self.filename, 'rb')
- zefania_bible_tree = etree.parse(import_file)
+ zefania_bible_tree = etree.parse(import_file, parser=etree.XMLParser(recover=True))
# Find bible language
language_id = None
language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()")
@@ -76,9 +76,19 @@ class ZefaniaBible(BibleDB):
etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False)
xmlbible = zefania_bible_tree.getroot()
for BIBLEBOOK in xmlbible:
- book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books)
- if not book_ref_id:
- book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname')))
+ if self.stop_import_flag:
+ break
+ bname = BIBLEBOOK.get('bname')
+ bnumber = BIBLEBOOK.get('bnumber')
+ if not bname and not bnumber:
+ continue
+ if bname:
+ book_ref_id = self.get_book_ref_id_by_name(bname, num_books)
+ if not book_ref_id:
+ book_ref_id = self.get_book_ref_id_by_localised_name(bname)
+ else:
+ log.debug('Could not find a name, will use number, basically a guess.')
+ book_ref_id = int(bnumber)
if not book_ref_id:
log.error('Importing books from "%s" failed' % self.filename)
return False
@@ -94,7 +104,7 @@ class ZefaniaBible(BibleDB):
self.wizard.increment_progress_bar(
translate('BiblesPlugin.Zefnia', 'Importing %(bookname)s %(chapter)s...' %
{'bookname': db_book.name, 'chapter': chapter_number}))
- self.session.commit()
+ self.session.commit()
self.application.process_events()
except Exception as e:
critical_error_message_box(
diff --git a/openlp/plugins/presentations/lib/mediaitem.py b/openlp/plugins/presentations/lib/mediaitem.py
index 04553c375..10b295235 100644
--- a/openlp/plugins/presentations/lib/mediaitem.py
+++ b/openlp/plugins/presentations/lib/mediaitem.py
@@ -225,10 +225,10 @@ class PresentationMediaItem(MediaManagerItem):
self.clean_up_thumbnails(filepath)
self.main_window.increment_progress_bar()
self.main_window.finished_progress_bar()
- self.application.set_busy_cursor()
for row in row_list:
self.list_view.takeItem(row)
Settings().setValue(self.settings_section + '/presentations files', self.get_file_list())
+ self.application.set_normal_cursor()
def clean_up_thumbnails(self, filepath):
"""
diff --git a/openlp/plugins/presentations/lib/presentationcontroller.py b/openlp/plugins/presentations/lib/presentationcontroller.py
index 06d17694a..926971924 100644
--- a/openlp/plugins/presentations/lib/presentationcontroller.py
+++ b/openlp/plugins/presentations/lib/presentationcontroller.py
@@ -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
if Settings().value('presentations/thumbnail_scheme') == 'md5':
- folder = md5_hash('', self.file_path)
+ folder = md5_hash(self.file_path.encode('utf-8'))
else:
folder = self.get_file_name()
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
if Settings().value('presentations/thumbnail_scheme') == 'md5':
- folder = md5_hash('', self.file_path)
+ folder = md5_hash(self.file_path.encode('utf-8'))
else:
folder = folder = self.get_file_name()
return os.path.join(self.controller.temp_folder, folder)
diff --git a/openlp/plugins/songs/forms/songexportform.py b/openlp/plugins/songs/forms/songexportform.py
index 9caf7a446..f96c46638 100644
--- a/openlp/plugins/songs/forms/songexportform.py
+++ b/openlp/plugins/songs/forms/songexportform.py
@@ -244,12 +244,16 @@ class SongExportForm(OpenLPWizard):
for song in self._find_list_widget_items(self.selected_list_widget)
]
exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text())
- if exporter.do_export():
- self.progress_label.setText(
- translate('SongsPlugin.SongExportForm',
- 'Finished export. To import these files use the OpenLyrics importer.'))
- else:
- self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.'))
+ try:
+ if exporter.do_export():
+ self.progress_label.setText(
+ translate('SongsPlugin.SongExportForm',
+ 'Finished export. To import these files use the OpenLyrics importer.'))
+ else:
+ self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.'))
+ except OSError as ose:
+ self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed because this '
+ 'error occurred: %s') % ose.strerror)
def _find_list_widget_items(self, list_widget, text=''):
"""
diff --git a/openlp/plugins/songs/lib/importers/presentationmanager.py b/openlp/plugins/songs/lib/importers/presentationmanager.py
index f35b812a1..523bc5763 100644
--- a/openlp/plugins/songs/lib/importers/presentationmanager.py
+++ b/openlp/plugins/songs/lib/importers/presentationmanager.py
@@ -25,7 +25,9 @@ Presentationmanager song files into the current database.
"""
import os
-from lxml import objectify
+import re
+import chardet
+from lxml import objectify, etree
from openlp.core.ui.wizard import WizardStrings
from .songimport import SongImport
@@ -42,7 +44,18 @@ class PresentationManagerImport(SongImport):
if self.stop_import_flag:
return
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)
def process_song(self, root):
diff --git a/openlp/plugins/songusage/forms/songusagedetailform.py b/openlp/plugins/songusage/forms/songusagedetailform.py
index ba65e21df..a4136e88b 100644
--- a/openlp/plugins/songusage/forms/songusagedetailform.py
+++ b/openlp/plugins/songusage/forms/songusagedetailform.py
@@ -27,6 +27,7 @@ from PyQt4 import QtGui
from sqlalchemy.sql import and_
from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate
+from openlp.core.lib.ui import critical_error_message_box
from openlp.plugins.songusage.lib.db import SongUsageItem
from .songusagedetaildialog import Ui_SongUsageDetailDialog
@@ -104,8 +105,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog, RegistryPrope
translate('SongUsagePlugin.SongUsageDetailForm',
'Report \n%s \nhas been successfully created. ') % report_file_name
)
- except IOError:
+ except OSError as ose:
log.exception('Failed to write out song usage records')
+ critical_error_message_box(translate('SongUsagePlugin.SongUsageDetailForm', 'Report Creation Failed'),
+ translate('SongUsagePlugin.SongUsageDetailForm',
+ 'An error occurred while creating the report: %s') % ose.strerror)
finally:
if file_handle:
file_handle.close()
diff --git a/resources/images/openlp-2.qrc b/resources/images/openlp-2.qrc
index ba0f10e96..11c3482da 100644
--- a/resources/images/openlp-2.qrc
+++ b/resources/images/openlp-2.qrc
@@ -83,6 +83,7 @@
openlp-logo-64x64.pngopenlp-logo-128x128.pngopenlp-logo-256x256.png
+ openlp-logo.svgexception.png
diff --git a/tests/functional/openlp_core_common/test_projector_utilities.py b/tests/functional/openlp_core_common/test_projector_utilities.py
index fd9a2c57e..df06a3efd 100644
--- a/tests/functional/openlp_core_common/test_projector_utilities.py
+++ b/tests/functional/openlp_core_common/test_projector_utilities.py
@@ -23,6 +23,8 @@
Package to test the openlp.core.ui.projector.networkutils package.
"""
+import os
+
from unittest import TestCase
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'
pin = 'JBMIAProjectorLink'
test_hash = '5d8409bc1c3fa39749434aa3a5c38682'
+test_non_ascii_string = '이것은 한국어 시험 문자열'
+test_non_ascii_hash = 'fc00c7912976f6e9c19099b514ced201'
ip4_loopback = '127.0.0.1'
ip4_local = '192.168.1.1'
@@ -120,7 +124,7 @@ class testProjectorUtilities(TestCase):
Test MD5 hash from salt+data pass (python)
"""
# 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
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)
"""
# 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
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)
"""
# 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
- 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):
"""
Test MD5 hash from salt+hash fail (Qt)
"""
# 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
- 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')
diff --git a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py
index 0a234903c..63545a00c 100644
--- a/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py
+++ b/tests/functional/openlp_plugins/bibles/test_zefaniaimport.py
@@ -77,7 +77,6 @@ class TestZefaniaImport(TestCase):
mocked_import_wizard = MagicMock()
importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
importer.wizard = mocked_import_wizard
- importer.get_book_ref_id_by_name = MagicMock()
importer.create_verse = MagicMock()
importer.create_book = MagicMock()
importer.session = MagicMock()
@@ -92,3 +91,34 @@ class TestZefaniaImport(TestCase):
self.assertTrue(importer.create_verse.called)
for verse_tag, verse_text in test_data['verses']:
importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
+ importer.create_book.assert_any_call('Genesis', 1, 1)
+
+ def file_import_no_book_name_test(self):
+ """
+ Test the import of Zefania Bible file without book names
+ """
+ # GIVEN: Test files with a mocked out "manager", "import_wizard", and mocked functions
+ # get_book_ref_id_by_name, create_verse, create_book, session and get_language.
+ result_file = open(os.path.join(TEST_PATH, 'rst.json'), 'rb')
+ test_data = json.loads(result_file.read().decode())
+ bible_file = 'zefania-rst.xml'
+ with patch('openlp.plugins.bibles.lib.zefania.ZefaniaBible.application'):
+ mocked_manager = MagicMock()
+ mocked_import_wizard = MagicMock()
+ importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
+ importer.wizard = mocked_import_wizard
+ importer.create_verse = MagicMock()
+ importer.create_book = MagicMock()
+ importer.session = MagicMock()
+ importer.get_language = MagicMock()
+ importer.get_language.return_value = 'Russian'
+
+ # WHEN: Importing bible file
+ importer.filename = os.path.join(TEST_PATH, bible_file)
+ importer.do_import()
+
+ # THEN: The create_verse() method should have been called with each verse in the file.
+ self.assertTrue(importer.create_verse.called)
+ for verse_tag, verse_text in test_data['verses']:
+ importer.create_verse.assert_any_call(importer.create_book().id, '1', verse_tag, verse_text)
+ importer.create_book.assert_any_call('Exodus', 2, 1)
diff --git a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py
index 218596cf7..585aa34c5 100644
--- a/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py
+++ b/tests/functional/openlp_plugins/songs/test_presentationmanagerimport.py
@@ -46,3 +46,5 @@ class TestPresentationManagerFileImport(SongImportTestHelper):
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.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')))
diff --git a/tests/functional/openlp_plugins/songs/test_songselect.py b/tests/functional/openlp_plugins/songs/test_songselect.py
index 5ad3a594c..700933465 100644
--- a/tests/functional/openlp_plugins/songs/test_songselect.py
+++ b/tests/functional/openlp_plugins/songs/test_songselect.py
@@ -506,7 +506,6 @@ class TestSongSelectFileImport(TestCase, TestMixin):
# WHEN: We call the song importer
song_import.do_import()
- print(song_import.verses)
# THEN: Song values should be equal to test values in setUp
self.assertEquals(song_import.title, self.title, 'Song title should match')
self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match')
diff --git a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py
index 4c0e0fe14..b4eec48af 100644
--- a/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py
+++ b/tests/interfaces/openlp_plugins/bibles/test_lib_parse_reference.py
@@ -109,3 +109,13 @@ class TestBibleManager(TestCase, TestMixin):
results = parse_reference('Raoul 1', self.manager.db_cache['tests'], MagicMock())
# THEN a verse array should be returned
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")
diff --git a/tests/resources/bibles/rst.json b/tests/resources/bibles/rst.json
new file mode 100644
index 000000000..d8aca09ac
--- /dev/null
+++ b/tests/resources/bibles/rst.json
@@ -0,0 +1,16 @@
+{
+ "book": "Exodus",
+ "chapter": 1,
+ "verses": [
+ [ "1", "Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:" ],
+ [ "2", "Рувим, Симеон, Левий и Иуда," ],
+ [ "3", "Иссахар, Завулон и Вениамин," ],
+ [ "4", "Дан и Неффалим, Гад и Асир." ],
+ [ "5", "Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте." ],
+ [ "6", "И умер Иосиф и все братья его и весь род их;" ],
+ [ "7", "а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та." ],
+ [ "8", "И восстал в Египте новый царь, который не знал Иосифа," ],
+ [ "9", "и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;" ],
+ [ "10", "перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]." ]
+ ]
+}
diff --git a/tests/resources/bibles/zefania-rst.xml b/tests/resources/bibles/zefania-rst.xml
new file mode 100644
index 000000000..72d1230a4
--- /dev/null
+++ b/tests/resources/bibles/zefania-rst.xml
@@ -0,0 +1,48 @@
+
+
+
+
+
+
+
+ Russian Synodal Translation
+ Zefania XML Bible Markup Language
+ 2009-01-20
+ Jens Grabner
+
+ RUS
+
+ RST
+
+ 1876 Russian Synodal Translation, 1956 Edition
+ The text was supplied by "Light in East Germany".
+
+
+
+ "Light in East Germany" Tel +49 711 83 30 57
+ Postfach 1340 Fax +49 711 83 13 51
+ 7015 Korntal
+ Munchingen 1
+ Germany
+
+
+
+
+
+
+
+
Вторая книга Моисеева. Исход
+ Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:
+ Рувим, Симеон, Левий и Иуда,
+ Иссахар, Завулон и Вениамин,
+ Дан и Неффалим, Гад и Асир.
+ Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте.
+ И умер Иосиф и все братья его и весь род их;
+ а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та.
+ И восстал в Египте новый царь, который не знал Иосифа,
+ и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;
+ перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей].
+
+
+
diff --git a/tests/resources/presentationmanagersongs/Amazing Grace.json b/tests/resources/presentationmanagersongs/Amazing Grace.json
new file mode 100644
index 000000000..fa2e182a9
--- /dev/null
+++ b/tests/resources/presentationmanagersongs/Amazing Grace.json
@@ -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"
+ ]
+ ]
+}
diff --git a/tests/resources/presentationmanagersongs/Amazing Grace.sng b/tests/resources/presentationmanagersongs/Amazing Grace.sng
new file mode 100644
index 000000000..47b5ff3b3
Binary files /dev/null and b/tests/resources/presentationmanagersongs/Amazing Grace.sng differ