This commit is contained in:
Andreas Preikschat 2013-06-01 16:48:30 +02:00
commit 851c667c8d
39 changed files with 525 additions and 162 deletions

View File

@ -30,6 +30,7 @@
The :mod:`lib` module contains most of the components and libraries that make The :mod:`lib` module contains most of the components and libraries that make
OpenLP work. OpenLP work.
""" """
from __future__ import division
from distutils.version import LooseVersion from distutils.version import LooseVersion
import logging import logging
import os import os
@ -155,7 +156,7 @@ def build_icon(icon):
``icon`` ``icon``
The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file The icon to build. This can be a QIcon, a resource string in the form ``:/resource/file.png``, or a file
location like ``/path/to/file.png``. location like ``/path/to/file.png``. However, the **recommended** way is to specify a resource string.
""" """
button_icon = QtGui.QIcon() button_icon = QtGui.QIcon()
if isinstance(icon, QtGui.QIcon): if isinstance(icon, QtGui.QIcon):
@ -202,12 +203,13 @@ def create_thumb(image_path, thumb_path, return_icon=True, size=None):
States if an icon should be build and returned from the thumb. Defaults to ``True``. States if an icon should be build and returned from the thumb. Defaults to ``True``.
``size`` ``size``
Allows to state a own size to use. Defaults to ``None``, which means that a default height of 88 is used. Allows to state a own size (QtCore.QSize) to use. Defaults to ``None``, which means that a default height of 88
is used.
""" """
ext = os.path.splitext(thumb_path)[1].lower() ext = os.path.splitext(thumb_path)[1].lower()
reader = QtGui.QImageReader(image_path) reader = QtGui.QImageReader(image_path)
if size is None: if size is None:
ratio = float(reader.size().width()) / float(reader.size().height()) ratio = reader.size().width() / reader.size().height()
reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88)) reader.setScaledSize(QtCore.QSize(int(ratio * 88), 88))
else: else:
reader.setScaledSize(size) reader.setScaledSize(size)
@ -260,8 +262,8 @@ def resize_image(image_path, width, height, background=u'#000000'):
log.debug(u'resize_image - start') log.debug(u'resize_image - start')
reader = QtGui.QImageReader(image_path) reader = QtGui.QImageReader(image_path)
# The image's ratio. # The image's ratio.
image_ratio = float(reader.size().width()) / float(reader.size().height()) image_ratio = reader.size().width() / reader.size().height()
resize_ratio = float(width) / float(height) resize_ratio = width / height
# Figure out the size we want to resize the image to (keep aspect ratio). # Figure out the size we want to resize the image to (keep aspect ratio).
if image_ratio == resize_ratio: if image_ratio == resize_ratio:
size = QtCore.QSize(width, height) size = QtCore.QSize(width, height)
@ -282,7 +284,7 @@ def resize_image(image_path, width, height, background=u'#000000'):
new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied) new_image = QtGui.QImage(width, height, QtGui.QImage.Format_ARGB32_Premultiplied)
painter = QtGui.QPainter(new_image) painter = QtGui.QPainter(new_image)
painter.fillRect(new_image.rect(), QtGui.QColor(background)) painter.fillRect(new_image.rect(), QtGui.QColor(background))
painter.drawImage((width - real_width) / 2, (height - real_height) / 2, preview) painter.drawImage((width - real_width) // 2, (height - real_height) // 2, preview)
return new_image return new_image

View File

@ -30,6 +30,7 @@
""" """
Provide additional functionality required by OpenLP from the inherited QDockWidget. Provide additional functionality required by OpenLP from the inherited QDockWidget.
""" """
from __future__ import division
import logging import logging
from PyQt4 import QtGui from PyQt4 import QtGui
@ -55,7 +56,7 @@ class OpenLPDockWidget(QtGui.QDockWidget):
self.setWindowIcon(build_icon(icon)) self.setWindowIcon(build_icon(icon))
# Sort out the minimum width. # Sort out the minimum width.
screens = ScreenList() screens = ScreenList()
main_window_docbars = screens.current[u'size'].width() / 5 main_window_docbars = screens.current[u'size'].width() // 5
if main_window_docbars > 300: if main_window_docbars > 300:
self.setMinimumWidth(300) self.setMinimumWidth(300)
else: else:

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtWebKit from PyQt4 import QtWebKit
@ -276,7 +276,7 @@ def build_background_css(item, width):
``item`` ``item``
Service Item containing theme and location information Service Item containing theme and location information
""" """
width = int(width) / 2 width = int(width) // 2
theme = item.themedata theme = item.themedata
background = u'background-color: black' background = u'background-color: black'
if theme: if theme:

View File

@ -102,7 +102,6 @@ class MediaManagerItem(QtGui.QWidget):
self.setupUi() self.setupUi()
self.retranslateUi() self.retranslateUi()
self.auto_select_id = -1 self.auto_select_id = -1
Registry().register_function(u'%s_service_load' % self.plugin.name, self.service_load)
# Need to use event as called across threads and UI is updated # Need to use event as called across threads and UI is updated
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote) QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_go_live' % self.plugin.name), self.go_live_remote)
QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote) QtCore.QObject.connect(self, QtCore.SIGNAL(u'%s_add_to_service' % self.plugin.name), self.add_to_service_remote)
@ -585,12 +584,15 @@ class MediaManagerItem(QtGui.QWidget):
else: else:
return None return None
def service_load(self, message): def service_load(self, item):
""" """
Method to add processing when a service has been loaded and individual service items need to be processed by the Method to add processing when a service has been loaded and individual service items need to be processed by the
plugins. plugins.
``item``
The item to be processed and returned.
""" """
pass return item
def check_search_result(self): def check_search_result(self):
""" """

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtGui, QtCore, QtWebKit from PyQt4 import QtGui, QtCore, QtWebKit
@ -327,7 +327,7 @@ class Renderer(object):
screen_size = self.screens.current[u'size'] screen_size = self.screens.current[u'size']
self.width = screen_size.width() self.width = screen_size.width()
self.height = screen_size.height() self.height = screen_size.height()
self.screen_ratio = float(self.height) / float(self.width) self.screen_ratio = self.height / self.width
log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio)) log.debug(u'_calculate default %s, %f' % (screen_size, self.screen_ratio))
# 90% is start of footer # 90% is start of footer
self.footer_start = int(self.height * 0.90) self.footer_start = int(self.height * 0.90)
@ -546,15 +546,15 @@ class Renderer(object):
""" """
smallest_index = 0 smallest_index = 0
highest_index = len(html_list) - 1 highest_index = len(html_list) - 1
index = int(highest_index / 2) index = highest_index // 2
while True: while True:
if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()): if not self._text_fits_on_slide(previous_html + separator.join(html_list[:index + 1]).strip()):
# We know that it does not fit, so change/calculate the new index and highest_index accordingly. # We know that it does not fit, so change/calculate the new index and highest_index accordingly.
highest_index = index highest_index = index
index = int(index - (index - smallest_index) / 2) index = index - (index - smallest_index) // 2
else: else:
smallest_index = index smallest_index = index
index = int(index + (highest_index - index) / 2) index = index + (highest_index - index) // 2
# We found the number of words which will fit. # We found the number of words which will fit.
if smallest_index == index or highest_index == index: if smallest_index == index or highest_index == index:
index = smallest_index index = smallest_index
@ -582,7 +582,7 @@ class Renderer(object):
html_list[0] = html_tags + html_list[0] html_list[0] = html_tags + html_list[0]
smallest_index = 0 smallest_index = 0
highest_index = len(html_list) - 1 highest_index = len(html_list) - 1
index = int(highest_index / 2) index = highest_index // 2
return previous_html, previous_raw return previous_html, previous_raw
def _text_fits_on_slide(self, text): def _text_fits_on_slide(self, text):

View File

