forked from openlp/openlp
trunk
This commit is contained in:
commit
0617ce3dc9
@ -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):
|
||||||
|
@ -271,8 +271,8 @@ ERROR_MSG = {E_OK: translate('OpenLP.ProjectorConstants', 'OK'), # E_OK | S_OK
|
|||||||
E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants',
|
E_PROXY_NOT_FOUND: translate('OpenLP.ProjectorConstants',
|
||||||
'The proxy address set with setProxy() was not found'),
|
'The proxy address set with setProxy() was not found'),
|
||||||
E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
|
E_PROXY_PROTOCOL: translate('OpenLP.ProjectorConstants',
|
||||||
'The connection negotiation with the proxy server because the response '
|
'The connection negotiation with the proxy server failed because the '
|
||||||
'from the proxy server could not be understood'),
|
'response from the proxy server could not be understood'),
|
||||||
E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
|
E_UNKNOWN_SOCKET_ERROR: translate('OpenLP.ProjectorConstants', 'An unidentified error occurred'),
|
||||||
S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
|
S_NOT_CONNECTED: translate('OpenLP.ProjectorConstants', 'Not connected'),
|
||||||
S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
|
S_CONNECTING: translate('OpenLP.ProjectorConstants', 'Connecting'),
|
||||||
|
@ -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'))
|
||||||
|
@ -989,6 +989,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
|
|||||||
# Read the temp file and output the user's CONF file with blanks to
|
# Read the temp file and output the user's CONF file with blanks to
|
||||||
# make it more readable.
|
# make it more readable.
|
||||||
temp_conf = open(temp_file, 'r')
|
temp_conf = open(temp_file, 'r')
|
||||||
|
try:
|
||||||
export_conf = open(export_file_name, 'w')
|
export_conf = open(export_file_name, 'w')
|
||||||
for file_record in temp_conf:
|
for file_record in temp_conf:
|
||||||
# Get rid of any invalid entries.
|
# Get rid of any invalid entries.
|
||||||
@ -998,6 +999,11 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow, RegistryProperties):
|
|||||||
temp_conf.close()
|
temp_conf.close()
|
||||||
export_conf.close()
|
export_conf.close()
|
||||||
os.remove(temp_file)
|
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):
|
def on_mode_default_item_clicked(self):
|
||||||
"""
|
"""
|
||||||
|
@ -601,6 +601,12 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
|
|||||||
shutil.copy(temp_file_name, path_file_name)
|
shutil.copy(temp_file_name, path_file_name)
|
||||||
except shutil.Error:
|
except shutil.Error:
|
||||||
return self.save_file_as()
|
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.main_window.add_recent_file(path_file_name)
|
||||||
self.set_modified(False)
|
self.set_modified(False)
|
||||||
delete_file(temp_file_name)
|
delete_file(temp_file_name)
|
||||||
|
@ -377,17 +377,11 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
|
|||||||
self.application.set_busy_cursor()
|
self.application.set_busy_cursor()
|
||||||
if path:
|
if path:
|
||||||
Settings().setValue(self.settings_section + '/last directory export', path)
|
Settings().setValue(self.settings_section + '/last directory export', path)
|
||||||
try:
|
if self._export_theme(path, theme):
|
||||||
self._export_theme(path, theme)
|
|
||||||
QtGui.QMessageBox.information(self,
|
QtGui.QMessageBox.information(self,
|
||||||
translate('OpenLP.ThemeManager', 'Theme Exported'),
|
translate('OpenLP.ThemeManager', 'Theme Exported'),
|
||||||
translate('OpenLP.ThemeManager',
|
translate('OpenLP.ThemeManager',
|
||||||
'Your theme has been successfully exported.'))
|
'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()
|
self.application.set_normal_cursor()
|
||||||
|
|
||||||
def _export_theme(self, path, theme):
|
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
|
:param theme: The name of the theme to be exported
|
||||||
"""
|
"""
|
||||||
theme_path = os.path.join(path, theme + '.otz')
|
theme_path = os.path.join(path, theme + '.otz')
|
||||||
|
theme_zip = None
|
||||||
try:
|
try:
|
||||||
theme_zip = zipfile.ZipFile(theme_path, 'w')
|
theme_zip = zipfile.ZipFile(theme_path, 'w')
|
||||||
source = os.path.join(self.path, theme)
|
source = os.path.join(self.path, theme)
|
||||||
for files in os.walk(source):
|
for files in os.walk(source):
|
||||||
for name in files[2]:
|
for name in files[2]:
|
||||||
theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
|
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:
|
if theme_zip:
|
||||||
theme_zip.close()
|
theme_zip.close()
|
||||||
shutil.rmtree(theme_path, True)
|
shutil.rmtree(theme_path, True)
|
||||||
raise
|
return False
|
||||||
else:
|
|
||||||
theme_zip.close()
|
|
||||||
|
|
||||||
def on_import_theme(self, field=None):
|
def on_import_theme(self, field=None):
|
||||||
"""
|
"""
|
||||||
|
@ -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))
|
||||||
|
@ -365,31 +365,20 @@ class CWExtract(RegistryProperties):
|
|||||||
if not soup:
|
if not soup:
|
||||||
return None
|
return None
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
html_verses = soup.find_all('span', 'versetext')
|
verses_div = soup.find_all('div', 'verse')
|
||||||
if not html_verses:
|
if not verses_div:
|
||||||
log.error('No verses found in the CrossWalk response.')
|
log.error('No verses found in the CrossWalk response.')
|
||||||
send_error_message('parse')
|
send_error_message('parse')
|
||||||
return None
|
return None
|
||||||
verses = {}
|
verses = {}
|
||||||
for verse in html_verses:
|
for verse in verses_div:
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
verse_number = int(verse.contents[0].contents[0])
|
verse_number = int(verse.find('strong').contents[0])
|
||||||
verse_text = ''
|
verse_span = verse.find('span')
|
||||||
for part in verse.contents:
|
tags_to_remove = verse_span.find_all(['a', 'sup'])
|
||||||
self.application.process_events()
|
for tag in tags_to_remove:
|
||||||
if isinstance(part, NavigableString):
|
tag.decompose()
|
||||||
verse_text += part
|
verse_text = verse_span.get_text()
|
||||||
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
|
|
||||||
self.application.process_events()
|
self.application.process_events()
|
||||||
# Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and .
|
# Fix up leading and trailing spaces, multiple spaces, and spaces between text and , and .
|
||||||
verse_text = verse_text.strip('\n\r\t ')
|
verse_text = verse_text.strip('\n\r\t ')
|
||||||
@ -409,16 +398,13 @@ class CWExtract(RegistryProperties):
|
|||||||
soup = get_soup_for_bible_ref(chapter_url)
|
soup = get_soup_for_bible_ref(chapter_url)
|
||||||
if not soup:
|
if not soup:
|
||||||
return None
|
return None
|
||||||
content = soup.find('div', {'class': 'Body'})
|
content = soup.find_all(('h4', {'class': 'small-header'}))
|
||||||
content = content.find('ul', {'class': 'parent'})
|
|
||||||
if not content:
|
if not content:
|
||||||
log.error('No books found in the Crosswalk response.')
|
log.error('No books found in the Crosswalk response.')
|
||||||
send_error_message('parse')
|
send_error_message('parse')
|
||||||
return None
|
return None
|
||||||
content = content.find_all('li')
|
|
||||||
books = []
|
books = []
|
||||||
for book in content:
|
for book in content:
|
||||||
book = book.find('a')
|
|
||||||
books.append(book.contents[0])
|
books.append(book.contents[0])
|
||||||
return books
|
return books
|
||||||
|
|
||||||
|
@ -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
|
# 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.
|
# detection, and the two mechanisms together interfere with each other.
|
||||||
import_file = open(self.filename, 'rb')
|
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'}
|
namespace = {'ns': 'http://www.bibletechnologies.net/2003/OSIS/namespace'}
|
||||||
# Find bible language
|
# Find bible language
|
||||||
language_id = None
|
language_id = None
|
||||||
|
@ -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
|
# 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.
|
# detection, and the two mechanisms together interfere with each other.
|
||||||
import_file = open(self.filename, 'rb')
|
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
|
# Find bible language
|
||||||
language_id = None
|
language_id = None
|
||||||
language = zefania_bible_tree.xpath("/XMLBIBLE/INFORMATION/language/text()")
|
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)
|
etree.strip_elements(zefania_bible_tree, ('PROLOG', 'REMARK', 'CAPTION', 'MEDIA'), with_tail=False)
|
||||||
xmlbible = zefania_bible_tree.getroot()
|
xmlbible = zefania_bible_tree.getroot()
|
||||||
for BIBLEBOOK in xmlbible:
|
for BIBLEBOOK in xmlbible:
|
||||||
book_ref_id = self.get_book_ref_id_by_name(str(BIBLEBOOK.get('bname')), num_books)
|
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:
|
if not book_ref_id:
|
||||||
book_ref_id = self.get_book_ref_id_by_localised_name(str(BIBLEBOOK.get('bname')))
|
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:
|
if not book_ref_id:
|
||||||
log.error('Importing books from "%s" failed' % self.filename)
|
log.error('Importing books from "%s" failed' % self.filename)
|
||||||
return False
|
return False
|
||||||
|
@ -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.encode('utf-8'))
|
||||||
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.encode('utf-8'))
|
||||||
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)
|
||||||
|
@ -244,12 +244,16 @@ class SongExportForm(OpenLPWizard):
|
|||||||
for song in self._find_list_widget_items(self.selected_list_widget)
|
for song in self._find_list_widget_items(self.selected_list_widget)
|
||||||
]
|
]
|
||||||
exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text())
|
exporter = OpenLyricsExport(self, songs, self.directory_line_edit.text())
|
||||||
|
try:
|
||||||
if exporter.do_export():
|
if exporter.do_export():
|
||||||
self.progress_label.setText(
|
self.progress_label.setText(
|
||||||
translate('SongsPlugin.SongExportForm',
|
translate('SongsPlugin.SongExportForm',
|
||||||
'Finished export. To import these files use the <strong>OpenLyrics</strong> importer.'))
|
'Finished export. To import these files use the <strong>OpenLyrics</strong> importer.'))
|
||||||
else:
|
else:
|
||||||
self.progress_label.setText(translate('SongsPlugin.SongExportForm', 'Your song export failed.'))
|
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=''):
|
def _find_list_widget_items(self, list_widget, text=''):
|
||||||
"""
|
"""
|
||||||
|
@ -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):
|
||||||
|
@ -27,6 +27,7 @@ from PyQt4 import QtGui
|
|||||||
from sqlalchemy.sql import and_
|
from sqlalchemy.sql import and_
|
||||||
|
|
||||||
from openlp.core.common import RegistryProperties, Settings, check_directory_exists, translate
|
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 openlp.plugins.songusage.lib.db import SongUsageItem
|
||||||
from .songusagedetaildialog import Ui_SongUsageDetailDialog
|
from .songusagedetaildialog import Ui_SongUsageDetailDialog
|
||||||
|
|
||||||
@ -104,8 +105,11 @@ class SongUsageDetailForm(QtGui.QDialog, Ui_SongUsageDetailDialog, RegistryPrope
|
|||||||
translate('SongUsagePlugin.SongUsageDetailForm',
|
translate('SongUsagePlugin.SongUsageDetailForm',
|
||||||
'Report \n%s \nhas been successfully created. ') % report_file_name
|
'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')
|
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:
|
finally:
|
||||||
if file_handle:
|
if file_handle:
|
||||||
file_handle.close()
|
file_handle.close()
|
||||||
|
@ -83,6 +83,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')
|
||||||
|
@ -77,7 +77,6 @@ class TestZefaniaImport(TestCase):
|
|||||||
mocked_import_wizard = MagicMock()
|
mocked_import_wizard = MagicMock()
|
||||||
importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
|
importer = ZefaniaBible(mocked_manager, path='.', name='.', filename='')
|
||||||
importer.wizard = mocked_import_wizard
|
importer.wizard = mocked_import_wizard
|
||||||
importer.get_book_ref_id_by_name = MagicMock()
|
|
||||||
importer.create_verse = MagicMock()
|
importer.create_verse = MagicMock()
|
||||||
importer.create_book = MagicMock()
|
importer.create_book = MagicMock()
|
||||||
importer.session = MagicMock()
|
importer.session = MagicMock()
|
||||||
@ -92,3 +91,34 @@ class TestZefaniaImport(TestCase):
|
|||||||
self.assertTrue(importer.create_verse.called)
|
self.assertTrue(importer.create_verse.called)
|
||||||
for verse_tag, verse_text in test_data['verses']:
|
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_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)
|
||||||
|
@ -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')))
|
||||||
|
@ -506,7 +506,6 @@ class TestSongSelectFileImport(TestCase, TestMixin):
|
|||||||
|
|
||||||
# WHEN: We call the song importer
|
# WHEN: We call the song importer
|
||||||
song_import.do_import()
|
song_import.do_import()
|
||||||
print(song_import.verses)
|
|
||||||
# THEN: Song values should be equal to test values in setUp
|
# 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.title, self.title, 'Song title should match')
|
||||||
self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match')
|
self.assertEquals(song_import.ccli_number, self.ccli_number, 'CCLI Song Number should match')
|
||||||
|
@ -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")
|
||||||
|
16
tests/resources/bibles/rst.json
Normal file
16
tests/resources/bibles/rst.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"book": "Exodus",
|
||||||
|
"chapter": 1,
|
||||||
|
"verses": [
|
||||||
|
[ "1", "Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:" ],
|
||||||
|
[ "2", "Рувим, Симеон, Левий и Иуда," ],
|
||||||
|
[ "3", "Иссахар, Завулон и Вениамин," ],
|
||||||
|
[ "4", "Дан и Неффалим, Гад и Асир." ],
|
||||||
|
[ "5", "Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте." ],
|
||||||
|
[ "6", "И умер Иосиф и все братья его и весь род их;" ],
|
||||||
|
[ "7", "а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та." ],
|
||||||
|
[ "8", "И восстал в Египте новый царь, который не знал Иосифа," ],
|
||||||
|
[ "9", "и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;" ],
|
||||||
|
[ "10", "перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей]." ]
|
||||||
|
]
|
||||||
|
}
|
48
tests/resources/bibles/zefania-rst.xml
Normal file
48
tests/resources/bibles/zefania-rst.xml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--Visit the online documentation for Zefania XML Markup-->
|
||||||
|
<!--http://bgfdb.de/zefaniaxml/bml/-->
|
||||||
|
<!--Download another Zefania XML files from-->
|
||||||
|
<!--http://sourceforge.net/projects/zefania-sharp-->
|
||||||
|
<XMLBIBLE xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="zef2005.xsd" version="2.0.1.18" status="v" biblename="Russian Synodal Translation" type="x-bible" revision="0">
|
||||||
|
<INFORMATION>
|
||||||
|
<title>Russian Synodal Translation</title>
|
||||||
|
<format>Zefania XML Bible Markup Language</format>
|
||||||
|
<date>2009-01-20</date>
|
||||||
|
<creator>Jens Grabner</creator>
|
||||||
|
<source>http://www.agape-biblia.org
|
||||||
|
http://www.crosswire.org/sword/modules/</source>
|
||||||
|
<language>RUS</language>
|
||||||
|
<publisher />
|
||||||
|
<identifier>RST</identifier>
|
||||||
|
<contributors>
|
||||||
|
1876 Russian Synodal Translation, 1956 Edition
|
||||||
|
The text was supplied by "Light in East Germany".</contributors>
|
||||||
|
<rights>
|
||||||
|
</rights>
|
||||||
|
<description>
|
||||||
|
"Light in East Germany" Tel +49 711 83 30 57
|
||||||
|
Postfach 1340 Fax +49 711 83 13 51
|
||||||
|
7015 Korntal
|
||||||
|
Munchingen 1
|
||||||
|
Germany
|
||||||
|
</description>
|
||||||
|
<subject />
|
||||||
|
<type />
|
||||||
|
<coverage />
|
||||||
|
</INFORMATION>
|
||||||
|
<BIBLEBOOK bnumber="2">
|
||||||
|
<CHAPTER cnumber="1">
|
||||||
|
<CAPTION vref="1">Вторая книга Моисеева. Исход</CAPTION>
|
||||||
|
<VERS vnumber="1">Вот имена сынов Израилевых, которые вошли в Египет с Иаковом, вошли каждый с домом своим:</VERS>
|
||||||
|
<VERS vnumber="2">Рувим, Симеон, Левий и Иуда,</VERS>
|
||||||
|
<VERS vnumber="3">Иссахар, Завулон и Вениамин,</VERS>
|
||||||
|
<VERS vnumber="4">Дан и Неффалим, Гад и Асир.</VERS>
|
||||||
|
<VERS vnumber="5">Всех же душ, происшедших от чресл Иакова, было семьдесят, а Иосиф был [уже] в Египте.</VERS>
|
||||||
|
<VERS vnumber="6">И умер Иосиф и все братья его и весь род их;</VERS>
|
||||||
|
<VERS vnumber="7">а сыны Израилевы расплодились и размножились, и возросли и усилились чрезвычайно, и наполнилась ими земля та.</VERS>
|
||||||
|
<VERS vnumber="8">И восстал в Египте новый царь, который не знал Иосифа,</VERS>
|
||||||
|
<VERS vnumber="9">и сказал народу своему: вот, народ сынов Израилевых многочислен и сильнее нас;</VERS>
|
||||||
|
<VERS vnumber="10">перехитрим же его, чтобы он не размножался; иначе, когда случится война, соединится и он с нашими неприятелями, и вооружится против нас, и выйдет из земли [нашей].</VERS>
|
||||||
|
</CHAPTER>
|
||||||
|
</BIBLEBOOK>
|
||||||
|
</XMLBIBLE>
|
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.
Loading…
Reference in New Issue
Block a user