forked from openlp/openlp
trunk
This commit is contained in:
commit
db477f8d9c
@ -286,6 +286,7 @@ class Settings(QtCore.QSettings):
|
||||
'themes/last directory export': '',
|
||||
'themes/last directory import': '',
|
||||
'themes/theme level': ThemeLevel.Song,
|
||||
'themes/wrap footer': False,
|
||||
'user interface/live panel': True,
|
||||
'user interface/live splitter geometry': QtCore.QByteArray(),
|
||||
'user interface/lock panel': False,
|
||||
|
@ -96,9 +96,10 @@ def upgrade_db(url, upgrade):
|
||||
mapper(Metadata, metadata_table)
|
||||
version_meta = session.query(Metadata).get('version')
|
||||
if version_meta is None:
|
||||
version_meta = Metadata.populate(key='version', value='0')
|
||||
# Tables have just been created - fill the version field with the most recent version
|
||||
version = upgrade.__version__
|
||||
version_meta = Metadata.populate(key='version', value=version)
|
||||
session.add(version_meta)
|
||||
version = 0
|
||||
else:
|
||||
version = int(version_meta.value)
|
||||
if version > upgrade.__version__:
|
||||
|
@ -398,6 +398,7 @@ import logging
|
||||
|
||||
from PyQt4 import QtWebKit
|
||||
|
||||
from openlp.core.common import Settings
|
||||
from openlp.core.lib.theme import BackgroundType, BackgroundGradientType, VerticalType, HorizontalType
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
@ -750,12 +751,13 @@ def build_footer_css(item, height):
|
||||
font-size: %spt;
|
||||
color: %s;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
white-space: %s;
|
||||
"""
|
||||
theme = item.theme_data
|
||||
if not theme or not item.footer:
|
||||
return ''
|
||||
bottom = height - int(item.footer.y()) - int(item.footer.height())
|
||||
whitespace = 'normal' if Settings().value('themes/wrap footer') else 'nowrap'
|
||||
lyrics_html = style % (item.footer.x(), bottom, item.footer.width(),
|
||||
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color)
|
||||
theme.font_footer_name, theme.font_footer_size, theme.font_footer_color, whitespace)
|
||||
return lyrics_html
|
||||
|
@ -1103,7 +1103,7 @@ class ServiceManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ServiceManage
|
||||
Moves the cursor selection up the window. Called by the up arrow.
|
||||
"""
|
||||
item = self.service_manager_list.currentItem()
|
||||
item_before = self.service_manager_list.item_above(item)
|
||||
item_before = self.service_manager_list.itemAbove(item)
|
||||
if item_before is None:
|
||||
return
|
||||
self.service_manager_list.setCurrentItem(item_before)
|
||||
|
@ -384,16 +384,8 @@ 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)
|
||||
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).encode('utf-8'), os.path.join(theme, name).encode('utf-8')
|
||||
)
|
||||
self._export_theme(path, theme)
|
||||
QtGui.QMessageBox.information(self,
|
||||
translate('OpenLP.ThemeManager', 'Theme Exported'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
@ -403,11 +395,29 @@ class ThemeManager(OpenLPMixin, RegistryMixin, QtGui.QWidget, Ui_ThemeManager, R
|
||||
critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
|
||||
translate('OpenLP.ThemeManager',
|
||||
'Your theme could not be exported due to an error.'))
|
||||
finally:
|
||||
if theme_zip:
|
||||
theme_zip.close()
|
||||
self.application.set_normal_cursor()
|
||||
|
||||
def _export_theme(self, path, theme):
|
||||
"""
|
||||
Create the zipfile with the theme contents.
|
||||
:param path: Location where the zip file will be placed
|
||||
:param theme: The name of the theme to be exported
|
||||
"""
|
||||
theme_path = os.path.join(path, theme + '.otz')
|
||||
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):
|
||||
if theme_zip:
|
||||
theme_zip.close()
|
||||
shutil.rmtree(theme_path, True)
|
||||
raise
|
||||
else:
|
||||
theme_zip.close()
|
||||
|
||||
def on_import_theme(self, field=None):
|
||||
"""
|
||||
Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
|
||||
|
@ -69,6 +69,14 @@ class ThemesTab(SettingsTab):
|
||||
self.default_list_view.setObjectName('default_list_view')
|
||||
self.global_group_box_layout.addWidget(self.default_list_view)
|
||||
self.left_layout.addWidget(self.global_group_box)
|
||||
self.universal_group_box = QtGui.QGroupBox(self.left_column)
|
||||
self.universal_group_box.setObjectName('universal_group_box')
|
||||
self.universal_group_box_layout = QtGui.QVBoxLayout(self.universal_group_box)
|
||||
self.universal_group_box_layout.setObjectName('universal_group_box_layout')
|
||||
self.wrap_footer_check_box = QtGui.QCheckBox(self.universal_group_box)
|
||||
self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
|
||||
self.universal_group_box_layout.addWidget(self.wrap_footer_check_box)
|
||||
self.left_layout.addWidget(self.universal_group_box)
|
||||
self.left_layout.addStretch()
|
||||
self.level_group_box = QtGui.QGroupBox(self.right_column)
|
||||
self.level_group_box.setObjectName('level_group_box')
|
||||
@ -112,6 +120,8 @@ class ThemesTab(SettingsTab):
|
||||
"""
|
||||
self.tab_title_visible = UiStrings().Themes
|
||||
self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
|
||||
self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
|
||||
self.wrap_footer_check_box.setText(translate('OpenLP.ThemesTab', '&Wrap footer text'))
|
||||
self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
|
||||
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
|
||||
self.song_level_label.setText(
|
||||
@ -136,6 +146,7 @@ class ThemesTab(SettingsTab):
|
||||
settings.beginGroup(self.settings_section)
|
||||
self.theme_level = settings.value('theme level')
|
||||
self.global_theme = settings.value('global theme')
|
||||
self.wrap_footer_check_box.setChecked(settings.value('wrap footer'))
|
||||
settings.endGroup()
|
||||
if self.theme_level == ThemeLevel.Global:
|
||||
self.global_level_radio_button.setChecked(True)
|
||||
@ -152,6 +163,7 @@ class ThemesTab(SettingsTab):
|
||||
settings.beginGroup(self.settings_section)
|
||||
settings.setValue('theme level', self.theme_level)
|
||||
settings.setValue('global theme', self.global_theme)
|
||||
settings.setValue('wrap footer', self.wrap_footer_check_box.isChecked())
|
||||
settings.endGroup()
|
||||
self.renderer.set_theme_level(self.theme_level)
|
||||
if self.tab_visited:
|
||||
|
@ -138,6 +138,9 @@ class Ui_EditSongDialog(object):
|
||||
self.author_remove_layout = QtGui.QHBoxLayout()
|
||||
self.author_remove_layout.setObjectName('author_remove_layout')
|
||||
self.author_remove_layout.addStretch()
|
||||
self.author_edit_button = QtGui.QPushButton(self.authors_group_box)
|
||||
self.author_edit_button.setObjectName('author_edit_button')
|
||||
self.author_remove_layout.addWidget(self.author_edit_button)
|
||||
self.author_remove_button = QtGui.QPushButton(self.authors_group_box)
|
||||
self.author_remove_button.setObjectName('author_remove_button')
|
||||
self.author_remove_layout.addWidget(self.author_remove_button)
|
||||
@ -305,6 +308,7 @@ class Ui_EditSongDialog(object):
|
||||
translate('SongsPlugin.EditSongForm', 'Title && Lyrics'))
|
||||
self.authors_group_box.setTitle(SongStrings.Authors)
|
||||
self.author_add_button.setText(translate('SongsPlugin.EditSongForm', '&Add to Song'))
|
||||
self.author_edit_button.setText(translate('SongsPlugin.EditSongForm', '&Edit Author Type'))
|
||||
self.author_remove_button.setText(translate('SongsPlugin.EditSongForm', '&Remove'))
|
||||
self.maintenance_button.setText(translate('SongsPlugin.EditSongForm', '&Manage Authors, Topics, Song Books'))
|
||||
self.topics_group_box.setTitle(SongStrings.Topic)
|
||||
|
@ -70,6 +70,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
||||
self.setupUi(self)
|
||||
# Connecting signals and slots
|
||||
self.author_add_button.clicked.connect(self.on_author_add_button_clicked)
|
||||
self.author_edit_button.clicked.connect(self.on_author_edit_button_clicked)
|
||||
self.author_remove_button.clicked.connect(self.on_author_remove_button_clicked)
|
||||
self.authors_list_view.itemClicked.connect(self.on_authors_list_view_clicked)
|
||||
self.topic_add_button.clicked.connect(self.on_topic_add_button_clicked)
|
||||
@ -334,6 +335,7 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
||||
"""
|
||||
self.verse_edit_button.setEnabled(False)
|
||||
self.verse_delete_button.setEnabled(False)
|
||||
self.author_edit_button.setEnabled(False)
|
||||
self.author_remove_button.setEnabled(False)
|
||||
self.topic_remove_button.setEnabled(False)
|
||||
|
||||
@ -354,12 +356,9 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
||||
|
||||
# Types
|
||||
self.author_types_combo_box.clear()
|
||||
self.author_types_combo_box.addItem('')
|
||||
# Don't iterate over the dictionary to give them this specific order
|
||||
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Words], AuthorType.Words)
|
||||
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Music], AuthorType.Music)
|
||||
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.WordsAndMusic], AuthorType.WordsAndMusic)
|
||||
self.author_types_combo_box.addItem(AuthorType.Types[AuthorType.Translation], AuthorType.Translation)
|
||||
for author_type in AuthorType.SortedTypes:
|
||||
self.author_types_combo_box.addItem(AuthorType.Types[author_type], author_type)
|
||||
|
||||
def load_topics(self):
|
||||
"""
|
||||
@ -596,9 +595,32 @@ class EditSongForm(QtGui.QDialog, Ui_EditSongDialog, RegistryProperties):
|
||||
"""
|
||||
Run a set of actions when an author in the list is selected (mainly enable the delete button).
|
||||
"""
|
||||
if self.authors_list_view.count() > 1:
|
||||
count = self.authors_list_view.count()
|
||||
if count > 0:
|
||||
self.author_edit_button.setEnabled(True)
|
||||
if count > 1:
|
||||
# There must be at least one author
|
||||
self.author_remove_button.setEnabled(True)
|
||||
|
||||
def on_author_edit_button_clicked(self):
|
||||
"""
|
||||
Show a dialog to change the type of an author when the edit button is clicked
|
||||
"""
|
||||
self.author_edit_button.setEnabled(False)
|
||||
item = self.authors_list_view.currentItem()
|
||||
author_id, author_type = item.data(QtCore.Qt.UserRole)
|
||||
choice, ok = QtGui.QInputDialog.getItem(self, translate('SongsPlugin.EditSongForm', 'Edit Author Type'),
|
||||
translate('SongsPlugin.EditSongForm', 'Choose type for this author'),
|
||||
AuthorType.TranslatedTypes,
|
||||
current=AuthorType.SortedTypes.index(author_type),
|
||||
editable=False)
|
||||
if not ok:
|
||||
return
|
||||
author = self.manager.get_object(Author, author_id)
|
||||
author_type = AuthorType.from_translated_text(choice)
|
||||
item.setData(QtCore.Qt.UserRole, (author_id, author_type))
|
||||
item.setText(author.get_display_name(author_type))
|
||||
|
||||
def on_author_remove_button_clicked(self):
|
||||
"""
|
||||
Remove the author from the list when the delete button is clicked.
|
||||
|
@ -374,7 +374,7 @@ def clean_song(manager, song):
|
||||
:param manager: The song database manager object.
|
||||
:param song: The song object.
|
||||
"""
|
||||
from .xml import SongXML
|
||||
from .openlyricsxml import SongXML
|
||||
|
||||
if song.title:
|
||||
song.title = clean_title(song.title)
|
||||
|
@ -69,17 +69,42 @@ class AuthorType(object):
|
||||
|
||||
The 'words+music' type is not an official type, but is provided for convenience.
|
||||
"""
|
||||
NoType = ''
|
||||
Words = 'words'
|
||||
Music = 'music'
|
||||
WordsAndMusic = 'words+music'
|
||||
Translation = 'translation'
|
||||
Types = {
|
||||
NoType: '',
|
||||
Words: translate('SongsPlugin.AuthorType', 'Words', 'Author who wrote the lyrics of a song'),
|
||||
Music: translate('SongsPlugin.AuthorType', 'Music', 'Author who wrote the music of a song'),
|
||||
WordsAndMusic: translate('SongsPlugin.AuthorType', 'Words and Music',
|
||||
'Author who wrote both lyrics and music of a song'),
|
||||
Translation: translate('SongsPlugin.AuthorType', 'Translation', 'Author who translated the song')
|
||||
}
|
||||
SortedTypes = [
|
||||
NoType,
|
||||
Words,
|
||||
Music,
|
||||
WordsAndMusic
|
||||
]
|
||||
TranslatedTypes = [
|
||||
Types[NoType],
|
||||
Types[Words],
|
||||
Types[Music],
|
||||
Types[WordsAndMusic]
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def from_translated_text(translated_type):
|
||||
"""
|
||||
Get the AuthorType from a translated string.
|
||||
:param translated_type: Translated Author type.
|
||||
"""
|
||||
for key, value in AuthorType.Types.items():
|
||||
if value == translated_type:
|
||||
return key
|
||||
return AuthorType.NoType
|
||||
|
||||
|
||||
class Book(BaseModel):
|
||||
|
@ -51,12 +51,12 @@ from .importers.foilpresenter import FoilPresenterImport
|
||||
from .importers.zionworx import ZionWorxImport
|
||||
from .importers.propresenter import ProPresenterImport
|
||||
from .importers.worshipassistant import WorshipAssistantImport
|
||||
# Imports that might fail
|
||||
|
||||
from .importers.powerpraise import PowerPraiseImport
|
||||
from .importers.presentationmanager import PresentationManagerImport
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Imports that might fail
|
||||
try:
|
||||
from .importers.songsoffellowship import SongsOfFellowshipImport
|
||||
HAS_SOF = True
|
||||
@ -160,17 +160,19 @@ class SongFormat(object):
|
||||
FoilPresenter = 8
|
||||
MediaShout = 9
|
||||
OpenSong = 10
|
||||
PowerSong = 11
|
||||
ProPresenter = 12
|
||||
SongBeamer = 13
|
||||
SongPro = 14
|
||||
SongShowPlus = 15
|
||||
SongsOfFellowship = 16
|
||||
SundayPlus = 17
|
||||
WordsOfWorship = 18
|
||||
WorshipAssistant = 19
|
||||
WorshipCenterPro = 20
|
||||
ZionWorx = 21
|
||||
PowerPraise = 11
|
||||
PowerSong = 12
|
||||
PresentationManager = 13
|
||||
ProPresenter = 14
|
||||
SongBeamer = 15
|
||||
SongPro = 16
|
||||
SongShowPlus = 17
|
||||
SongsOfFellowship = 18
|
||||
SundayPlus = 19
|
||||
WordsOfWorship = 20
|
||||
WorshipAssistant = 21
|
||||
WorshipCenterPro = 22
|
||||
ZionWorx = 23
|
||||
|
||||
# Set optional attribute defaults
|
||||
__defaults__ = {
|
||||
@ -266,6 +268,12 @@ class SongFormat(object):
|
||||
'name': WizardStrings.OS,
|
||||
'prefix': 'openSong'
|
||||
},
|
||||
PowerPraise: {
|
||||
'class': PowerPraiseImport,
|
||||
'name': 'PowerPraise',
|
||||
'prefix': 'powerPraise',
|
||||
'filter': '%s (*.ppl)' % translate('SongsPlugin.ImportWizardForm', 'PowerPraise Song Files')
|
||||
},
|
||||
PowerSong: {
|
||||
'class': PowerSongImport,
|
||||
'name': 'PowerSong 1.0',
|
||||
@ -274,6 +282,12 @@ class SongFormat(object):
|
||||
'invalidSourceMsg': translate('SongsPlugin.ImportWizardForm', 'You need to specify a valid PowerSong 1.0 '
|
||||
'database folder.')
|
||||
},
|
||||
PresentationManager: {
|
||||
'class': PresentationManagerImport,
|
||||
'name': 'PresentationManager',
|
||||
'prefix': 'presentationManager',
|
||||
'filter': '%s (*.sng)' % translate('SongsPlugin.ImportWizardForm', 'PresentationManager Song Files')
|
||||
},
|
||||
ProPresenter: {
|
||||
'class': ProPresenterImport,
|
||||
'name': 'ProPresenter',
|
||||
@ -374,7 +388,9 @@ class SongFormat(object):
|
||||
SongFormat.FoilPresenter,
|
||||
SongFormat.MediaShout,
|
||||
SongFormat.OpenSong,
|
||||
SongFormat.PowerPraise,
|
||||
SongFormat.PowerSong,
|
||||
SongFormat.PresentationManager,
|
||||
SongFormat.ProPresenter,
|
||||
SongFormat.SongBeamer,
|
||||
SongFormat.SongPro,
|
||||
|
91
openlp/plugins/songs/lib/importers/powerpraise.py
Normal file
91
openlp/plugins/songs/lib/importers/powerpraise.py
Normal file
@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`powerpraiseimport` module provides the functionality for importing
|
||||
Powerpraise song files into the current database.
|
||||
"""
|
||||
|
||||
import os
|
||||
from lxml import objectify
|
||||
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from .songimport import SongImport
|
||||
|
||||
|
||||
class PowerPraiseImport(SongImport):
|
||||
"""
|
||||
The :class:`PowerpraiseImport` class provides OpenLP with the
|
||||
ability to import Powerpraise song files.
|
||||
"""
|
||||
def do_import(self):
|
||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||
for file_path in self.import_source:
|
||||
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()
|
||||
self.process_song(root)
|
||||
|
||||
def process_song(self, root):
|
||||
self.set_defaults()
|
||||
self.title = str(root.general.title)
|
||||
verse_order_list = []
|
||||
verse_count = {}
|
||||
for item in root.order.item:
|
||||
verse_order_list.append(str(item))
|
||||
for part in root.songtext.part:
|
||||
original_verse_def = part.get('caption')
|
||||
# There are some predefined verse defitions in PowerPraise, try to parse these
|
||||
if original_verse_def.startswith("Strophe") or original_verse_def.startswith("Teil"):
|
||||
verse_def = 'v'
|
||||
elif original_verse_def.startswith("Refrain"):
|
||||
verse_def = 'c'
|
||||
elif original_verse_def.startswith("Bridge"):
|
||||
verse_def = 'b'
|
||||
elif original_verse_def.startswith("Schluss"):
|
||||
verse_def = 'e'
|
||||
else:
|
||||
verse_def = 'o'
|
||||
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
|
||||
verse_def = '%s%d' % (verse_def, verse_count[verse_def])
|
||||
verse_text = []
|
||||
for slide in part.slide:
|
||||
if not hasattr(slide, 'line'):
|
||||
continue # No content
|
||||
for line in slide.line:
|
||||
verse_text.append(str(line))
|
||||
self.add_verse('\n'.join(verse_text), verse_def)
|
||||
# Update verse name in verse order list
|
||||
for i in range(len(verse_order_list)):
|
||||
if verse_order_list[i].lower() == original_verse_def.lower():
|
||||
verse_order_list[i] = verse_def
|
||||
|
||||
self.verse_order_list = verse_order_list
|
||||
if not self.finish():
|
||||
self.log_error(self.import_source)
|
93
openlp/plugins/songs/lib/importers/presentationmanager.py
Normal file
93
openlp/plugins/songs/lib/importers/presentationmanager.py
Normal file
@ -0,0 +1,93 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`presentationmanager` module provides the functionality for importing
|
||||
Presentationmanager song files into the current database.
|
||||
"""
|
||||
|
||||
import os
|
||||
from lxml import objectify
|
||||
|
||||
from openlp.core.ui.wizard import WizardStrings
|
||||
from .songimport import SongImport
|
||||
|
||||
|
||||
class PresentationManagerImport(SongImport):
|
||||
"""
|
||||
The :class:`PresentationManagerImport` class provides OpenLP with the
|
||||
ability to import Presentationmanager song files.
|
||||
"""
|
||||
def do_import(self):
|
||||
self.import_wizard.progress_bar.setMaximum(len(self.import_source))
|
||||
for file_path in self.import_source:
|
||||
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()
|
||||
self.process_song(root)
|
||||
|
||||
def process_song(self, root):
|
||||
self.set_defaults()
|
||||
self.title = str(root.attributes.title)
|
||||
self.add_author(str(root.attributes.author))
|
||||
self.copyright = str(root.attributes.copyright)
|
||||
self.ccli_number = str(root.attributes.ccli_number)
|
||||
self.comments = str(root.attributes.comments)
|
||||
verse_order_list = []
|
||||
verse_count = {}
|
||||
duplicates = []
|
||||
for verse in root.verses.verse:
|
||||
original_verse_def = verse.get('id')
|
||||
# Presentation Manager stores duplicate verses instead of a verse order.
|
||||
# We need to create the verse order from that.
|
||||
is_duplicate = False
|
||||
if original_verse_def in duplicates:
|
||||
is_duplicate = True
|
||||
else:
|
||||
duplicates.append(original_verse_def)
|
||||
if original_verse_def.startswith("Verse"):
|
||||
verse_def = 'v'
|
||||
elif original_verse_def.startswith("Chorus") or original_verse_def.startswith("Refrain"):
|
||||
verse_def = 'c'
|
||||
elif original_verse_def.startswith("Bridge"):
|
||||
verse_def = 'b'
|
||||
elif original_verse_def.startswith("End"):
|
||||
verse_def = 'e'
|
||||
else:
|
||||
verse_def = 'o'
|
||||
if not is_duplicate: # Only increment verse number if no duplicate
|
||||
verse_count[verse_def] = verse_count.get(verse_def, 0) + 1
|
||||
verse_def = '%s%d' % (verse_def, verse_count[verse_def])
|
||||
if not is_duplicate: # Only add verse if no duplicate
|
||||
self.add_verse(str(verse).strip(), verse_def)
|
||||
verse_order_list.append(verse_def)
|
||||
|
||||
self.verse_order_list = verse_order_list
|
||||
if not self.finish():
|
||||
self.log_error(self.import_source)
|
@ -33,7 +33,6 @@ backend for the Songs plugin
|
||||
import logging
|
||||
|
||||
from sqlalchemy import Column, ForeignKey, types
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlalchemy.sql.expression import func, false, null, text
|
||||
|
||||
from openlp.core.lib.db import get_upgrade_op
|
||||
@ -57,16 +56,13 @@ def upgrade_1(session, metadata):
|
||||
:param session:
|
||||
:param metadata:
|
||||
"""
|
||||
try:
|
||||
op = get_upgrade_op(session)
|
||||
op.drop_table('media_files_songs')
|
||||
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
|
||||
op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
|
||||
if metadata.bind.url.get_dialect().name != 'sqlite':
|
||||
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
|
||||
op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
|
||||
except OperationalError:
|
||||
log.info('Upgrade 1 has already been run')
|
||||
op = get_upgrade_op(session)
|
||||
op.drop_table('media_files_songs')
|
||||
op.add_column('media_files', Column('song_id', types.Integer(), server_default=null()))
|
||||
op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0')))
|
||||
if metadata.bind.url.get_dialect().name != 'sqlite':
|
||||
# SQLite doesn't support ALTER TABLE ADD CONSTRAINT
|
||||
op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id'])
|
||||
|
||||
|
||||
def upgrade_2(session, metadata):
|
||||
@ -75,12 +71,9 @@ def upgrade_2(session, metadata):
|
||||
|
||||
This upgrade adds a create_date and last_modified date to the songs table
|
||||
"""
|
||||
try:
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
|
||||
op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
|
||||
except OperationalError:
|
||||
log.info('Upgrade 2 has already been run')
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column('songs', Column('create_date', types.DateTime(), default=func.now()))
|
||||
op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now()))
|
||||
|
||||
|
||||
def upgrade_3(session, metadata):
|
||||
@ -89,14 +82,11 @@ def upgrade_3(session, metadata):
|
||||
|
||||
This upgrade adds a temporary song flag to the songs table
|
||||
"""
|
||||
try:
|
||||
op = get_upgrade_op(session)
|
||||
if metadata.bind.url.get_dialect().name == 'sqlite':
|
||||
op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
|
||||
else:
|
||||
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
|
||||
except OperationalError:
|
||||
log.info('Upgrade 3 has already been run')
|
||||
op = get_upgrade_op(session)
|
||||
if metadata.bind.url.get_dialect().name == 'sqlite':
|
||||
op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false()))
|
||||
else:
|
||||
op.add_column('songs', Column('temporary', types.Boolean(), server_default=false()))
|
||||
|
||||
|
||||
def upgrade_4(session, metadata):
|
||||
@ -105,17 +95,14 @@ def upgrade_4(session, metadata):
|
||||
|
||||
This upgrade adds a column for author type to the authors_songs table
|
||||
"""
|
||||
try:
|
||||
# Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
|
||||
# and copy the old values
|
||||
op = get_upgrade_op(session)
|
||||
op.create_table('authors_songs_tmp',
|
||||
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||
Column('author_type', types.String(), primary_key=True,
|
||||
nullable=False, server_default=text('""')))
|
||||
op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
|
||||
op.drop_table('authors_songs')
|
||||
op.rename_table('authors_songs_tmp', 'authors_songs')
|
||||
except OperationalError:
|
||||
log.info('Upgrade 4 has already been run')
|
||||
# Since SQLite doesn't support changing the primary key of a table, we need to recreate the table
|
||||
# and copy the old values
|
||||
op = get_upgrade_op(session)
|
||||
op.create_table('authors_songs_tmp',
|
||||
Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True),
|
||||
Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True),
|
||||
Column('author_type', types.String(), primary_key=True,
|
||||
nullable=False, server_default=text('""')))
|
||||
op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs')
|
||||
op.drop_table('authors_songs')
|
||||
op.rename_table('authors_songs_tmp', 'authors_songs')
|
||||
|
@ -32,7 +32,6 @@ backend for the SongsUsage plugin
|
||||
"""
|
||||
import logging
|
||||
|
||||
from sqlalchemy.exc import OperationalError
|
||||
from sqlalchemy import Column, types
|
||||
|
||||
from openlp.core.lib.db import get_upgrade_op
|
||||
@ -50,9 +49,6 @@ def upgrade_1(session, metadata):
|
||||
:param session: SQLAlchemy Session object
|
||||
:param metadata: SQLAlchemy MetaData object
|
||||
"""
|
||||
try:
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
|
||||
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
|
||||
except OperationalError:
|
||||
log.info('Upgrade 1 has already taken place')
|
||||
op = get_upgrade_op(session)
|
||||
op.add_column('songusage_data', Column('plugin_name', types.Unicode(20), server_default=''))
|
||||
op.add_column('songusage_data', Column('source', types.Unicode(10), server_default=''))
|
||||
|
@ -116,7 +116,9 @@ class JenkinsTrigger(object):
|
||||
print('%s (revision %s)' % (get_repo_name(), revno))
|
||||
|
||||
for job in OpenLPJobs.Jobs:
|
||||
self.__print_build_info(job)
|
||||
if not self.__print_build_info(job):
|
||||
print('Stopping after failure')
|
||||
break
|
||||
|
||||
def open_browser(self):
|
||||
"""
|
||||
@ -133,6 +135,7 @@ class JenkinsTrigger(object):
|
||||
:param job_name: The name of the job we want the information from. For example *Branch-01-Pull*. Use the class
|
||||
variables from the :class:`OpenLPJobs` class.
|
||||
"""
|
||||
is_success = False
|
||||
job = self.jenkins_instance.job(job_name)
|
||||
while job.info['inQueue']:
|
||||
time.sleep(1)
|
||||
@ -141,11 +144,13 @@ class JenkinsTrigger(object):
|
||||
if build.info['result'] == 'SUCCESS':
|
||||
# Make 'SUCCESS' green.
|
||||
result_string = '%s%s%s' % (Colour.GREEN_START, build.info['result'], Colour.GREEN_END)
|
||||
is_success = True
|
||||
else:
|
||||
# Make 'FAILURE' red.
|
||||
result_string = '%s%s%s' % (Colour.RED_START, build.info['result'], Colour.RED_END)
|
||||
url = build.info['url']
|
||||
print('[%s] %s' % (result_string, url))
|
||||
return is_success
|
||||
|
||||
|
||||
def get_repo_name():
|
||||
|
@ -6,11 +6,12 @@ from unittest import TestCase
|
||||
|
||||
from PyQt4 import QtCore
|
||||
|
||||
from openlp.core.common import Settings
|
||||
from openlp.core.lib.htmlbuilder import build_html, build_background_css, build_lyrics_css, build_lyrics_outline_css, \
|
||||
build_lyrics_format_css, build_footer_css
|
||||
from openlp.core.lib.theme import HorizontalType, VerticalType
|
||||
from tests.functional import MagicMock, patch
|
||||
|
||||
from tests.helpers.testmixin import TestMixin
|
||||
|
||||
HTML = """
|
||||
<!DOCTYPE html>
|
||||
@ -184,7 +185,7 @@ LYRICS_OUTLINE_CSS = ' -webkit-text-stroke: 0.125em #000000; -webkit-text-fill-c
|
||||
LYRICS_FORMAT_CSS = ' word-wrap: break-word; text-align: justify; vertical-align: bottom; ' + \
|
||||
'font-family: Arial; font-size: 40pt; color: #FFFFFF; line-height: 108%; margin: 0;padding: 0; ' + \
|
||||
'padding-bottom: 0.5em; padding-left: 2px; width: 1580px; height: 810px; font-style:italic; font-weight:bold; '
|
||||
FOOTER_CSS = """
|
||||
FOOTER_CSS_BASE = """
|
||||
left: 10px;
|
||||
bottom: 0px;
|
||||
width: 1260px;
|
||||
@ -192,11 +193,28 @@ FOOTER_CSS = """
|
||||
font-size: 12pt;
|
||||
color: #FFFFFF;
|
||||
text-align: left;
|
||||
white-space: nowrap;
|
||||
white-space: %s;
|
||||
"""
|
||||
FOOTER_CSS = FOOTER_CSS_BASE % ('nowrap')
|
||||
FOOTER_CSS_WRAP = FOOTER_CSS_BASE % ('normal')
|
||||
|
||||
|
||||
class Htmbuilder(TestCase):
|
||||
class Htmbuilder(TestCase, TestMixin):
|
||||
"""
|
||||
Test the functions in the Htmlbuilder module
|
||||
"""
|
||||
def setUp(self):
|
||||
"""
|
||||
Create the UI
|
||||
"""
|
||||
self.build_settings()
|
||||
|
||||
def tearDown(self):
|
||||
"""
|
||||
Delete all the C++ objects at the end so that we don't have a segfault
|
||||
"""
|
||||
self.destroy_settings()
|
||||
|
||||
def build_html_test(self):
|
||||
"""
|
||||
Test the build_html() function
|
||||
@ -225,7 +243,7 @@ class Htmbuilder(TestCase):
|
||||
html = build_html(item, screen, is_live, background, plugins=plugins)
|
||||
|
||||
# THEN: The returned html should match.
|
||||
assert html == HTML
|
||||
self.assertEqual(html, HTML, 'The returned html should match')
|
||||
|
||||
def build_background_css_radial_test(self):
|
||||
"""
|
||||
@ -241,7 +259,7 @@ class Htmbuilder(TestCase):
|
||||
css = build_background_css(item, width)
|
||||
|
||||
# THEN: The returned css should match.
|
||||
assert BACKGROUND_CSS_RADIAL == css, 'The background css should be equal.'
|
||||
self.assertEqual(BACKGROUND_CSS_RADIAL, css, 'The background css should be equal.')
|
||||
|
||||
def build_lyrics_css_test(self):
|
||||
"""
|
||||
@ -262,7 +280,7 @@ class Htmbuilder(TestCase):
|
||||
css = build_lyrics_css(item)
|
||||
|
||||
# THEN: The css should be equal.
|
||||
assert LYRICS_CSS == css, 'The lyrics css should be equal.'
|
||||
self.assertEqual(LYRICS_CSS, css, 'The lyrics css should be equal.')
|
||||
|
||||
def build_lyrics_outline_css_test(self):
|
||||
"""
|
||||
@ -279,7 +297,7 @@ class Htmbuilder(TestCase):
|
||||
css = build_lyrics_outline_css(theme_data)
|
||||
|
||||
# THEN: The css should be equal.
|
||||
assert LYRICS_OUTLINE_CSS == css, 'The outline css should be equal.'
|
||||
self.assertEqual(LYRICS_OUTLINE_CSS, css, 'The outline css should be equal.')
|
||||
|
||||
def build_lyrics_format_css_test(self):
|
||||
"""
|
||||
@ -302,7 +320,7 @@ class Htmbuilder(TestCase):
|
||||
css = build_lyrics_format_css(theme_data, width, height)
|
||||
|
||||
# THEN: They should be equal.
|
||||
assert LYRICS_FORMAT_CSS == css, 'The lyrics format css should be equal.'
|
||||
self.assertEqual(LYRICS_FORMAT_CSS, css, 'The lyrics format css should be equal.')
|
||||
|
||||
def build_footer_css_test(self):
|
||||
"""
|
||||
@ -316,8 +334,27 @@ class Htmbuilder(TestCase):
|
||||
item.theme_data.font_footer_color = '#FFFFFF'
|
||||
height = 1024
|
||||
|
||||
# WHEN: create the css.
|
||||
# WHEN: create the css with default settings.
|
||||
css = build_footer_css(item, height)
|
||||
|
||||
# THEN: THE css should be the same.
|
||||
assert FOOTER_CSS == css, 'The footer strings should be equal.'
|
||||
self.assertEqual(FOOTER_CSS, css, 'The footer strings should be equal.')
|
||||
|
||||
def build_footer_css_wrap_test(self):
|
||||
"""
|
||||
Test the build_footer_css() function
|
||||
"""
|
||||
# GIVEN: Create a theme.
|
||||
item = MagicMock()
|
||||
item.footer = QtCore.QRect(10, 921, 1260, 103)
|
||||
item.theme_data.font_footer_name = 'Arial'
|
||||
item.theme_data.font_footer_size = 12
|
||||
item.theme_data.font_footer_color = '#FFFFFF'
|
||||
height = 1024
|
||||
|
||||
# WHEN: Settings say that footer should wrap
|
||||
Settings().setValue('themes/wrap footer', True)
|
||||
css = build_footer_css(item, height)
|
||||
|
||||
# THEN: Footer should wrap
|
||||
self.assertEqual(FOOTER_CSS_WRAP, css, 'The footer strings should be equal.')
|
||||
|
@ -504,21 +504,20 @@ class TestSlideController(TestCase):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.is_command.return_value = True
|
||||
mocked_item.name = 'Mocked Item'
|
||||
mocked_execute = MagicMock()
|
||||
mocked_update_preview = MagicMock()
|
||||
mocked_preview_widget = MagicMock()
|
||||
mocked_slide_selected = MagicMock()
|
||||
Registry.execute = mocked_execute
|
||||
Registry.create()
|
||||
slide_controller = SlideController(None)
|
||||
slide_controller.service_item = mocked_item
|
||||
slide_controller.update_preview = mocked_update_preview
|
||||
slide_controller.preview_widget = mocked_preview_widget
|
||||
slide_controller.slide_selected = mocked_slide_selected
|
||||
slide_controller.is_live = True
|
||||
with patch.object(Registry, 'execute') as mocked_execute:
|
||||
Registry.create()
|
||||
slide_controller = SlideController(None)
|
||||
slide_controller.service_item = mocked_item
|
||||
slide_controller.update_preview = mocked_update_preview
|
||||
slide_controller.preview_widget = mocked_preview_widget
|
||||
slide_controller.slide_selected = mocked_slide_selected
|
||||
slide_controller.is_live = True
|
||||
|
||||
# WHEN: The method is called
|
||||
slide_controller.on_slide_selected_index([9])
|
||||
# WHEN: The method is called
|
||||
slide_controller.on_slide_selected_index([9])
|
||||
|
||||
# THEN: It should have sent a notification
|
||||
mocked_item.is_command.assert_called_once_with()
|
||||
@ -535,20 +534,19 @@ class TestSlideController(TestCase):
|
||||
mocked_item = MagicMock()
|
||||
mocked_item.is_command.return_value = False
|
||||
mocked_item.name = 'Mocked Item'
|
||||
mocked_execute = MagicMock()
|
||||
mocked_update_preview = MagicMock()
|
||||
mocked_preview_widget = MagicMock()
|
||||
mocked_slide_selected = MagicMock()
|
||||
Registry.execute = mocked_execute
|
||||
Registry.create()
|
||||
slide_controller = SlideController(None)
|
||||
slide_controller.service_item = mocked_item
|
||||
slide_controller.update_preview = mocked_update_preview
|
||||
slide_controller.preview_widget = mocked_preview_widget
|
||||
slide_controller.slide_selected = mocked_slide_selected
|
||||
with patch.object(Registry, 'execute') as mocked_execute:
|
||||
Registry.create()
|
||||
slide_controller = SlideController(None)
|
||||
slide_controller.service_item = mocked_item
|
||||
slide_controller.update_preview = mocked_update_preview
|
||||
slide_controller.preview_widget = mocked_preview_widget
|
||||
slide_controller.slide_selected = mocked_slide_selected
|
||||
|
||||
# WHEN: The method is called
|
||||
slide_controller.on_slide_selected_index([7])
|
||||
# WHEN: The method is called
|
||||
slide_controller.on_slide_selected_index([7])
|
||||
|
||||
# THEN: It should have sent a notification
|
||||
mocked_item.is_command.assert_called_once_with()
|
||||
|
@ -27,14 +27,16 @@
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
Package to test the openlp.core.ui.thememanager package.
|
||||
"""
|
||||
import zipfile
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
from tests.interfaces import MagicMock
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.ui import ThemeManager
|
||||
from openlp.core.common import Registry
|
||||
|
||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||
from tests.interfaces import MagicMock, patch
|
||||
@ -48,6 +50,25 @@ class TestThemeManager(TestCase):
|
||||
"""
|
||||
Registry.create()
|
||||
|
||||
def export_theme_test(self):
|
||||
"""
|
||||
Test exporting a theme .
|
||||
"""
|
||||
# GIVEN: A new ThemeManager instance.
|
||||
theme_manager = ThemeManager()
|
||||
theme_manager.path = os.path.join(TEST_RESOURCES_PATH, 'themes')
|
||||
zipfile.ZipFile.__init__ = MagicMock()
|
||||
zipfile.ZipFile.__init__.return_value = None
|
||||
zipfile.ZipFile.write = MagicMock()
|
||||
|
||||
# WHEN: The theme is exported
|
||||
theme_manager._export_theme('/some/path', 'Default')
|
||||
|
||||
# THEN: The zipfile should be created at the given path
|
||||
zipfile.ZipFile.__init__.assert_called_with('/some/path/Default.otz', 'w')
|
||||
zipfile.ZipFile.write.assert_called_with(os.path.join(TEST_RESOURCES_PATH, 'themes', 'Default', 'Default.xml'),
|
||||
'Default/Default.xml')
|
||||
|
||||
def initial_theme_manager_test(self):
|
||||
"""
|
||||
Test the instantiation of theme manager.
|
||||
|
115
tests/functional/openlp_core_ui/test_thememanager.py.moved
Normal file
115
tests/functional/openlp_core_ui/test_thememanager.py.moved
Normal file
@ -0,0 +1,115 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
Package to test the openlp.core.ui.slidecontroller package.
|
||||
"""
|
||||
import os
|
||||
|
||||
from unittest import TestCase
|
||||
|
||||
from openlp.core.common import Registry
|
||||
from openlp.core.ui import ThemeManager
|
||||
|
||||
from tests.utils.constants import TEST_RESOURCES_PATH
|
||||
from tests.interfaces import MagicMock, patch
|
||||
|
||||
|
||||
class TestThemeManager(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Set up the tests
|
||||
"""
|
||||
Registry.create()
|
||||
|
||||
def initial_theme_manager_test(self):
|
||||
"""
|
||||
Test the instantiation of theme manager.
|
||||
"""
|
||||
# GIVEN: A new service manager instance.
|
||||
ThemeManager(None)
|
||||
|
||||
# WHEN: the default theme manager is built.
|
||||
# THEN: The the controller should be registered in the registry.
|
||||
self.assertIsNotNone(Registry().get('theme_manager'), 'The base theme manager should be registered')
|
||||
|
||||
def write_theme_same_image_test(self):
|
||||
"""
|
||||
Test that we don't try to overwrite a theme background image with itself
|
||||
"""
|
||||
# GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
|
||||
# theme, check_directory_exists and thememanager-attributes.
|
||||
with patch('builtins.open') as mocked_open, \
|
||||
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
|
||||
patch('openlp.core.ui.thememanager.check_directory_exists') as mocked_check_directory_exists:
|
||||
mocked_open.return_value = MagicMock()
|
||||
theme_manager = ThemeManager(None)
|
||||
theme_manager.old_background_image = None
|
||||
theme_manager.generate_and_save_image = MagicMock()
|
||||
theme_manager.path = ''
|
||||
mocked_theme = MagicMock()
|
||||
mocked_theme.theme_name = 'themename'
|
||||
mocked_theme.extract_formatted_xml = MagicMock()
|
||||
mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
|
||||
|
||||
# WHEN: Calling _write_theme with path to the same image, but the path written slightly different
|
||||
file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
|
||||
# Do replacement from end of string to avoid problems with path start
|
||||
file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1]
|
||||
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
|
||||
|
||||
# THEN: The mocked_copyfile should not have been called
|
||||
self.assertFalse(mocked_copyfile.called, 'shutil.copyfile should not be called')
|
||||
|
||||
def write_theme_diff_images_test(self):
|
||||
"""
|
||||
Test that we do overwrite a theme background image when a new is submitted
|
||||
"""
|
||||
# GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,
|
||||
# theme, check_directory_exists and thememanager-attributes.
|
||||
with patch('builtins.open') as mocked_open, \
|
||||
patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
|
||||
patch('openlp.core.ui.thememanager.check_directory_exists') as mocked_check_directory_exists:
|
||||
mocked_open.return_value = MagicMock()
|
||||
theme_manager = ThemeManager(None)
|
||||
theme_manager.old_background_image = None
|
||||
theme_manager.generate_and_save_image = MagicMock()
|
||||
theme_manager.path = ''
|
||||
mocked_theme = MagicMock()
|
||||
mocked_theme.theme_name = 'themename'
|
||||
mocked_theme.extract_formatted_xml = MagicMock()
|
||||
mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
|
||||
|
||||
# WHEN: Calling _write_theme with path to different images
|
||||
file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')
|
||||
file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg')
|
||||
theme_manager._write_theme(mocked_theme, file_name1, file_name2)
|
||||
|
||||
# THEN: The mocked_copyfile should not have been called
|
||||
self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called')
|
@ -112,3 +112,44 @@ class TestDB(TestCase):
|
||||
# THEN: It should have been removed and the other author should still be there
|
||||
self.assertEqual(1, len(song.authors_songs))
|
||||
self.assertEqual(None, song.authors_songs[0].author_type)
|
||||
|
||||
def test_get_author_type_from_translated_text(self):
|
||||
"""
|
||||
Test getting an author type from translated text
|
||||
"""
|
||||
# GIVEN: A string with an author type
|
||||
author_type_name = AuthorType.Types[AuthorType.Words]
|
||||
|
||||
# WHEN: We call the method
|
||||
author_type = AuthorType.from_translated_text(author_type_name)
|
||||
|
||||
# THEN: The type should be correct
|
||||
self.assertEqual(author_type, AuthorType.Words)
|
||||
|
||||
def test_author_get_display_name(self):
|
||||
"""
|
||||
Test that the display name of an author is correct
|
||||
"""
|
||||
# GIVEN: An author
|
||||
author = Author()
|
||||
author.display_name = "John Doe"
|
||||
|
||||
# WHEN: We call the get_display_name() function
|
||||
display_name = author.get_display_name()
|
||||
|
||||
# THEN: It should return only the name
|
||||
self.assertEqual("John Doe", display_name)
|
||||
|
||||
def test_author_get_display_name_with_type(self):
|
||||
"""
|
||||
Test that the display name of an author with a type is correct
|
||||
"""
|
||||
# GIVEN: An author
|
||||
author = Author()
|
||||
author.display_name = "John Doe"
|
||||
|
||||
# WHEN: We call the get_display_name() function
|
||||
display_name = author.get_display_name(AuthorType.Words)
|
||||
|
||||
# THEN: It should return the name with the type in brackets
|
||||
self.assertEqual("John Doe (Words)", display_name)
|
||||
|
@ -0,0 +1,56 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2013 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2013 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
The :mod:`powerpraiseimport` module provides the functionality for importing
|
||||
ProPresenter song files into the current installation database.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from tests.helpers.songfileimport import SongImportTestHelper
|
||||
|
||||
TEST_PATH = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'powerpraisesongs'))
|
||||
|
||||
|
||||
class TestPowerPraiseFileImport(SongImportTestHelper):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.importer_class_name = 'PowerPraiseImport'
|
||||
self.importer_module_name = 'powerpraise'
|
||||
super(TestPowerPraiseFileImport, self).__init__(*args, **kwargs)
|
||||
|
||||
def test_song_import(self):
|
||||
"""
|
||||
Test that loading a PowerPraise file works correctly
|
||||
"""
|
||||
self.file_import([os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.ppl')],
|
||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Näher, mein Gott zu Dir.json')))
|
||||
self.file_import([os.path.join(TEST_PATH, 'You are so faithful.ppl')],
|
||||
self.load_external_result_data(os.path.join(TEST_PATH, 'You are so faithful.json')))
|
@ -0,0 +1,53 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# vim: autoindent shiftwidth=4 expandtab textwidth=120 tabstop=4 softtabstop=4
|
||||
|
||||
###############################################################################
|
||||
# OpenLP - Open Source Lyrics Projection #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Copyright (c) 2008-2014 Raoul Snyman #
|
||||
# Portions copyright (c) 2008-2014 Tim Bentley, Gerald Britton, Jonathan #
|
||||
# Corwin, Samuel Findlay, Michael Gorven, Scott Guerrieri, Matthias Hub, #
|
||||
# Meinert Jordan, Armin Köhler, Erik Lundin, Edwin Lunando, Brian T. Meyer. #
|
||||
# Joshua Miller, Stevan Pettit, Andreas Preikschat, Mattias Põldaru, #
|
||||
# Christian Richter, Philip Ridout, Simon Scudder, Jeffrey Smith, #
|
||||
# Maikel Stuivenberg, Martin Thompson, Jon Tibble, Dave Warnock, #
|
||||
# Frode Woldsund, Martin Zibricky, Patrick Zimmermann #
|
||||
# --------------------------------------------------------------------------- #
|
||||
# This program is free software; you can redistribute it and/or modify it #
|
||||
# under the terms of the GNU General Public License as published by the Free #
|
||||
# Software Foundation; version 2 of the License. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, but WITHOUT #
|
||||
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or #
|
||||
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for #
|
||||
# more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License along #
|
||||
# with this program; if not, write to the Free Software Foundation, Inc., 59 #
|
||||
# Temple Place, Suite 330, Boston, MA 02111-1307 USA #
|
||||
###############################################################################
|
||||
"""
|
||||
This module contains tests for the PresentationManager song importer.
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from tests.helpers.songfileimport import SongImportTestHelper
|
||||
|
||||
TEST_PATH = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), '..', '..', '..', 'resources', 'presentationmanagersongs'))
|
||||
|
||||
|
||||
class TestSongShowPlusFileImport(SongImportTestHelper):
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.importer_class_name = 'PresentationManagerImport'
|
||||
self.importer_module_name = 'presentationmanager'
|
||||
super(TestSongShowPlusFileImport, self).__init__(*args, **kwargs)
|
||||
|
||||
def test_song_import(self):
|
||||
"""
|
||||
Test that loading a PresentationManager file works correctly
|
||||
"""
|
||||
self.file_import([os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.sng')],
|
||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Great Is Thy Faithfulness.json')))
|
@ -48,7 +48,7 @@ class TestProPresenterFileImport(SongImportTestHelper):
|
||||
|
||||
def test_song_import(self):
|
||||
"""
|
||||
Test that loading an ProPresenter file works correctly
|
||||
Test that loading a ProPresenter file works correctly
|
||||
"""
|
||||
self.file_import([os.path.join(TEST_PATH, 'Amazing Grace.pro4')],
|
||||
self.load_external_result_data(os.path.join(TEST_PATH, 'Amazing Grace.json')))
|
||||
|
@ -31,10 +31,13 @@ The :mod:`songfileimporthelper` modules provides a helper class and methods to e
|
||||
song files from third party applications.
|
||||
"""
|
||||
import json
|
||||
import logging
|
||||
from unittest import TestCase
|
||||
|
||||
from tests.functional import patch, MagicMock, call
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SongImportTestHelper(TestCase):
|
||||
"""
|
||||
@ -108,9 +111,21 @@ class SongImportTestHelper(TestCase):
|
||||
topics = self._get_data(result_data, 'topics')
|
||||
verse_order_list = self._get_data(result_data, 'verse_order_list')
|
||||
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been
|
||||
# called.
|
||||
# THEN: do_import should return none, the song data should be as expected, and finish should have been called.
|
||||
self.assertIsNone(importer.do_import(), 'do_import should return None when it has completed')
|
||||
|
||||
# Debug information - will be displayed when the test fails
|
||||
log.debug("Title imported: %s" % importer.title)
|
||||
log.debug("Verses imported: %s" % self.mocked_add_verse.mock_calls)
|
||||
log.debug("Verse order imported: %s" % importer.verse_order_list)
|
||||
log.debug("Authors imported: %s" % self.mocked_add_author.mock_calls)
|
||||
log.debug("CCLI No. imported: %s" % importer.ccli_number)
|
||||
log.debug("Comments imported: %s" % importer.comments)
|
||||
log.debug("Songbook imported: %s" % importer.song_book_name)
|
||||
log.debug("Song number imported: %s" % importer.song_number)
|
||||
log.debug("Song copyright imported: %s" % importer.song_number)
|
||||
log.debug("Topics imported: %s" % importer.topics)
|
||||
|
||||
self.assertEqual(importer.title, title, 'title for %s should be "%s"' % (source_file_name, title))
|
||||
for author in author_calls:
|
||||
self.mocked_add_author.assert_any_call(author)
|
||||
|
@ -0,0 +1,18 @@
|
||||
{
|
||||
"title": "Näher, mein Gott, zu Dir",
|
||||
"verse_order_list": ["v1", "v2", "v3"],
|
||||
"verses": [
|
||||
[
|
||||
"Näher, mein Gott, zu Dir,\nsei meine Bitt'!\nNäher, o Herr, zu Dir\nmit jedem Schritt.\nNur an dem Herzen Dein\nkann ich geborgen sein;\ndeshalb die Bitte mein:\nNäher zu Dir!",
|
||||
"v1"
|
||||
],
|
||||
[
|
||||
"Näher, mein Gott, zu Dir!\nEin jeder Tag\nsoll es neu zeigen mir,\nwas er vermag:\nWie seiner Gnade Macht,\nErlösung hat gebracht,\nin uns're Sündennacht.\nNäher zu Dir!",
|
||||
"v2"
|
||||
],
|
||||
[
|
||||
"Näher, mein Gott, zu Dir!\nDich bet' ich an.\nWie vieles hast an mir,\nDu doch getan!\nVon Banden frei und los,\nruh' ich in Deinem Schoss.\nJa, Deine Gnad' ist gross!\nNäher zu Dir!",
|
||||
"v3"
|
||||
]
|
||||
]
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<ppl version="3.0"><general><title>Näher, mein Gott, zu Dir</title><category>Anbetung</category><language>Deutsch</language></general><songtext><part caption="Teil 1"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir,</line><line>sei meine Bitt'!</line><line>Näher, o Herr, zu Dir</line><line>mit jedem Schritt.</line></slide><slide mainsize="44" backgroundnr="0"><line>Nur an dem Herzen Dein</line><line>kann ich geborgen sein;</line><line>deshalb die Bitte mein:</line><line>Näher zu Dir!</line></slide></part><part caption="Teil 2"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir!</line><line>Ein jeder Tag</line><line>soll es neu zeigen mir,</line><line>was er vermag:</line></slide><slide mainsize="42" backgroundnr="0"><line>Wie seiner Gnade Macht,</line><line>Erlösung hat gebracht,</line><line>in uns're Sündennacht.</line><line>Näher zu Dir!</line></slide></part><part caption="Teil 3"><slide mainsize="42" backgroundnr="0"><line>Näher, mein Gott, zu Dir!</line><line>Dich bet' ich an.</line><line>Wie vieles hast an mir,</line><line>Du doch getan!</line></slide><slide mainsize="42" backgroundnr="0"><line>Von Banden frei und los,</line><line>ruh' ich in Deinem Schoss.</line><line>Ja, Deine Gnad' ist gross!</line><line>Näher zu Dir!</line></slide></part></songtext><order><item>Teil 1</item><item>Teil 2</item><item>Teil 3</item></order><information><copyright><position>lastslide</position><text><line>Text und Musik: Lowell Mason, 1792-1872</line></text></copyright><source><position>firstslide</position><text><line>grünes Buch 339</line></text></source></information><formatting><font><maintext><name>Times New Roman</name><size>44</size><bold>true</bold><italic>true</italic><color>16777215</color><outline>30</outline><shadow>15</shadow></maintext><translationtext><name>Times New Roman</name><size>20</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></translationtext><copyrighttext><name>Times New Roman</name><size>14</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></copyrighttext><sourcetext><name>Times New Roman</name><size>30</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></sourcetext><outline><enabled>false</enabled><color>0</color></outline><shadow><enabled>true</enabled><color>0</color><direction>125</direction></shadow></font><background><file>Blumen\Blume 3.jpg</file></background><linespacing><main>30</main><translation>20</translation></linespacing><textorientation><horizontal>left</horizontal><vertical>center</vertical><transpos>inline</transpos></textorientation><borders><mainleft>50</mainleft><maintop>40</maintop><mainright>60</mainright><mainbottom>70</mainbottom><copyrightbottom>30</copyrightbottom><sourcetop>20</sourcetop><sourceright>40</sourceright></borders></formatting></ppl>
|
26
tests/resources/powerpraisesongs/You are so faithful.json
Normal file
26
tests/resources/powerpraisesongs/You are so faithful.json
Normal file
@ -0,0 +1,26 @@
|
||||
{
|
||||
"title": "You are so faithful",
|
||||
"verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1", "v4"],
|
||||
"verses": [
|
||||
[
|
||||
"You are so faithful\nso faithful, so faithful.\nYou are so faithful\nso faithful, so faithful.",
|
||||
"v1"
|
||||
],
|
||||
[
|
||||
"That's why I praise you\nin the morning\nThat's why I praise you\nin the noontime.\nThat's why I praise you\nin the evening\nThat's why I praise you\nall the time.",
|
||||
"c1"
|
||||
],
|
||||
[
|
||||
"You are so loving\nso loving, so loving.\nYou are so loving\nso loving, so loving.",
|
||||
"v2"
|
||||
],
|
||||
[
|
||||
"You are so caring\nso caring, so caring.\nYou are so caring\nso caring, so caring.",
|
||||
"v3"
|
||||
],
|
||||
[
|
||||
"You are so mighty\nso mighty, so mighty.\nYou are so mighty\nso mighty, so mighty.",
|
||||
"v4"
|
||||
]
|
||||
]
|
||||
}
|
2
tests/resources/powerpraisesongs/You are so faithful.ppl
Normal file
2
tests/resources/powerpraisesongs/You are so faithful.ppl
Normal file
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="ISO-8859-1"?>
|
||||
<ppl version="3.0"><general><title>You are so faithful</title><category>Lobpreis</category><language>Englisch</language></general><songtext><part caption="Strophe 1"><slide mainsize="30" backgroundnr="0"><line>You are so faithful</line><line>so faithful, so faithful.</line><translation>Du bist so treu</translation><translation>so treu, so treu.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so faithful</line><line>so faithful, so faithful.</line><translation>Du bist so treu</translation><translation>so treu, so treu.</translation></slide></part><part caption="Refrain"><slide mainsize="30" backgroundnr="0"><line>That's why I praise you</line><line>in the morning</line><line>That's why I praise you</line><line>in the noontime.</line><translation>Deshalb preise ich Dich</translation><translation>am Morgen</translation><translation>Deshalb preise ich Dich</translation><translation>am Mittag.</translation></slide><slide mainsize="30" backgroundnr="0"><line>That's why I praise you</line><line>in the evening</line><line>That's why I praise you</line><line>all the time.</line><translation>Deshalb preise ich Dich</translation><translation>am Abend</translation><translation>Deshalb preise ich Dich</translation><translation>allezeit.</translation></slide></part><part caption="Strophe 2"><slide mainsize="30" backgroundnr="0"><line>You are so loving</line><line>so loving, so loving.</line><translation>Du bist so liebevoll</translation><translation>so liebevoll, so liebevoll.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so loving</line><line>so loving, so loving.</line><translation>Du bist so liebevoll</translation><translation>so liebevoll, so liebevoll.</translation></slide></part><part caption="Strophe 3"><slide mainsize="30" backgroundnr="0"><line>You are so caring</line><line>so caring, so caring.</line><translation>Du sorgst so gut</translation><translation>Du kümmerst dich um uns.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so caring</line><line>so caring, so caring.</line><translation>Du sorgst so gut</translation><translation>Du kümmerst dich um uns.</translation></slide></part><part caption="Strophe 4"><slide mainsize="30" backgroundnr="0"><line>You are so mighty</line><line>so mighty, so mighty.</line><translation>Du bist so mächtig</translation><translation>so mächtig, so mächtig.</translation></slide><slide mainsize="30" backgroundnr="0"><line>You are so mighty</line><line>so mighty, so mighty.</line><translation>Du bist so mächtig</translation><translation>so mächtig, so mächtig.</translation></slide></part></songtext><order><item>Strophe 1</item><item>Refrain</item><item>Strophe 2</item><item>Refrain</item><item>Strophe 3</item><item>Refrain</item><item>Strophe 4</item></order><information><copyright><position>lastslide</position><text><line>Musik & Copyright unbekannt</line></text></copyright><source><position>firstslide</position><text/></source></information><formatting><font><maintext><name>Tahoma</name><size>30</size><bold>true</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></maintext><translationtext><name>Tahoma</name><size>20</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></translationtext><copyrighttext><name>Tahoma</name><size>14</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></copyrighttext><sourcetext><name>Tahoma</name><size>30</size><bold>false</bold><italic>false</italic><color>16777215</color><outline>30</outline><shadow>20</shadow></sourcetext><outline><enabled>true</enabled><color>0</color></outline><shadow><enabled>true</enabled><color>0</color><direction>125</direction></shadow></font><background><file>Blumen\Blume 6.jpg</file></background><linespacing><main>30</main><translation>20</translation></linespacing><textorientation><horizontal>center</horizontal><vertical>center</vertical><transpos>inline</transpos></textorientation><borders><mainleft>50</mainleft><maintop>40</maintop><mainright>60</mainright><mainbottom>70</mainbottom><copyrightbottom>30</copyrightbottom><sourcetop>20</sourcetop><sourceright>40</sourceright></borders></formatting></ppl>
|
@ -0,0 +1,25 @@
|
||||
{
|
||||
"title": "Great Is Thy Faithfulness",
|
||||
"authors": [
|
||||
"Thomas O. Chisholm (1866-1960)"
|
||||
],
|
||||
"verse_order_list": ["v1", "c1", "v2", "c1", "v3", "c1"],
|
||||
"verses": [
|
||||
[
|
||||
"\"Great is Thy faithfulness\", O God my Father.\nThere is no shadow of turning with Thee;\nThou changest not, Thy compassions they fail not,\nAs Thou hast been Thou forever shall be.",
|
||||
"v1"
|
||||
],
|
||||
[
|
||||
"Great is Thy faithfulness!\nGreat is Thy faithfulness!\nMorning by morning new mercies I see!\nAll I have needed Thy hand hath provided -\n\"Great is Thy faithfulness\", Lord, unto me!",
|
||||
"c1"
|
||||
],
|
||||
[
|
||||
"Summer and winter, and springtime and harvest,\nSun, moon, and stars in their courses above,\nJoin with all nature in manifold witness,\nTo Thy great faithfulness, mercy and love.",
|
||||
"v2"
|
||||
],
|
||||
[
|
||||
"Pardon for sin and a peace that endureth,\nThine own dear presence to cheer and to guide,\nStrength for today and bright hope for tomorrow,\nBlessings all mine, with ten thousand beside!",
|
||||
"v3"
|
||||
]
|
||||
]
|
||||
}
|
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<song xmlns="creativelifestyles/song">
|
||||
<attributes>
|
||||
<title>Great Is Thy Faithfulness</title>
|
||||
<author>Thomas O. Chisholm (1866-1960)</author>
|
||||
<copyright></copyright>
|
||||
<ccli_number></ccli_number>
|
||||
<comments></comments>
|
||||
</attributes>
|
||||
<verses>
|
||||
<verse id="Verse 1">
|
||||
"Great is Thy faithfulness", O God my Father.
|
||||
There is no shadow of turning with Thee;
|
||||
Thou changest not, Thy compassions they fail not,
|
||||
As Thou hast been Thou forever shall be.
|
||||
</verse>
|
||||
<verse id="Chorus">
|
||||
Great is Thy faithfulness!
|
||||
Great is Thy faithfulness!
|
||||
Morning by morning new mercies I see!
|
||||
All I have needed Thy hand hath provided -
|
||||
"Great is Thy faithfulness", Lord, unto me!
|
||||
</verse>
|
||||
<verse id="Verse 2">
|
||||
Summer and winter, and springtime and harvest,
|
||||
Sun, moon, and stars in their courses above,
|
||||
Join with all nature in manifold witness,
|
||||
To Thy great faithfulness, mercy and love.
|
||||
</verse>
|
||||
<verse id="Chorus">
|
||||
Great is Thy faithfulness!
|
||||
Great is Thy faithfulness!
|
||||
Morning by morning new mercies I see!
|
||||
All I have needed Thy hand hath provided -
|
||||
"Great is Thy faithfulness", Lord, unto me!
|
||||
</verse>
|
||||
<verse id="Verse 3">
|
||||
Pardon for sin and a peace that endureth,
|
||||
Thine own dear presence to cheer and to guide,
|
||||
Strength for today and bright hope for tomorrow,
|
||||
Blessings all mine, with ten thousand beside!
|
||||
</verse>
|
||||
<verse id="Chorus">
|
||||
Great is Thy faithfulness!
|
||||
Great is Thy faithfulness!
|
||||
Morning by morning new mercies I see!
|
||||
All I have needed Thy hand hath provided -
|
||||
"Great is Thy faithfulness", Lord, unto me!
|
||||
</verse>
|
||||
</verses>
|
||||
</song>
|
34
tests/resources/themes/Default/Default.xml
Normal file
34
tests/resources/themes/Default/Default.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<theme version="2.0">
|
||||
<name>Default</name>
|
||||
<background type="solid">
|
||||
<color>#000000</color>
|
||||
</background>
|
||||
<font type="main">
|
||||
<name>Arial</name>
|
||||
<color>#FFFFFF</color>
|
||||
<size>40</size>
|
||||
<bold>False</bold>
|
||||
<italics>False</italics>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location height="690" override="False" width="1004" x="10" y="10"/>
|
||||
<shadow shadowColor="#000000" shadowSize="5">True</shadow>
|
||||
<outline outlineColor="#000000" outlineSize="2">False</outline>
|
||||
</font>
|
||||
<font type="footer">
|
||||
<name>Arial</name>
|
||||
<color>#FFFFFF</color>
|
||||
<size>12</size>
|
||||
<bold>False</bold>
|
||||
<italics>False</italics>
|
||||
<line_adjustment>0</line_adjustment>
|
||||
<location height="78" override="False" width="1004" x="10" y="690"/>
|
||||
<shadow shadowColor="#000000" shadowSize="5">True</shadow>
|
||||
<outline outlineColor="#000000" outlineSize="2">False</outline>
|
||||
</font>
|
||||
<display>
|
||||
<horizontalAlign>0</horizontalAlign>
|
||||
<verticalAlign>0</verticalAlign>
|
||||
<slideTransition>False</slideTransition>
|
||||
</display>
|
||||
</theme>
|
Loading…
Reference in New Issue
Block a user