@ -30,6 +30,7 @@
The :mod:`screen` module provides management functionality for a machines' The :mod:`screen` module provides management functionality for a machines'
displays. displays.
""" """
from __future__ import division
import logging import logging
import copy import copy
@ -232,8 +233,8 @@ class ScreenList(object):
``window`` ``window``
A QWidget we are finding the location of. A QWidget we are finding the location of.
""" """
x = window.x() + (window.width() / 2) x = window.x() + (window.width() // 2)
y = window.y() + (window.height() / 2) y = window.y() + (window.height() // 2)
for screen in self.screen_list: for screen in self.screen_list:
size = screen[u'size'] size = screen[u'size']
if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()): if x >= size.x() and x <= (size.x() + size.width()) and y >= size.y() and y <= (size.y() + size.height()):

View File

@ -26,7 +26,7 @@
# with this program; if not, write to the Free Software Foundation, Inc., 59 # # with this program; if not, write to the Free Software Foundation, Inc., 59 #
# Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Temple Place, Suite 330, Boston, MA 02111-1307 USA #
############################################################################### ###############################################################################
from __future__ import division
import logging import logging
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
@ -85,10 +85,10 @@ class SearchEdit(QtGui.QLineEdit):
size = self.clear_button.size() size = self.clear_button.size()
frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth) frame_width = self.style().pixelMetric(QtGui.QStyle.PM_DefaultFrameWidth)
self.clear_button.move(self.rect().right() - frame_width - size.width(), self.clear_button.move(self.rect().right() - frame_width - size.width(),
(self.rect().bottom() + 1 - size.height()) / 2) (self.rect().bottom() + 1 - size.height()) // 2)
if hasattr(self, u'menu_button'): if hasattr(self, u'menu_button'):
size = self.menu_button.size() size = self.menu_button.size()
self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) / 2) self.menu_button.move(self.rect().left() + frame_width + 2, (self.rect().bottom() + 1 - size.height()) // 2)
def current_search_type(self): def current_search_type(self):
""" """

View File

@ -58,8 +58,7 @@ class ItemCapabilities(object):
Provides an enumeration of a service item's capabilities Provides an enumeration of a service item's capabilities
``CanPreview`` ``CanPreview``
The capability to allow the ServiceManager to add to the preview The capability to allow the ServiceManager to add to the preview tab when making the previous item live.
tab when making the previous item live.
``CanEdit`` ``CanEdit``
The capability to allow the ServiceManager to allow the item to be edited The capability to allow the ServiceManager to allow the item to be edited
@ -71,8 +70,7 @@ class ItemCapabilities(object):
Determines is the service_item needs a Media Player Determines is the service_item needs a Media Player
``CanLoop`` ``CanLoop``
The capability to allow the SlideController to allow the loop The capability to allow the SlideController to allow the loop processing.
processing.
``CanAppend`` ``CanAppend``
The capability to allow the ServiceManager to add leaves to the The capability to allow the ServiceManager to add leaves to the
@ -82,22 +80,19 @@ class ItemCapabilities(object):
The capability to remove lines breaks in the renderer The capability to remove lines breaks in the renderer
``OnLoadUpdate`` ``OnLoadUpdate``
The capability to update MediaManager when a service Item is The capability to update MediaManager when a service Item is loaded.
loaded.
``AddIfNewItem`` ``AddIfNewItem``
Not Used Not Used
``ProvidesOwnDisplay`` ``ProvidesOwnDisplay``
The capability to tell the SlideController the service Item has a The capability to tell the SlideController the service Item has a different display.
different display.
``HasDetailedTitleDisplay`` ``HasDetailedTitleDisplay``
ServiceItem provides a title Being Removed and decommissioned.
``HasVariableStartTime`` ``HasVariableStartTime``
The capability to tell the ServiceManager that a change to start The capability to tell the ServiceManager that a change to start time is possible.
time is possible.
``CanSoftBreak`` ``CanSoftBreak``
The capability to tell the renderer that Soft Break is allowed The capability to tell the renderer that Soft Break is allowed
@ -149,7 +144,7 @@ class ServiceItem(object):
if plugin: if plugin:
self.name = plugin.name self.name = plugin.name
self.title = u'' self.title = u''
self.shortname = u'' self.processor = None
self.audit = u'' self.audit = u''
self.items = [] self.items = []
self.iconic_representation = None self.iconic_representation = None
@ -353,7 +348,8 @@ class ServiceItem(object):
u'media_length': self.media_length, u'media_length': self.media_length,
u'background_audio': self.background_audio, u'background_audio': self.background_audio,
u'theme_overwritten': self.theme_overwritten, u'theme_overwritten': self.theme_overwritten,
u'will_auto_start': self.will_auto_start u'will_auto_start': self.will_auto_start,
u'processor': self.processor
} }
service_data = [] service_data = []
if self.service_item_type == ServiceItemType.Text: if self.service_item_type == ServiceItemType.Text:
@ -387,7 +383,6 @@ class ServiceItem(object):
self.title = header[u'title'] self.title = header[u'title']
self.name = header[u'name'] self.name = header[u'name']
self.service_item_type = header[u'type'] self.service_item_type = header[u'type']
self.shortname = header[u'plugin']
self.theme = header[u'theme'] self.theme = header[u'theme']
self.add_icon(header[u'icon']) self.add_icon(header[u'icon'])
self.raw_footer = header[u'footer'] self.raw_footer = header[u'footer']
@ -406,7 +401,13 @@ class ServiceItem(object):
self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False) self.auto_play_slides_loop = header.get(u'auto_play_slides_loop', False)
self.timed_slide_interval = header.get(u'timed_slide_interval', 0) self.timed_slide_interval = header.get(u'timed_slide_interval', 0)
self.will_auto_start = header.get(u'will_auto_start', False) self.will_auto_start = header.get(u'will_auto_start', False)
self.processor = header.get(u'processor', None)
self.has_original_files = True self.has_original_files = True
#TODO Remove me in 2,3 build phase
if self.is_capable(ItemCapabilities.HasDetailedTitleDisplay):
self.capabilities.remove(ItemCapabilities.HasDetailedTitleDisplay)
self.processor = self.title
self.title = None
if u'background_audio' in header: if u'background_audio' in header:
self.background_audio = [] self.background_audio = []
for filename in header[u'background_audio']: for filename in header[u'background_audio']:
@ -429,6 +430,8 @@ class ServiceItem(object):
self.add_from_image(text_image[u'path'], text_image[u'title'], background) self.add_from_image(text_image[u'path'], text_image[u'title'], background)
elif self.service_item_type == ServiceItemType.Command: elif self.service_item_type == ServiceItemType.Command:
for text_image in serviceitem[u'serviceitem'][u'data']: for text_image in serviceitem[u'serviceitem'][u'data']:
if not self.title:
self.title = text_image[u'title']
if path: if path:
self.has_original_files = False self.has_original_files = False
self.add_from_command(path, text_image[u'title'], text_image[u'image']) self.add_from_command(path, text_image[u'title'], text_image[u'image'])
@ -443,9 +446,7 @@ class ServiceItem(object):
if self.is_text(): if self.is_text():
return self.title return self.title
else: else:
if ItemCapabilities.HasDetailedTitleDisplay in self.capabilities: if len(self._raw_frames) > 1:
return self._raw_frames[0][u'title']
elif len(self._raw_frames) > 1:
return self.title return self.title
else: else:
return self._raw_frames[0][u'title'] return self._raw_frames[0][u'title']

View File

@ -30,6 +30,7 @@
The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their The :mod:`~openlp.core.lib.settingstab` module contains the base SettingsTab class which plugins use for adding their
own tab to the settings dialog. own tab to the settings dialog.
""" """
from __future__ import division
from PyQt4 import QtGui from PyQt4 import QtGui
@ -90,7 +91,7 @@ class SettingsTab(QtGui.QWidget):
QtGui.QWidget.resizeEvent(self, event) QtGui.QWidget.resizeEvent(self, event)
width = self.width() - self.tab_layout.spacing() - \ width = self.width() - self.tab_layout.spacing() - \
self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right() self.tab_layout.contentsMargins().left() - self.tab_layout.contentsMargins().right()
left_width = min(width - self.right_column.minimumSizeHint().width(), width / 2) left_width = min(width - self.right_column.minimumSizeHint().width(), width // 2)
left_width = max(left_width, self.left_column.minimumSizeHint().width()) left_width = max(left_width, self.left_column.minimumSizeHint().width())
self.left_column.setFixedWidth(left_width) self.left_column.setFixedWidth(left_width)

View File

@ -35,6 +35,7 @@ Some of the code for this form is based on the examples at:
* `http://html5demos.com/two-videos`_ * `http://html5demos.com/two-videos`_
""" """
from __future__ import division
import cgi import cgi
import logging import logging
import sys import sys
@ -207,8 +208,8 @@ class MainDisplay(Display):
painter_image.begin(self.initial_fame) painter_image.begin(self.initial_fame)
painter_image.fillRect(self.initial_fame.rect(), background_color) painter_image.fillRect(self.initial_fame.rect(), background_color)
painter_image.drawImage( painter_image.drawImage(
(self.screen[u'size'].width() - splash_image.width()) / 2, (self.screen[u'size'].width() - splash_image.width()) // 2,
(self.screen[u'size'].height() - splash_image.height()) / 2, (self.screen[u'size'].height() - splash_image.height()) // 2,
splash_image) splash_image)
service_item = ServiceItem() service_item = ServiceItem()
service_item.bg_image_bytes = image_to_byte(self.initial_fame) service_item.bg_image_bytes = image_to_byte(self.initial_fame)
@ -268,7 +269,7 @@ class MainDisplay(Display):
self.resize(self.width(), alert_height) self.resize(self.width(), alert_height)
self.setVisible(True) self.setVisible(True)
if location == AlertLocation.Middle: if location == AlertLocation.Middle:
self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) / 2) self.move(self.screen[u'size'].left(), (self.screen[u'size'].height() - alert_height) // 2)
elif location == AlertLocation.Bottom: elif location == AlertLocation.Bottom:
self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height) self.move(self.screen[u'size'].left(), self.screen[u'size'].height() - alert_height)
else: else:

View File

@ -466,8 +466,8 @@ class MediaController(object):
The ServiceItem containing the details to be played. The ServiceItem containing the details to be played.
""" """
used_players = get_media_players()[0] used_players = get_media_players()[0]
if service_item.title != UiStrings().Automatic: if service_item.processor != UiStrings().Automatic:
used_players = [service_item.title.lower()] used_players = [service_item.processor.lower()]
if controller.media_info.file_info.isFile(): if controller.media_info.file_info.isFile():
suffix = u'*.%s' % controller.media_info.file_info.suffix().lower() suffix = u'*.%s' % controller.media_info.file_info.suffix().lower()
for title in used_players: for title in used_players:

View File

@ -295,8 +295,8 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
Sets up the service manager, toolbars, list view, et al. Sets up the service manager, toolbars, list view, et al.
""" """
QtGui.QWidget.__init__(self, parent) QtGui.QWidget.__init__(self, parent)
self.active = build_icon(QtGui.QImage(u':/media/auto-start_active.png')) self.active = build_icon(u':/media/auto-start_active.png')
self.inactive = build_icon(QtGui.QImage(u':/media/auto-start_inactive.png')) self.inactive = build_icon(u':/media/auto-start_inactive.png')
Registry().register(u'service_manager', self) Registry().register(u'service_manager', self)
self.service_items = [] self.service_items = []
self.suffixes = [] self.suffixes = []
@ -715,13 +715,10 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
else: else:
service_item.set_from_service(item, self.servicePath) service_item.set_from_service(item, self.servicePath)
service_item.validate_item(self.suffixes) service_item.validate_item(self.suffixes)
self.load_item_unique_identifier = 0
if service_item.is_capable(ItemCapabilities.OnLoadUpdate): if service_item.is_capable(ItemCapabilities.OnLoadUpdate):
Registry().execute(u'%s_service_load' % service_item.name.lower(), service_item) new_item = Registry().get(service_item.name).service_load(service_item)
# if the item has been processed if new_item:
if service_item.unique_identifier == self.load_item_unique_identifier: service_item = new_item
service_item.edit_id = int(self.load_item_edit_id)
service_item.temporary_edit = self.load_item_temporary
self.add_service_item(service_item, repaint=False) self.add_service_item(service_item, repaint=False)
delete_file(p_file) delete_file(p_file)
self.main_window.add_recent_file(file_name) self.main_window.add_recent_file(file_name)
@ -1260,14 +1257,6 @@ class ServiceManager(QtGui.QWidget, ServiceManagerDialog):
self.repaint_service_list(-1, -1) self.repaint_service_list(-1, -1)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def service_item_update(self, edit_id, unique_identifier, temporary=False):
"""
Triggered from plugins to update service items. Save the values as they will be used as part of the service load
"""
self.load_item_unique_identifier = unique_identifier
self.load_item_edit_id = int(edit_id)
self.load_item_temporary = str_to_bool(temporary)
def replace_service_item(self, newItem): def replace_service_item(self, newItem):
""" """
Using the service item passed replace the one with the same edit id if found. Using the service item passed replace the one with the same edit id if found.

View File

@ -29,6 +29,8 @@
""" """
The Themes configuration tab The Themes configuration tab
""" """
from __future__ import division
from PyQt4 import QtCore, QtGui from PyQt4 import QtCore, QtGui
from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate from openlp.core.lib import Registry, Settings, SettingsTab, UiStrings, translate
@ -90,7 +92,7 @@ class ThemesTab(SettingsTab):
self.global_level_label.setObjectName(u'global_level_label') self.global_level_label.setObjectName(u'global_level_label')
self.level_layout.addRow(self.global_level_radio_button, self.global_level_label) self.level_layout.addRow(self.global_level_radio_button, self.global_level_label)
label_top_margin = (self.song_level_radio_button.sizeHint().height() - label_top_margin = (self.song_level_radio_button.sizeHint().height() -
self.song_level_label.sizeHint().height()) / 2 self.song_level_label.sizeHint().height()) // 2
for label in [self.song_level_label, self.service_level_label, self.global_level_label]: for label in [self.song_level_label, self.service_level_label, self.global_level_label]:
rect = label.rect() rect = label.rect()
rect.setTop(rect.top() + label_top_margin) rect.setTop(rect.top() + label_top_margin)

View File

@ -75,9 +75,9 @@ class Ui_AlertDialog(object):
self.manage_button_layout.addWidget(self.delete_button) self.manage_button_layout.addWidget(self.delete_button)
self.manage_button_layout.addStretch() self.manage_button_layout.addStretch()
self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1) self.alert_dialog_layout.addLayout(self.manage_button_layout, 1, 1)
displayIcon = build_icon(u':/general/general_live.png') display_icon = build_icon(u':/general/general_live.png')
self.display_button = create_button(alert_dialog, u'display_button', icon=displayIcon, enabled=False) self.display_button = create_button(alert_dialog, u'display_button', icon=display_icon, enabled=False)
self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=displayIcon, self.display_close_button = create_button(alert_dialog, u'display_close_button', icon=display_icon,
enabled=False) enabled=False)
self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'], self.button_box = create_button_box(alert_dialog, u'button_box', [u'close'],
[self.display_button, self.display_close_button]) [self.display_button, self.display_close_button])

View File

@ -511,7 +511,7 @@ class BibleImportForm(OpenLPWizard):
name = bible[u'abbreviation'] name = bible[u'abbreviation']
self.web_bible_list[download_type][version] = name.strip() self.web_bible_list[download_type][version] = name.strip()
def preWizard(self): def pre_wizard(self):
""" """
Prepare the UI for the import. Prepare the UI for the import.
""" """

View File

@ -105,7 +105,7 @@ class BibleUpgradeForm(OpenLPWizard):
Perform necessary functions depending on which wizard page is active. Perform necessary functions depending on which wizard page is active.
""" """
if self.page(pageId) == self.progress_page: if self.page(pageId) == self.progress_page:
self.preWizard() self.pre_wizard()
self.performWizard() self.performWizard()
self.post_wizard() self.post_wizard()
elif self.page(pageId) == self.selectPage and not self.files: elif self.page(pageId) == self.selectPage and not self.files:
@ -329,7 +329,7 @@ class BibleUpgradeForm(OpenLPWizard):
self.cancel_button.setVisible(True) self.cancel_button.setVisible(True)
settings.endGroup() settings.endGroup()
def preWizard(self): def pre_wizard(self):
""" """
Prepare the UI for the upgrade. Prepare the UI for the upgrade.
""" """

View File

@ -40,6 +40,7 @@ from openlp.plugins.custom.lib.db import CustomSlide
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class CustomSearch(object): class CustomSearch(object):
""" """
An enumeration for custom search methods. An enumeration for custom search methods.
@ -214,7 +215,6 @@ class CustomMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_keywords = self.search_text_edit.displayText() search_keywords = self.search_text_edit.displayText()
search_results = []
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
if search_type == CustomSearch.Titles: if search_type == CustomSearch.Titles:
log.debug(u'Titles Search') log.debug(u'Titles Search')
@ -252,7 +252,8 @@ class CustomMediaItem(MediaManagerItem):
and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme, and_(CustomSlide.title == item.title, CustomSlide.theme_name == item.theme,
CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:])) CustomSlide.credits == item.raw_footer[0][len(item.title) + 1:]))
if custom: if custom:
self.service_manager.service_item_update(custom.id, item.unique_identifier) item.edit_id = custom.id
return item
else: else:
if self.add_custom_from_service: if self.add_custom_from_service:
self.create_from_service_item(item) self.create_from_service_item(item)
@ -281,8 +282,6 @@ class CustomMediaItem(MediaManagerItem):
custom.text = unicode(custom_xml.extract_xml(), u'utf-8') custom.text = unicode(custom_xml.extract_xml(), u'utf-8')
self.plugin.manager.save_object(custom) self.plugin.manager.save_object(custom)
self.on_search_text_button_clicked() self.on_search_text_button_clicked()
if item.name.lower() == u'custom':
Registry().execute(u'service_item_update', u'%s:%s:%s' % (custom.id, item.unique_identifier, False))
def on_clear_text_button_click(self): def on_clear_text_button_click(self):
""" """

View File

@ -50,6 +50,7 @@ class ChooseGroupForm(QtGui.QDialog, Ui_ChooseGroupDialog):
``selected_group`` ``selected_group``
The ID of the group that should be selected by default when showing the dialog. The ID of the group that should be selected by default when showing the dialog.
""" """
self.new_group_edit.clear()
if selected_group is not None: if selected_group is not None:
for index in range(self.group_combobox.count()): for index in range(self.group_combobox.count()):
if self.group_combobox.itemData(index) == selected_group: if self.group_combobox.itemData(index) == selected_group:

View File

@ -32,7 +32,6 @@ from PyQt4 import QtGui
from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate from openlp.core.lib import Registry, SettingsTab, Settings, UiStrings, translate
class ImageTab(SettingsTab): class ImageTab(SettingsTab):
""" """
ImageTab is the images settings tab in the settings dialog. ImageTab is the images settings tab in the settings dialog.

View File

@ -391,6 +391,7 @@ class ImageMediaItem(MediaManagerItem):
``initial_load`` ``initial_load``
When set to False, the busy cursor and progressbar will be shown while loading images When set to False, the busy cursor and progressbar will be shown while loading images
""" """
parent_group = None
if target_group is None: if target_group is None:
# Find out if a group must be pre-selected # Find out if a group must be pre-selected
preselect_group = None preselect_group = None
@ -436,6 +437,8 @@ class ImageMediaItem(MediaManagerItem):
parent_group.parent_id = 0 parent_group.parent_id = 0
parent_group.group_name = self.choose_group_form.new_group_edit.text() parent_group.group_name = self.choose_group_form.new_group_edit.text()
self.manager.save_object(parent_group) self.manager.save_object(parent_group)
self.fill_groups_combobox(self.choose_group_form.group_combobox)
self.fill_groups_combobox(self.add_group_form.parent_group_combobox)
else: else:
parent_group = target_group.data(0, QtCore.Qt.UserRole) parent_group = target_group.data(0, QtCore.Qt.UserRole)
if isinstance(parent_group, ImageFilenames): if isinstance(parent_group, ImageFilenames):
@ -550,28 +553,25 @@ class ImageMediaItem(MediaManagerItem):
service_item.add_capability(ItemCapabilities.CanAppend) service_item.add_capability(ItemCapabilities.CanAppend)
# force a nonexistent theme # force a nonexistent theme
service_item.theme = -1 service_item.theme = -1
missing_items = []
missing_items_filenames = [] missing_items_filenames = []
images_filenames = []
# Expand groups to images # Expand groups to images
for bitem in items: for bitem in items:
if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None: if isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageGroups) or bitem.data(0, QtCore.Qt.UserRole) is None:
for index in range(0, bitem.childCount()): for index in range(0, bitem.childCount()):
if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames): if isinstance(bitem.child(index).data(0, QtCore.Qt.UserRole), ImageFilenames):
items.append(bitem.child(index)) images_filenames.append(bitem.child(index).data(0, QtCore.Qt.UserRole).filename)
items.remove(bitem) elif isinstance(bitem.data(0, QtCore.Qt.UserRole), ImageFilenames):
images_filenames.append(bitem.data(0, QtCore.Qt.UserRole).filename)
# Don't try to display empty groups # Don't try to display empty groups
if not items: if not images_filenames:
return False return False
# Find missing files # Find missing files
for bitem in items: for filename in images_filenames:
filename = bitem.data(0, QtCore.Qt.UserRole).filename
if not os.path.exists(filename): if not os.path.exists(filename):
missing_items.append(bitem)
missing_items_filenames.append(filename) missing_items_filenames.append(filename)
for item in missing_items:
items.remove(item)
# We cannot continue, as all images do not exist. # We cannot continue, as all images do not exist.
if not items: if not images_filenames:
if not remote: if not remote:
critical_error_message_box( critical_error_message_box(
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
@ -579,15 +579,14 @@ class ImageMediaItem(MediaManagerItem):
u'\n'.join(missing_items_filenames)) u'\n'.join(missing_items_filenames))
return False return False
# We have missing as well as existing images. We ask what to do. # We have missing as well as existing images. We ask what to do.
elif missing_items and QtGui.QMessageBox.question(self, elif missing_items_filenames and QtGui.QMessageBox.question(self,
translate('ImagePlugin.MediaItem', 'Missing Image(s)'), translate('ImagePlugin.MediaItem', 'Missing Image(s)'),
translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n' translate('ImagePlugin.MediaItem', 'The following image(s) no longer exist: %s\n'
'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames), 'Do you want to add the other images anyway?') % u'\n'.join(missing_items_filenames),
QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No: QtGui.QMessageBox.StandardButtons(QtGui.QMessageBox.No | QtGui.QMessageBox.Yes)) == QtGui.QMessageBox.No:
return False return False
# Continue with the existing images. # Continue with the existing images.
for bitem in items: for filename in images_filenames:
filename = bitem.data(0, QtCore.Qt.UserRole).filename
name = os.path.split(filename)[1] name = os.path.split(filename)[1]
service_item.add_from_image(filename, name, background) service_item.add_from_image(filename, name, background)
return True return True

View File

@ -44,10 +44,10 @@ log = logging.getLogger(__name__)
CLAPPERBOARD = u':/media/slidecontroller_multimedia.png' CLAPPERBOARD = u':/media/slidecontroller_multimedia.png'
VIDEO_ICON = build_icon(QtGui.QImage(u':/media/media_video.png')) VIDEO_ICON = build_icon(u':/media/media_video.png')
AUDIO_ICON = build_icon(QtGui.QImage(u':/media/media_audio.png')) AUDIO_ICON = build_icon(u':/media/media_audio.png')
DVD_ICON = build_icon(QtGui.QImage(u':/media/media_video.png')) DVD_ICON = build_icon(u':/media/media_video.png')
ERROR_ICON = build_icon(QtGui.QImage(u':/general/general_delete.png')) ERROR_ICON = build_icon(u':/general/general_delete.png')
class MediaMediaItem(MediaManagerItem): class MediaMediaItem(MediaManagerItem):
@ -155,7 +155,7 @@ class MediaMediaItem(MediaManagerItem):
if os.path.exists(filename): if os.path.exists(filename):
service_item = ServiceItem() service_item = ServiceItem()
service_item.title = u'webkit' service_item.title = u'webkit'
service_item.shortname = service_item.title service_item.processor = u'webkit'
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.add_from_command(path, name,CLAPPERBOARD) service_item.add_from_command(path, name,CLAPPERBOARD)
if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True): if self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True):
@ -185,9 +185,9 @@ class MediaMediaItem(MediaManagerItem):
translate('MediaPlugin.MediaItem', 'Missing Media File'), translate('MediaPlugin.MediaItem', 'Missing Media File'),
translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename) translate('MediaPlugin.MediaItem', 'The file %s no longer exists.') % filename)
return False return False
service_item.title = self.display_type_combo_box.currentText()
service_item.shortname = service_item.title
(path, name) = os.path.split(filename) (path, name) = os.path.split(filename)
service_item.title = name
service_item.processor = self.display_type_combo_box.currentText()
service_item.add_from_command(path, name, CLAPPERBOARD) service_item.add_from_command(path, name, CLAPPERBOARD)
# Only get start and end times if going to a service # Only get start and end times if going to a service
if context == ServiceItemContext.Service: if context == ServiceItemContext.Service:
@ -196,7 +196,6 @@ class MediaMediaItem(MediaManagerItem):
return False return False
service_item.add_capability(ItemCapabilities.CanAutoStartForLive) service_item.add_capability(ItemCapabilities.CanAutoStartForLive)
service_item.add_capability(ItemCapabilities.RequiresMedia) service_item.add_capability(ItemCapabilities.RequiresMedia)
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay)
if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked: if Settings().value(self.settings_section + u'/media auto start') == QtCore.Qt.Checked:
service_item.will_auto_start = True service_item.will_auto_start = True
# force a non-existent theme # force a non-existent theme
@ -260,8 +259,7 @@ class MediaMediaItem(MediaManagerItem):
Settings().setValue(self.settings_section + u'/media files', self.get_file_list()) Settings().setValue(self.settings_section + u'/media files', self.get_file_list())
def load_list(self, media, target_group=None): def load_list(self, media, target_group=None):
# Sort the media by its filename considering language specific # Sort the media by its filename considering language specific characters.
# characters.
media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1])) media.sort(key=lambda filename: get_locale_key(os.path.split(unicode(filename))[1]))
for track in media: for track in media:
track_info = QtCore.QFileInfo(track) track_info = QtCore.QFileInfo(track)

View File

@ -244,22 +244,20 @@ class PresentationMediaItem(MediaManagerItem):
items = self.list_view.selectedItems() items = self.list_view.selectedItems()
if len(items) > 1: if len(items) > 1:
return False return False
service_item.title = self.display_type_combo_box.currentText() service_item.processor = self.display_type_combo_box.currentText()
service_item.shortname = self.display_type_combo_box.currentText()
service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay) service_item.add_capability(ItemCapabilities.ProvidesOwnDisplay)
service_item.add_capability(ItemCapabilities.HasDetailedTitleDisplay) if not self.display_type_combo_box.currentText():
shortname = service_item.shortname
if not shortname:
return False return False
for bitem in items: for bitem in items:
filename = bitem.data(QtCore.Qt.UserRole) filename = bitem.data(QtCore.Qt.UserRole)
(path, name) = os.path.split(filename)
service_item.title = name
if os.path.exists(filename): if os.path.exists(filename):
if shortname == self.Automatic: if service_item.processor == self.Automatic:
service_item.shortname = self.findControllerByType(filename) service_item.processor = self.findControllerByType(filename)
if not service_item.shortname: if not service_item.processor:
return False return False
controller = self.controllers[service_item.shortname] controller = self.controllers[service_item.processor]
(path, name) = os.path.split(filename)
doc = controller.add_document(filename) doc = controller.add_document(filename)
if doc.get_thumbnail_path(1, True) is None: if doc.get_thumbnail_path(1, True) is None:
doc.load_presentation() doc.load_presentation()

View File

@ -36,6 +36,7 @@ from openlp.core.ui import HideMode
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class Controller(object): class Controller(object):
""" """
This is the Presentation listener who acts on events from the slide controller and passes the messages on the the This is the Presentation listener who acts on events from the slide controller and passes the messages on the the
@ -314,7 +315,7 @@ class MessageListener(object):
item = message[0] item = message[0]
hide_mode = message[2] hide_mode = message[2]
file = item.get_frame_path() file = item.get_frame_path()
self.handler = item.title self.handler = item.processor
if self.handler == self.media_item.Automatic: if self.handler == self.media_item.Automatic:
self.handler = self.media_item.findControllerByType(file) self.handler = self.media_item.findControllerByType(file)
if not self.handler: if not self.handler:

View File

@ -38,6 +38,7 @@ from openlp.core.utils import AppLocation
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
class PresentationDocument(object): class PresentationDocument(object):
""" """
Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the Base class for presentation documents to inherit from. Loads and closes the presentation as well as triggering the
@ -322,7 +323,7 @@ class PresentationController(object):
``supports`` ``supports``
The primary native file types this application supports. The primary native file types this application supports.
``alsosupports`` ``also_supports``
Other file types the application can import, although not necessarily the first choice due to potential Other file types the application can import, although not necessarily the first choice due to potential
incompatibilities. incompatibilities.
@ -358,7 +359,7 @@ class PresentationController(object):
Name of the application, to appear in the application Name of the application, to appear in the application
""" """
self.supports = [] self.supports = []
self.alsosupports = [] self.also_supports = []
self.docs = [] self.docs = []
self.plugin = plugin self.plugin = plugin
self.name = name self.name = name

View File

@ -49,7 +49,7 @@ __default_settings__ = {
u'presentations/Powerpoint': QtCore.Qt.Checked, u'presentations/Powerpoint': QtCore.Qt.Checked,
u'presentations/Powerpoint Viewer': QtCore.Qt.Checked, u'presentations/Powerpoint Viewer': QtCore.Qt.Checked,
u'presentations/presentations files': [] u'presentations/presentations files': []
} }
class PresentationPlugin(Plugin): class PresentationPlugin(Plugin):

View File

@ -147,7 +147,7 @@ window.OpenLP = {
}, },
pollServer: function () { pollServer: function () {
$.getJSON( $.getJSON(
"/stage/api/poll", "/stage/poll",
function (data, status) { function (data, status) {
var prevItem = OpenLP.currentItem; var prevItem = OpenLP.currentItem;
OpenLP.currentSlide = data.results.slide; OpenLP.currentSlide = data.results.slide;

View File

@ -26,7 +26,7 @@
window.OpenLP = { window.OpenLP = {
loadService: function (event) { loadService: function (event) {
$.getJSON( $.getJSON(
"/stage/api/service/list", "/stage/service/list",
function (data, status) { function (data, status) {
OpenLP.nextSong = ""; OpenLP.nextSong = "";
$("#notes").html(""); $("#notes").html("");
@ -46,7 +46,7 @@ window.OpenLP = {
}, },
loadSlides: function (event) { loadSlides: function (event) {
$.getJSON( $.getJSON(
"/stage/api/controller/live/text", "/stage/controller/live/text",
function (data, status) { function (data, status) {
OpenLP.currentSlides = data.results.slides; OpenLP.currentSlides = data.results.slides;
OpenLP.currentSlide = 0; OpenLP.currentSlide = 0;
@ -137,7 +137,7 @@ window.OpenLP = {
}, },
pollServer: function () { pollServer: function () {
$.getJSON( $.getJSON(
"/stage/api/poll", "/stage/poll",
function (data, status) { function (data, status) {
OpenLP.updateClock(data); OpenLP.updateClock(data);
if (OpenLP.currentItem != data.results.item || if (OpenLP.currentItem != data.results.item ||

View File

@ -267,11 +267,11 @@ class HttpRouter(object):
(u'^/(stage)$', self.serve_file), (u'^/(stage)$', self.serve_file),
(r'^/files/(.*)$', self.serve_file), (r'^/files/(.*)$', self.serve_file),
(r'^/api/poll$', self.poll), (r'^/api/poll$', self.poll),
(r'^/stage/api/poll$', self.poll), (r'^/stage/poll$', self.poll),
(r'^/api/controller/(live|preview)/(.*)$', self.controller), (r'^/api/controller/(live|preview)/(.*)$', self.controller),
(r'^/stage/api/controller/(live|preview)/(.*)$', self.controller), (r'^/stage/controller/(live|preview)/(.*)$', self.controller),
(r'^/api/service/(.*)$', self.service), (r'^/api/service/(.*)$', self.service),
(r'^/stage/api/service/(.*)$', self.service), (r'^/stage/service/(.*)$', self.service),
(r'^/api/display/(hide|show|blank|theme|desktop)$', self.display), (r'^/api/display/(hide|show|blank|theme|desktop)$', self.display),
(r'^/api/alert$', self.alert), (r'^/api/alert$', self.alert),
(r'^/api/plugin/(search)$', self.plugin_info), (r'^/api/plugin/(search)$', self.plugin_info),

View File

@ -52,3 +52,4 @@ This allows OpenLP to use ``self.object`` for all the GUI elements while keeping
them separate from the functionality, so that it is easier to recreate the GUI them separate from the functionality, so that it is easier to recreate the GUI
from the .ui files later if necessary. from the .ui files later if necessary.
""" """
from editsongform import EditSongForm

View File

@ -235,7 +235,7 @@ class SongExportForm(OpenLPWizard):
self.availableListWidget.addItem(item) self.availableListWidget.addItem(item)
self.application.set_normal_cursor() self.application.set_normal_cursor()
def preWizard(self): def pre_wizard(self):
""" """
Perform pre export tasks. Perform pre export tasks.
""" """

View File

@ -325,7 +325,7 @@ class SongImportForm(OpenLPWizard):
self.error_copy_to_button.setHidden(True) self.error_copy_to_button.setHidden(True)
self.error_save_to_button.setHidden(True) self.error_save_to_button.setHidden(True)
def preWizard(self): def pre_wizard(self):
""" """
Perform pre import tasks Perform pre import tasks
""" """

View File

@ -72,10 +72,7 @@ class SongMediaItem(MediaManagerItem):
def __init__(self, parent, plugin): def __init__(self, parent, plugin):
self.icon_path = u'songs/song' self.icon_path = u'songs/song'
MediaManagerItem.__init__(self, parent, plugin) MediaManagerItem.__init__(self, parent, plugin)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.single_service_item = False self.single_service_item = False
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
# Holds information about whether the edit is remotely triggered and which Song is required. # Holds information about whether the edit is remotely triggered and which Song is required.
self.remote_song = -1 self.remote_song = -1
self.edit_item = None self.edit_item = None
@ -132,6 +129,12 @@ class SongMediaItem(MediaManagerItem):
'Maintain the lists of authors, topics and books.')) 'Maintain the lists of authors, topics and books.'))
def initialise(self): def initialise(self):
"""
Initialise variables when they cannot be initialised in the constructor.
"""
self.song_maintenance_form = SongMaintenanceForm(self.plugin.manager, self)
self.edit_song_form = EditSongForm(self, self.main_window, self.plugin.manager)
self.openLyrics = OpenLyrics(self.plugin.manager)
self.search_text_edit.set_search_types([ self.search_text_edit.set_search_types([
(SongSearch.Entire, u':/songs/song_search_all.png', (SongSearch.Entire, u':/songs/song_search_all.png',
translate('SongsPlugin.MediaItem', 'Entire Song'), translate('SongsPlugin.MediaItem', 'Entire Song'),
@ -157,7 +160,6 @@ class SongMediaItem(MediaManagerItem):
Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type()) Settings().setValue(u'%s/last search type' % self.settings_section, self.search_text_edit.current_search_type())
# Reload the list considering the new search type. # Reload the list considering the new search type.
search_keywords = unicode(self.search_text_edit.displayText()) search_keywords = unicode(self.search_text_edit.displayText())
search_results = []
search_type = self.search_text_edit.current_search_type() search_type = self.search_text_edit.current_search_type()
if search_type == SongSearch.Entire: if search_type == SongSearch.Entire:
log.debug(u'Entire Song Search') log.debug(u'Entire Song Search')
@ -457,14 +459,7 @@ class SongMediaItem(MediaManagerItem):
for slide in verses: for slide in verses:
service_item.add_from_text(unicode(slide)) service_item.add_from_text(unicode(slide))
service_item.title = song.title service_item.title = song.title
author_list = [unicode(author.display_name) for author in song.authors] author_list = self.generate_footer(service_item, song)
service_item.raw_footer.append(song.title)
service_item.raw_footer.append(create_separated_list(author_list))
service_item.raw_footer.append(song.copyright)
if Settings().value(u'core/ccli number'):
service_item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'core/ccli number'))
service_item.audit = [song.title, author_list, song.copyright, unicode(song.ccli_number)]
service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)} service_item.data_string = {u'title': song.search_title, u'authors': u', '.join(author_list)}
service_item.xml_version = self.openLyrics.song_to_xml(song) service_item.xml_version = self.openLyrics.song_to_xml(song)
# Add the audio file to the service item. # Add the audio file to the service item.
@ -473,6 +468,30 @@ class SongMediaItem(MediaManagerItem):
service_item.background_audio = [m.file_name for m in song.media_files] service_item.background_audio = [m.file_name for m in song.media_files]
return True return True
def generate_footer(self, item, song):
"""
Generates the song footer based on a song and adds details to a service item.
author_list is only required for initial song generation.
``item``
The service item to be amended
``song``
The song to be used to generate the footer
"""
author_list = [unicode(author.display_name) for author in song.authors]
item.audit = [
song.title, author_list, song.copyright, unicode(song.ccli_number)
]
item.raw_footer = []
item.raw_footer.append(song.title)
item.raw_footer.append(create_separated_list(author_list))
item.raw_footer.append(song.copyright)
if Settings().value(u'core/ccli number'):
item.raw_footer.append(translate('SongsPlugin.MediaItem', 'CCLI License: ') +
Settings().value(u'core/ccli number'))
return author_list
def service_load(self, item): def service_load(self, item):
""" """
Triggered by a song being loaded by the service manager. Triggered by a song being loaded by the service manager.
@ -490,9 +509,8 @@ class SongMediaItem(MediaManagerItem):
else: else:
search_results = self.plugin.manager.get_all_objects(Song, search_results = self.plugin.manager.get_all_objects(Song,
Song.search_title == item.data_string[u'title'], Song.search_title.asc()) Song.search_title == item.data_string[u'title'], Song.search_title.asc())
editId = 0 edit_id = 0
add_song = True add_song = True
temporary = False
if search_results: if search_results:
for song in search_results: for song in search_results:
author_list = item.data_string[u'authors'] author_list = item.data_string[u'authors']
@ -505,7 +523,7 @@ class SongMediaItem(MediaManagerItem):
break break
if same_authors and author_list.strip(u', ') == u'': if same_authors and author_list.strip(u', ') == u'':
add_song = False add_song = False
editId = song.id edit_id = song.id
break break
# If there's any backing tracks, copy them over. # If there's any backing tracks, copy them over.
if item.background_audio: if item.background_audio:
@ -523,11 +541,11 @@ class SongMediaItem(MediaManagerItem):
# If there's any backing tracks, copy them over. # If there's any backing tracks, copy them over.
if item.background_audio: if item.background_audio:
self._update_background_audio(song, item) self._update_background_audio(song, item)
editId = song.id edit_id = song.id
temporary = True # Update service with correct song id and return it to caller.
# Update service with correct song id. item.edit_id = edit_id
if editId: self.generate_footer(item, song)
self.service_manager.service_item_update(editId, item.unique_identifier, temporary) return item
def search(self, string, showError): def search(self, string, showError):
""" """

View File

@ -235,8 +235,7 @@ class SongsPlugin(Plugin):
u'delete': translate('SongsPlugin', 'Delete the selected song.'), u'delete': translate('SongsPlugin', 'Delete the selected song.'),
u'preview': translate('SongsPlugin', 'Preview the selected song.'), u'preview': translate('SongsPlugin', 'Preview the selected song.'),
u'live': translate('SongsPlugin', 'Send the selected song live.'), u'live': translate('SongsPlugin', 'Send the selected song live.'),
u'service': translate('SongsPlugin', u'service': translate('SongsPlugin', 'Add the selected song to the service.')
'Add the selected song to the service.')
} }
self.set_plugin_ui_text_strings(tooltips) self.set_plugin_ui_text_strings(tooltips)

View File

@ -1,13 +1,20 @@
""" """
Package to test the openlp.core.lib package. Package to test the openlp.core.lib package.
""" """
import os
from unittest import TestCase from unittest import TestCase
from datetime import datetime, timedelta from datetime import datetime, timedelta
from mock import MagicMock, patch from mock import MagicMock, patch
from PyQt4 import QtCore, QtGui
from openlp.core.lib import str_to_bool, create_thumb, translate, check_directory_exists, get_text_file_string, \
build_icon, image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), u'..', u'..', u'resources'))
from openlp.core.lib import str_to_bool, translate, check_directory_exists, get_text_file_string, build_icon, \
image_to_byte, check_item_selected, validate_thumb, create_separated_list, clean_tags, expand_tags
class TestLib(TestCase): class TestLib(TestCase):
@ -125,7 +132,7 @@ class TestLib(TestCase):
Test the check_directory_exists() function Test the check_directory_exists() function
""" """
with patch(u'openlp.core.lib.os.path.exists') as mocked_exists, \ with patch(u'openlp.core.lib.os.path.exists') as mocked_exists, \
patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs: patch(u'openlp.core.lib.os.makedirs') as mocked_makedirs:
# GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists # GIVEN: A directory to check and a mocked out os.makedirs and os.path.exists
directory_to_check = u'existing/directory' directory_to_check = u'existing/directory'
@ -219,7 +226,7 @@ class TestLib(TestCase):
Test the build_icon() function with a resource URI Test the build_icon() function with a resource URI
""" """
with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \ with patch(u'openlp.core.lib.QtGui') as MockedQtGui, \
patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap: patch(u'openlp.core.lib.QtGui.QPixmap') as MockedQPixmap:
# GIVEN: A mocked QIcon and a mocked QPixmap # GIVEN: A mocked QIcon and a mocked QPixmap
MockedQtGui.QIcon = MagicMock MockedQtGui.QIcon = MagicMock
MockedQtGui.QIcon.Normal = 1 MockedQtGui.QIcon.Normal = 1
@ -261,9 +268,43 @@ class TestLib(TestCase):
mocked_byte_array.toBase64.assert_called_with() mocked_byte_array.toBase64.assert_called_with()
assert result == u'base64mock', u'The result should be the return value of the mocked out base64 method' assert result == u'base64mock', u'The result should be the return value of the mocked out base64 method'
def create_thumb_with_size_test(self):
"""
Test the create_thumb() function
"""
# GIVEN: An image to create a thumb of.
image_path = os.path.join(TEST_PATH, u'church.jpg')
thumb_path = os.path.join(TEST_PATH, u'church_thumb.jpg')
thumb_size = QtCore.QSize(10, 20)
# Remove the thumb so that the test actually tests if the thumb will be created. Maybe it was not deleted in the
# last test.
try:
os.remove(thumb_path)
except:
pass
# Only continue when the thumb does not exist.
assert not os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
# WHEN: Create the thumb.
icon = create_thumb(image_path, thumb_path, size=thumb_size)
# THEN: Check if the thumb was created.
assert os.path.exists(thumb_path), u'Test was not ran, because the thumb already exists.'
assert isinstance(icon, QtGui.QIcon), u'The icon should be a QIcon.'
assert not icon.isNull(), u'The icon should not be null.'
assert QtGui.QImageReader(thumb_path).size() == thumb_size, u'The thumb should have the given size.'
# Remove the thumb so that the test actually tests if the thumb will be created.
try:
os.remove(thumb_path)
except:
pass
def check_item_selected_true_test(self): def check_item_selected_true_test(self):
""" """
Test that the check_item_selected() function returns True when there are selected indexes. Test that the check_item_selected() function returns True when there are selected indexes
""" """
# GIVEN: A mocked out QtGui module and a list widget with selected indexes # GIVEN: A mocked out QtGui module and a list widget with selected indexes
MockedQtGui = patch(u'openlp.core.lib.QtGui') MockedQtGui = patch(u'openlp.core.lib.QtGui')
@ -423,7 +464,7 @@ class TestLib(TestCase):
def create_separated_list_qlocate_test(self): def create_separated_list_qlocate_test(self):
""" """
Test the create_separated_list function using the Qt provided method. Test the create_separated_list function using the Qt provided method
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, \ with patch(u'openlp.core.lib.Qt') as mocked_qt, \
patch(u'openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList: patch(u'openlp.core.lib.QtCore.QLocale.createSeparatedList') as mocked_createSeparatedList:
@ -442,7 +483,7 @@ class TestLib(TestCase):
def create_separated_list_empty_list_test(self): def create_separated_list_empty_list_test(self):
""" """
Test the create_separated_list function with an empty list. Test the create_separated_list function with an empty list
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt: with patch(u'openlp.core.lib.Qt') as mocked_qt:
# GIVEN: An empty list and the mocked Qt module. # GIVEN: An empty list and the mocked Qt module.
@ -458,7 +499,7 @@ class TestLib(TestCase):
def create_separated_list_with_one_item_test(self): def create_separated_list_with_one_item_test(self):
""" """
Test the create_separated_list function with a list consisting of only one entry. Test the create_separated_list function with a list consisting of only one entry
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt: with patch(u'openlp.core.lib.Qt') as mocked_qt:
# GIVEN: A list with a string and the mocked Qt module. # GIVEN: A list with a string and the mocked Qt module.
@ -474,7 +515,7 @@ class TestLib(TestCase):
def create_separated_list_with_two_items_test(self): def create_separated_list_with_two_items_test(self):
""" """
Test the create_separated_list function with a list of two entries. Test the create_separated_list function with a list of two entries
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate: with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
# GIVEN: A list of strings and the mocked Qt module. # GIVEN: A list of strings and the mocked Qt module.
@ -491,7 +532,7 @@ class TestLib(TestCase):
def create_separated_list_with_three_items_test(self): def create_separated_list_with_three_items_test(self):
""" """
Test the create_separated_list function with a list of three items. Test the create_separated_list function with a list of three items
""" """
with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate: with patch(u'openlp.core.lib.Qt') as mocked_qt, patch(u'openlp.core.lib.translate') as mocked_translate:
# GIVEN: A list with a string and the mocked Qt module. # GIVEN: A list with a string and the mocked Qt module.

View File

@ -1,7 +1,7 @@
""" """
Package to test the openlp.core.lib.screenlist package. Package to test the openlp.core.lib.screenlist package.
""" """
import copy
from unittest import TestCase from unittest import TestCase
from mock import MagicMock from mock import MagicMock

View File

@ -210,7 +210,6 @@ class TestServiceItem(TestCase):
# THEN: We should get back a valid service item # THEN: We should get back a valid service item
assert service_item.is_valid is True, u'The new service item should be valid' assert service_item.is_valid is True, u'The new service item should be valid'
print service_item.get_rendered_frame(0)
assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image' assert service_item.get_rendered_frame(0) == test_file, u'The first frame should match the path to the image'
assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1' assert service_item.get_frames()[0] == frame_array, u'The return should match frame array1'
assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image' assert service_item.get_frame_path(0) == test_file, u'The frame path should match the full path to the image'
@ -268,6 +267,26 @@ class TestServiceItem(TestCase):
assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \ assert service_item.is_capable(ItemCapabilities.CanAppend) is True, \
u'This service item should be able to have new items added to it' u'This service item should be able to have new items added to it'
def serviceitem_migrate_test_20_22(self):
"""
Test the Service Item - migrating a media only service item from 2.0 to 2.2 format
"""
# GIVEN: A new service item and a mocked add icon function
service_item = ServiceItem(None)
service_item.add_icon = MagicMock()
# WHEN: adding an media from a saved Service and mocked exists
line = self.convert_file_service_item(u'migrate_video_20_22.osd')
with patch('os.path.exists'):
service_item.set_from_service(line, TEST_PATH)
# THEN: We should get back a converted service item
assert service_item.is_valid is True, u'The new service item should be valid'
assert service_item.processor is None, u'The Processor should have been set'
assert service_item.title is None, u'The title should be set to a value'
assert service_item.is_capable(ItemCapabilities.HasDetailedTitleDisplay) is False, \
u'The Capability should have been removed'
def convert_file_service_item(self, name): def convert_file_service_item(self, name):
service_file = os.path.join(TEST_PATH, name) service_file = os.path.join(TEST_PATH, name)
try: try:

View File

@ -9,7 +9,7 @@ from unittest import TestCase
from mock import MagicMock, patch from mock import MagicMock, patch
from openlp.core.lib import Registry from openlp.core.lib import Registry
from openlp.plugins.images.lib.db import ImageFilenames from openlp.plugins.images.lib.db import ImageFilenames, ImageGroups
from openlp.plugins.images.lib.mediaitem import ImageMediaItem from openlp.plugins.images.lib.mediaitem import ImageMediaItem
@ -23,6 +23,7 @@ class TestImageMediaItem(TestCase):
Registry.create() Registry.create()
Registry().register(u'service_list', MagicMock()) Registry().register(u'service_list', MagicMock())
Registry().register(u'main_window', self.mocked_main_window) Registry().register(u'main_window', self.mocked_main_window)
Registry().register(u'live_controller', MagicMock())
mocked_parent = MagicMock() mocked_parent = MagicMock()
mocked_plugin = MagicMock() mocked_plugin = MagicMock()
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.__init__') as mocked_init:
@ -35,7 +36,7 @@ class TestImageMediaItem(TestCase):
""" """
# GIVEN: An empty image_list # GIVEN: An empty image_list
image_list = [] image_list = []
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
self.media_item.manager = MagicMock() self.media_item.manager = MagicMock()
# WHEN: We run save_new_images_list with the empty list # WHEN: We run save_new_images_list with the empty list
@ -51,7 +52,7 @@ class TestImageMediaItem(TestCase):
""" """
# GIVEN: A list with 1 image # GIVEN: A list with 1 image
image_list = [ u'test_image.jpg' ] image_list = [ u'test_image.jpg' ]
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
ImageFilenames.filename = '' ImageFilenames.filename = ''
self.media_item.manager = MagicMock() self.media_item.manager = MagicMock()
@ -59,7 +60,7 @@ class TestImageMediaItem(TestCase):
self.media_item.save_new_images_list(image_list, reload_list=True) self.media_item.save_new_images_list(image_list, reload_list=True)
# THEN: load_full_list() should have been called # THEN: load_full_list() should have been called
assert mocked_loadFullList.call_count == 1, u'load_full_list() should have been called' assert mocked_load_full_list.call_count == 1, u'load_full_list() should have been called'
# CLEANUP: Remove added attribute from ImageFilenames # CLEANUP: Remove added attribute from ImageFilenames
delattr(ImageFilenames, 'filename') delattr(ImageFilenames, 'filename')
@ -70,14 +71,14 @@ class TestImageMediaItem(TestCase):
""" """
# GIVEN: A list with 1 image # GIVEN: A list with 1 image
image_list = [ u'test_image.jpg' ] image_list = [ u'test_image.jpg' ]
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
self.media_item.manager = MagicMock() self.media_item.manager = MagicMock()
# WHEN: We run save_new_images_list with reload_list=False # WHEN: We run save_new_images_list with reload_list=False
self.media_item.save_new_images_list(image_list, reload_list=False) self.media_item.save_new_images_list(image_list, reload_list=False)
# THEN: load_full_list() should not have been called # THEN: load_full_list() should not have been called
assert mocked_loadFullList.call_count == 0, u'load_full_list() should not have been called' assert mocked_load_full_list.call_count == 0, u'load_full_list() should not have been called'
def save_new_images_list_multiple_images_test(self): def save_new_images_list_multiple_images_test(self):
""" """
@ -85,7 +86,7 @@ class TestImageMediaItem(TestCase):
""" """
# GIVEN: A list with 3 images # GIVEN: A list with 3 images
image_list = [ u'test_image_1.jpg', u'test_image_2.jpg', u'test_image_3.jpg' ] image_list = [ u'test_image_1.jpg', u'test_image_2.jpg', u'test_image_3.jpg' ]
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
self.media_item.manager = MagicMock() self.media_item.manager = MagicMock()
# WHEN: We run save_new_images_list with the list of 3 images # WHEN: We run save_new_images_list with the list of 3 images
@ -101,7 +102,7 @@ class TestImageMediaItem(TestCase):
""" """
# GIVEN: A list with images and objects # GIVEN: A list with images and objects
image_list = [ u'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ] image_list = [ u'test_image_1.jpg', None, True, ImageFilenames(), 'test_image_2.jpg' ]
with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_loadFullList: with patch(u'openlp.plugins.images.lib.mediaitem.ImageMediaItem.load_full_list') as mocked_load_full_list:
self.media_item.manager = MagicMock() self.media_item.manager = MagicMock()
# WHEN: We run save_new_images_list with the list of images and objects # WHEN: We run save_new_images_list with the list of images and objects
@ -110,3 +111,68 @@ class TestImageMediaItem(TestCase):
# THEN: load_full_list() should not have been called # THEN: load_full_list() should not have been called
assert self.media_item.manager.save_object.call_count == 2, \ assert self.media_item.manager.save_object.call_count == 2, \
u'load_full_list() should have been called only once' u'load_full_list() should have been called only once'
def on_reset_click_test(self):
"""
Test that on_reset_click() actually resets the background
"""
# GIVEN: A mocked version of reset_action
self.media_item.reset_action = MagicMock()
# WHEN: on_reset_click is called
self.media_item.on_reset_click()
# THEN: the reset_action should be set visible, and the image should be reset
self.media_item.reset_action.setVisible.assert_called_with(False)
self.media_item.live_controller.display.reset_image.assert_called_with()
def _recursively_delete_group_side_effect(*args, **kwargs):
"""
Side effect method that creates custom retun values for the recursively_delete_group method
"""
if args[1] == ImageFilenames and args[2]:
# Create some fake objects that should be removed
returned_object1 = ImageFilenames()
returned_object1.id = 1
returned_object1.filename = u'/tmp/test_file_1.jpg'
returned_object2 = ImageFilenames()
returned_object2.id = 2
returned_object2.filename = u'/tmp/test_file_2.jpg'
returned_object3 = ImageFilenames()
returned_object3.id = 3
returned_object3.filename = u'/tmp/test_file_3.jpg'
return [returned_object1, returned_object2, returned_object3]
if args[1] == ImageGroups and args[2]:
# Change the parent_id that is matched so we don't get into an endless loop
ImageGroups.parent_id = 0
# Create a fake group that will be used in the next run
returned_object1 = ImageGroups()
returned_object1.id = 1
return [returned_object1]
return []
def recursively_delete_group_test(self):
"""
Test that recursively_delete_group() works
"""
# GIVEN: An ImageGroups object and mocked functions
with patch(u'openlp.core.utils.delete_file') as mocked_delete_file:
ImageFilenames.group_id = 1
ImageGroups.parent_id = 1
self.media_item.manager = MagicMock()
self.media_item.manager.get_all_objects.side_effect = self._recursively_delete_group_side_effect
self.media_item.servicePath = ""
test_group = ImageGroups()
test_group.id = 1
# WHEN: recursively_delete_group() is called
self.media_item.recursively_delete_group(test_group)
# THEN:
assert mocked_delete_file.call_count == 0, u'delete_file() should not be called'
assert self.media_item.manager.delete_object.call_count == 7, \
u'manager.delete_object() should be called exactly 7 times'
# CLEANUP: Remove added attribute from ImageFilenames and ImageGroups
delattr(ImageFilenames, 'group_id')
delattr(ImageGroups, 'parent_id')

View File

@ -0,0 +1,125 @@
"""
This module contains tests for the lib submodule of the Songs plugin.
"""
import os
from tempfile import mkstemp
from unittest import TestCase
from mock import patch, MagicMock
from PyQt4 import QtGui
from openlp.core.lib import Registry, ServiceItem, Settings
from openlp.plugins.songs.lib.mediaitem import SongMediaItem
class TestMediaItem(TestCase):
"""
Test the functions in the :mod:`lib` module.
"""
def setUp(self):
"""
Set up the components need for all tests.
"""
Registry.create()
Registry().register(u'service_list', MagicMock())
Registry().register(u'main_window', MagicMock())
with patch('openlp.core.lib.mediamanageritem.MediaManagerItem.__init__'), \
patch('openlp.plugins.songs.forms.editsongform.EditSongForm.__init__'):
self.media_item = SongMediaItem(MagicMock(), MagicMock())
fd, self.ini_file = mkstemp(u'.ini')
Settings().set_filename(self.ini_file)
self.application = QtGui.QApplication.instance()
def tearDown(self):
"""
Delete all the C++ objects at the end so that we don't have a segfault
"""
del self.application
# Not all tests use settings!
try:
os.unlink(self.ini_file)
os.unlink(Settings().fileName())
except Exception:
pass
def build_song_footer_one_author_test(self):
"""
Test build songs footer with basic song and one author
"""
# GIVEN: A Song and a Service Item
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
author_list = self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright'],
u'The array should be returned correctly with a song, one author and copyright')
self.assertEqual(author_list, [u'my author'],
u'The author list should be returned correctly with one author')
def build_song_footer_two_authors_test(self):
"""
Test build songs footer with basic song and two authors
"""
# GIVEN: A Song and a Service Item
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_author = MagicMock()
mock_author.display_name = u'another author'
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
# WHEN: I generate the Footer with default settings
author_list = self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author and another author', u'My copyright'],
u'The array should be returned correctly with a song, two authors and copyright')
self.assertEqual(author_list, [u'my author', u'another author'],
u'The author list should be returned correctly with two authors')
def build_song_footer_base_ccli_test(self):
"""
Test build songs footer with basic song and two authors
"""
# GIVEN: A Song and a Service Item and a configured CCLI license
mock_song = MagicMock()
mock_song.title = u'My Song'
mock_author = MagicMock()
mock_author.display_name = u'my author'
mock_song.authors = []
mock_song.authors.append(mock_author)
mock_song.copyright = u'My copyright'
service_item = ServiceItem(None)
Settings().setValue(u'core/ccli number', u'1234')
# WHEN: I generate the Footer with default settings
self.media_item.generate_footer(service_item, mock_song)
# THEN: I get the following Array returned
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 1234'],
u'The array should be returned correctly with a song, an author, copyright and ccli')
# WHEN: I amend the CCLI value
Settings().setValue(u'core/ccli number', u'4321')
self.media_item.generate_footer(service_item, mock_song)
# THEN: I would get an amended footer string
self.assertEqual(service_item.raw_footer, [u'My Song', u'my author', u'My copyright', u'CCLI License: 4321'],
u'The array should be returned correctly with a song, an author, copyright and amended ccli')

View File

@ -0,0 +1,98 @@
(lp1
(dp2
Vserviceitem
p3
(dp4
Vheader
p5
(dp6
Vxml_version
p7
NsVauto_play_slides_loop
p8
I00
sVauto_play_slides_once
p9
I00
sVwill_auto_start
p10
I01
sVtitle
p11
VVLC
p12
sVcapabilities
p13
(lp14
I12
aI16
aI4
aI11
asVtheme
p15
I-1
sVbackground_audio
p16
(lp17
sVicon
p18
V:/plugins/plugin_media.png
p19
sVtype
p20
I3
sVstart_time
p21
I0
sVfrom_plugin
p22
I00
sVmedia_length
p23
I144
sVdata
p24
V
sVtimed_slide_interval
p25
I0
sVaudit
p26
V
sVsearch
p27
V
sVname
p28
Vmedia
p29
sVfooter
p30
(lp31
sVnotes
p32
V
sVplugin
p33
g29
sVtheme_overwritten
p34
I00
sVend_time
p35
I0
ssg24
(lp36
(dp37
Vpath
p38
V/home/tim/Videos/puppets
p39
sVimage
p40
V:/media/slidecontroller_multimedia.png
p41
sg11
VMVI_3405.MOV
p42
sassa.