Merge lp:~phill-ridout/openlp/pathlib7 into lp:openlp

Proposed by Phill
Status: Merged
Merged at revision: 2771
Proposed branch: lp:~phill-ridout/openlp/pathlib7
Merge into: lp:openlp
Diff against target: 1282 lines (+304/-310)
13 files modified
openlp/core/lib/json/theme.json (+1/-1)
openlp/core/lib/renderer.py (+4/-3)
openlp/core/lib/theme.py (+19/-15)
openlp/core/ui/maindisplay.py (+4/-4)
openlp/core/ui/themeform.py (+31/-27)
openlp/core/ui/thememanager.py (+184/-209)
openlp/core/ui/themestab.py (+2/-2)
openlp/plugins/images/lib/upgrade.py (+0/-1)
tests/functional/openlp_core_lib/test_theme.py (+6/-5)
tests/functional/openlp_core_ui/test_maindisplay.py (+9/-9)
tests/functional/openlp_core_ui/test_themeform.py (+1/-1)
tests/functional/openlp_core_ui/test_thememanager.py (+22/-29)
tests/interfaces/openlp_core_ui/test_thememanager.py (+21/-4)
To merge this branch: bzr merge lp:~phill-ridout/openlp/pathlib7
Reviewer Review Type Date Requested Status
Tomas Groth Approve
Tim Bentley Approve
Review via email: mp+331364@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Phill (phill-ridout) wrote :

Also automatically upgrades themes from XML to json.

Revision history for this message
Tim Bentley (trb143) wrote :

Looks good and finishes my changes from XML to json. Thanks

review: Approve
Revision history for this message
Tomas Groth (tomasgroth) wrote :

Seems legit ;-)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'openlp/core/lib/json/theme.json'
--- openlp/core/lib/json/theme.json 2013-10-18 18:10:47 +0000
+++ openlp/core/lib/json/theme.json 2017-09-26 17:09:04 +0000
@@ -4,7 +4,7 @@
4 "color": "#000000",4 "color": "#000000",
5 "direction": "vertical",5 "direction": "vertical",
6 "end_color": "#000000",6 "end_color": "#000000",
7 "filename": "",7 "filename": null,
8 "start_color": "#000000",8 "start_color": "#000000",
9 "type": "solid"9 "type": "solid"
10 },10 },
1111
=== modified file 'openlp/core/lib/renderer.py'
--- openlp/core/lib/renderer.py 2017-01-25 21:17:27 +0000
+++ openlp/core/lib/renderer.py 2017-09-26 17:09:04 +0000
@@ -26,6 +26,7 @@
26from PyQt5 import QtGui, QtCore, QtWebKitWidgets26from PyQt5 import QtGui, QtCore, QtWebKitWidgets
2727
28from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings28from openlp.core.common import Registry, RegistryProperties, OpenLPMixin, RegistryMixin, Settings
29from openlp.core.common.path import path_to_str
29from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \30from openlp.core.lib import FormattingTags, ImageSource, ItemCapabilities, ScreenList, ServiceItem, expand_tags, \
30 build_lyrics_format_css, build_lyrics_outline_css, build_chords_css31 build_lyrics_format_css, build_lyrics_outline_css, build_chords_css
31from openlp.core.common import ThemeLevel32from openlp.core.common import ThemeLevel
@@ -118,7 +119,7 @@
118 theme_data, main_rect, footer_rect = self._theme_dimensions[theme_name]119 theme_data, main_rect, footer_rect = self._theme_dimensions[theme_name]
119 # if No file do not update cache120 # if No file do not update cache
120 if theme_data.background_filename:121 if theme_data.background_filename:
121 self.image_manager.add_image(theme_data.background_filename,122 self.image_manager.add_image(path_to_str(theme_data.background_filename),
122 ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))123 ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
123124
124 def pre_render(self, override_theme_data=None):125 def pre_render(self, override_theme_data=None):
@@ -207,8 +208,8 @@
207 service_item.raw_footer = FOOTER208 service_item.raw_footer = FOOTER
208 # if No file do not update cache209 # if No file do not update cache
209 if theme_data.background_filename:210 if theme_data.background_filename:
210 self.image_manager.add_image(211 self.image_manager.add_image(path_to_str(theme_data.background_filename),
211 theme_data.background_filename, ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))212 ImageSource.Theme, QtGui.QColor(theme_data.background_border_color))
212 theme_data, main, footer = self.pre_render(theme_data)213 theme_data, main, footer = self.pre_render(theme_data)
213 service_item.theme_data = theme_data214 service_item.theme_data = theme_data
214 service_item.main = main215 service_item.main = main
215216
=== modified file 'openlp/core/lib/theme.py'
--- openlp/core/lib/theme.py 2017-08-12 17:45:56 +0000
+++ openlp/core/lib/theme.py 2017-09-26 17:09:04 +0000
@@ -22,13 +22,13 @@
22"""22"""
23Provide the theme XML and handling functions for OpenLP v2 themes.23Provide the theme XML and handling functions for OpenLP v2 themes.
24"""24"""
25import os25import json
26import logging26import logging
27import json
2827
29from lxml import etree, objectify28from lxml import etree, objectify
30from openlp.core.common import AppLocation, de_hump29from openlp.core.common import AppLocation, de_hump
3130from openlp.core.common.json import OpenLPJsonDecoder, OpenLPJsonEncoder
31from openlp.core.common.path import Path, str_to_path
32from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string32from openlp.core.lib import str_to_bool, ScreenList, get_text_file_string
3333
34log = logging.getLogger(__name__)34log = logging.getLogger(__name__)
@@ -160,9 +160,8 @@
160 # basic theme object with defaults160 # basic theme object with defaults
161 json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json'161 json_path = AppLocation.get_directory(AppLocation.AppDir) / 'core' / 'lib' / 'json' / 'theme.json'
162 jsn = get_text_file_string(json_path)162 jsn = get_text_file_string(json_path)
163 jsn = json.loads(jsn)163 self.load_theme(jsn)
164 self.expand_json(jsn)164 self.background_filename = None
165 self.background_filename = ''
166165
167 def expand_json(self, var, prev=None):166 def expand_json(self, var, prev=None):
168 """167 """
@@ -174,8 +173,6 @@
174 for key, value in var.items():173 for key, value in var.items():
175 if prev:174 if prev:
176 key = prev + "_" + key175 key = prev + "_" + key
177 else:
178 key = key
179 if isinstance(value, dict):176 if isinstance(value, dict):
180 self.expand_json(value, key)177 self.expand_json(value, key)
181 else:178 else:
@@ -185,13 +182,13 @@
185 """182 """
186 Add the path name to the image name so the background can be rendered.183 Add the path name to the image name so the background can be rendered.
187184
188 :param path: The path name to be added.185 :param openlp.core.common.path.Path path: The path name to be added.
186 :rtype: None
189 """187 """
190 if self.background_type == 'image' or self.background_type == 'video':188 if self.background_type == 'image' or self.background_type == 'video':
191 if self.background_filename and path:189 if self.background_filename and path:
192 self.theme_name = self.theme_name.strip()190 self.theme_name = self.theme_name.strip()
193 self.background_filename = self.background_filename.strip()191 self.background_filename = path / self.theme_name / self.background_filename
194 self.background_filename = os.path.join(path, self.theme_name, self.background_filename)
195192
196 def set_default_header_footer(self):193 def set_default_header_footer(self):
197 """194 """
@@ -206,16 +203,21 @@
206 self.font_footer_y = current_screen['size'].height() * 9 / 10203 self.font_footer_y = current_screen['size'].height() * 9 / 10
207 self.font_footer_height = current_screen['size'].height() / 10204 self.font_footer_height = current_screen['size'].height() / 10
208205
209 def load_theme(self, theme):206 def load_theme(self, theme, theme_path=None):
210 """207 """
211 Convert the JSON file and expand it.208 Convert the JSON file and expand it.
212209
213 :param theme: the theme string210 :param theme: the theme string
211 :param openlp.core.common.path.Path theme_path: The path to the theme
212 :rtype: None
214 """213 """
215 jsn = json.loads(theme)214 if theme_path:
215 jsn = json.loads(theme, cls=OpenLPJsonDecoder, base_path=theme_path)
216 else:
217 jsn = json.loads(theme, cls=OpenLPJsonDecoder)
216 self.expand_json(jsn)218 self.expand_json(jsn)
217219
218 def export_theme(self):220 def export_theme(self, theme_path=None):
219 """221 """
220 Loop through the fields and build a dictionary of them222 Loop through the fields and build a dictionary of them
221223
@@ -223,7 +225,9 @@
223 theme_data = {}225 theme_data = {}
224 for attr, value in self.__dict__.items():226 for attr, value in self.__dict__.items():
225 theme_data["{attr}".format(attr=attr)] = value227 theme_data["{attr}".format(attr=attr)] = value
226 return json.dumps(theme_data)228 if theme_path:
229 return json.dumps(theme_data, cls=OpenLPJsonEncoder, base_path=theme_path)
230 return json.dumps(theme_data, cls=OpenLPJsonEncoder)
227231
228 def parse(self, xml):232 def parse(self, xml):
229 """233 """
230234
=== modified file 'openlp/core/ui/maindisplay.py'
--- openlp/core/ui/maindisplay.py 2017-09-05 04:28:50 +0000
+++ openlp/core/ui/maindisplay.py 2017-09-26 17:09:04 +0000
@@ -346,7 +346,7 @@
346 if not hasattr(self, 'service_item'):346 if not hasattr(self, 'service_item'):
347 return False347 return False
348 self.override['image'] = path348 self.override['image'] = path
349 self.override['theme'] = self.service_item.theme_data.background_filename349 self.override['theme'] = path_to_str(self.service_item.theme_data.background_filename)
350 self.image(path)350 self.image(path)
351 # Update the preview frame.351 # Update the preview frame.
352 if self.is_live:352 if self.is_live:
@@ -454,7 +454,7 @@
454 Registry().execute('video_background_replaced')454 Registry().execute('video_background_replaced')
455 self.override = {}455 self.override = {}
456 # We have a different theme.456 # We have a different theme.
457 elif self.override['theme'] != service_item.theme_data.background_filename:457 elif self.override['theme'] != path_to_str(service_item.theme_data.background_filename):
458 Registry().execute('live_theme_changed')458 Registry().execute('live_theme_changed')
459 self.override = {}459 self.override = {}
460 else:460 else:
@@ -466,7 +466,7 @@
466 if self.service_item.theme_data.background_type == 'image':466 if self.service_item.theme_data.background_type == 'image':
467 if self.service_item.theme_data.background_filename:467 if self.service_item.theme_data.background_filename:
468 self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(468 self.service_item.bg_image_bytes = self.image_manager.get_image_bytes(
469 self.service_item.theme_data.background_filename, ImageSource.Theme)469 path_to_str(self.service_item.theme_data.background_filename), ImageSource.Theme)
470 if image_path:470 if image_path:
471 image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)471 image_bytes = self.image_manager.get_image_bytes(image_path, ImageSource.ImagePlugin)
472 created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,472 created_html = build_html(self.service_item, self.screen, self.is_live, background, image_bytes,
@@ -488,7 +488,7 @@
488 path = os.path.join(str(AppLocation.get_section_data_path('themes')),488 path = os.path.join(str(AppLocation.get_section_data_path('themes')),
489 self.service_item.theme_data.theme_name)489 self.service_item.theme_data.theme_name)
490 service_item.add_from_command(path,490 service_item.add_from_command(path,
491 self.service_item.theme_data.background_filename,491 path_to_str(self.service_item.theme_data.background_filename),
492 ':/media/slidecontroller_multimedia.png')492 ':/media/slidecontroller_multimedia.png')
493 self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)493 self.media_controller.video(DisplayControllerType.Live, service_item, video_behind_text=True)
494 self._hide_mouse()494 self._hide_mouse()
495495
=== modified file 'openlp/core/ui/themeform.py'
--- openlp/core/ui/themeform.py 2017-08-25 20:03:25 +0000
+++ openlp/core/ui/themeform.py 2017-09-26 17:09:04 +0000
@@ -28,7 +28,6 @@
28from PyQt5 import QtCore, QtGui, QtWidgets28from PyQt5 import QtCore, QtGui, QtWidgets
2929
30from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file30from openlp.core.common import Registry, RegistryProperties, UiStrings, translate, get_images_filter, is_not_image_file
31from openlp.core.common.path import Path, path_to_str, str_to_path
32from openlp.core.lib.theme import BackgroundType, BackgroundGradientType31from openlp.core.lib.theme import BackgroundType, BackgroundGradientType
33from openlp.core.lib.ui import critical_error_message_box32from openlp.core.lib.ui import critical_error_message_box
34from openlp.core.ui import ThemeLayoutForm33from openlp.core.ui import ThemeLayoutForm
@@ -61,7 +60,7 @@
61 self.setupUi(self)60 self.setupUi(self)
62 self.registerFields()61 self.registerFields()
63 self.update_theme_allowed = True62 self.update_theme_allowed = True
64 self.temp_background_filename = ''63 self.temp_background_filename = None
65 self.theme_layout_form = ThemeLayoutForm(self)64 self.theme_layout_form = ThemeLayoutForm(self)
66 self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed)65 self.background_combo_box.currentIndexChanged.connect(self.on_background_combo_box_current_index_changed)
67 self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed)66 self.gradient_combo_box.currentIndexChanged.connect(self.on_gradient_combo_box_current_index_changed)
@@ -188,8 +187,7 @@
188 """187 """
189 background_image = BackgroundType.to_string(BackgroundType.Image)188 background_image = BackgroundType.to_string(BackgroundType.Image)
190 if self.page(self.currentId()) == self.background_page and \189 if self.page(self.currentId()) == self.background_page and \
191 self.theme.background_type == background_image and \190 self.theme.background_type == background_image and is_not_image_file(self.theme.background_filename):
192 is_not_image_file(Path(self.theme.background_filename)):
193 QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),191 QtWidgets.QMessageBox.critical(self, translate('OpenLP.ThemeWizard', 'Background Image Empty'),
194 translate('OpenLP.ThemeWizard', 'You have not selected a '192 translate('OpenLP.ThemeWizard', 'You have not selected a '
195 'background image. Please select one before continuing.'))193 'background image. Please select one before continuing.'))
@@ -273,7 +271,7 @@
273 Run the wizard.271 Run the wizard.
274 """272 """
275 log.debug('Editing theme {name}'.format(name=self.theme.theme_name))273 log.debug('Editing theme {name}'.format(name=self.theme.theme_name))
276 self.temp_background_filename = ''274 self.temp_background_filename = None
277 self.update_theme_allowed = False275 self.update_theme_allowed = False
278 self.set_defaults()276 self.set_defaults()
279 self.update_theme_allowed = True277 self.update_theme_allowed = True
@@ -318,11 +316,11 @@
318 self.setField('background_type', 1)316 self.setField('background_type', 1)
319 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):317 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Image):
320 self.image_color_button.color = self.theme.background_border_color318 self.image_color_button.color = self.theme.background_border_color
321 self.image_path_edit.path = str_to_path(self.theme.background_filename)319 self.image_path_edit.path = self.theme.background_filename
322 self.setField('background_type', 2)320 self.setField('background_type', 2)
323 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):321 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
324 self.video_color_button.color = self.theme.background_border_color322 self.video_color_button.color = self.theme.background_border_color
325 self.video_path_edit.path = str_to_path(self.theme.background_filename)323 self.video_path_edit.path = self.theme.background_filename
326 self.setField('background_type', 4)324 self.setField('background_type', 4)
327 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):325 elif self.theme.background_type == BackgroundType.to_string(BackgroundType.Transparent):
328 self.setField('background_type', 3)326 self.setField('background_type', 3)
@@ -402,14 +400,14 @@
402 self.theme.background_type = BackgroundType.to_string(index)400 self.theme.background_type = BackgroundType.to_string(index)
403 if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \401 if self.theme.background_type != BackgroundType.to_string(BackgroundType.Image) and \
404 self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \402 self.theme.background_type != BackgroundType.to_string(BackgroundType.Video) and \
405 self.temp_background_filename == '':403 self.temp_background_filename is None:
406 self.temp_background_filename = self.theme.background_filename404 self.temp_background_filename = self.theme.background_filename
407 self.theme.background_filename = ''405 self.theme.background_filename = None
408 if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or406 if (self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or
409 self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \407 self.theme.background_type != BackgroundType.to_string(BackgroundType.Video)) and \
410 self.temp_background_filename != '':408 self.temp_background_filename is not None:
411 self.theme.background_filename = self.temp_background_filename409 self.theme.background_filename = self.temp_background_filename
412 self.temp_background_filename = ''410 self.temp_background_filename = None
413 self.set_background_page_values()411 self.set_background_page_values()
414412
415 def on_gradient_combo_box_current_index_changed(self, index):413 def on_gradient_combo_box_current_index_changed(self, index):
@@ -450,18 +448,24 @@
450 """448 """
451 self.theme.background_end_color = color449 self.theme.background_end_color = color
452450
453 def on_image_path_edit_path_changed(self, file_path):451 def on_image_path_edit_path_changed(self, new_path):
454 """452 """
455 Background Image button pushed.453 Handle the `pathEditChanged` signal from image_path_edit
456 """454
457 self.theme.background_filename = path_to_str(file_path)455 :param openlp.core.common.path.Path new_path: Path to the new image
456 :rtype: None
457 """
458 self.theme.background_filename = new_path
458 self.set_background_page_values()459 self.set_background_page_values()
459460
460 def on_video_path_edit_path_changed(self, file_path):461 def on_video_path_edit_path_changed(self, new_path):
461 """462 """
462 Background video button pushed.463 Handle the `pathEditChanged` signal from video_path_edit
463 """464
464 self.theme.background_filename = path_to_str(file_path)465 :param openlp.core.common.path.Path new_path: Path to the new video
466 :rtype: None
467 """
468 self.theme.background_filename = new_path
465 self.set_background_page_values()469 self.set_background_page_values()
466470
467 def on_main_color_changed(self, color):471 def on_main_color_changed(self, color):
@@ -537,14 +541,14 @@
537 translate('OpenLP.ThemeWizard', 'Theme Name Invalid'),541 translate('OpenLP.ThemeWizard', 'Theme Name Invalid'),
538 translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))542 translate('OpenLP.ThemeWizard', 'Invalid theme name. Please enter one.'))
539 return543 return
540 save_from = None544 source_path = None
541 save_to = None545 destination_path = None
542 if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \546 if self.theme.background_type == BackgroundType.to_string(BackgroundType.Image) or \
543 self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):547 self.theme.background_type == BackgroundType.to_string(BackgroundType.Video):
544 filename = os.path.split(str(self.theme.background_filename))[1]548 file_name = self.theme.background_filename.name
545 save_to = os.path.join(self.path, self.theme.theme_name, filename)549 destination_path = self.path / self.theme.theme_name / file_name
546 save_from = self.theme.background_filename550 source_path = self.theme.background_filename
547 if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):551 if not self.edit_mode and not self.theme_manager.check_if_theme_exists(self.theme.theme_name):
548 return552 return
549 self.theme_manager.save_theme(self.theme, save_from, save_to)553 self.theme_manager.save_theme(self.theme, source_path, destination_path)
550 return QtWidgets.QDialog.accept(self)554 return QtWidgets.QDialog.accept(self)
551555
=== modified file 'openlp/core/ui/thememanager.py'
--- openlp/core/ui/thememanager.py 2017-09-17 19:43:15 +0000
+++ openlp/core/ui/thememanager.py 2017-09-26 17:09:04 +0000
@@ -24,14 +24,14 @@
24"""24"""
25import os25import os
26import zipfile26import zipfile
27import shutil
28
29from xml.etree.ElementTree import ElementTree, XML27from xml.etree.ElementTree import ElementTree, XML
28
30from PyQt5 import QtCore, QtGui, QtWidgets29from PyQt5 import QtCore, QtGui, QtWidgets
3130
32from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \31from openlp.core.common import Registry, RegistryProperties, AppLocation, Settings, OpenLPMixin, RegistryMixin, \
33 UiStrings, check_directory_exists, translate, is_win, get_filesystem_encoding, delete_file32 UiStrings, check_directory_exists, translate, delete_file
34from openlp.core.common.path import Path, path_to_str, str_to_path33from openlp.core.common.languagemanager import get_locale_key
34from openlp.core.common.path import Path, copyfile, path_to_str, rmtree
35from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \35from openlp.core.lib import ImageSource, ValidationError, get_text_file_string, build_icon, \
36 check_item_selected, create_thumb, validate_thumb36 check_item_selected, create_thumb, validate_thumb
37from openlp.core.lib.theme import Theme, BackgroundType37from openlp.core.lib.theme import Theme, BackgroundType
@@ -39,7 +39,6 @@
39from openlp.core.ui import FileRenameForm, ThemeForm39from openlp.core.ui import FileRenameForm, ThemeForm
40from openlp.core.ui.lib import OpenLPToolbar40from openlp.core.ui.lib import OpenLPToolbar
41from openlp.core.ui.lib.filedialog import FileDialog41from openlp.core.ui.lib.filedialog import FileDialog
42from openlp.core.common.languagemanager import get_locale_key
4342
4443
45class Ui_ThemeManager(object):44class Ui_ThemeManager(object):
@@ -135,7 +134,7 @@
135 self.settings_section = 'themes'134 self.settings_section = 'themes'
136 # Variables135 # Variables
137 self.theme_list = []136 self.theme_list = []
138 self.old_background_image = None137 self.old_background_image_path = None
139138
140 def bootstrap_initialise(self):139 def bootstrap_initialise(self):
141 """140 """
@@ -145,25 +144,41 @@
145 self.global_theme = Settings().value(self.settings_section + '/global theme')144 self.global_theme = Settings().value(self.settings_section + '/global theme')
146 self.build_theme_path()145 self.build_theme_path()
147 self.load_first_time_themes()146 self.load_first_time_themes()
147 self.upgrade_themes()
148148
149 def bootstrap_post_set_up(self):149 def bootstrap_post_set_up(self):
150 """150 """
151 process the bootstrap post setup request151 process the bootstrap post setup request
152 """152 """
153 self.theme_form = ThemeForm(self)153 self.theme_form = ThemeForm(self)
154 self.theme_form.path = self.path154 self.theme_form.path = self.theme_path
155 self.file_rename_form = FileRenameForm()155 self.file_rename_form = FileRenameForm()
156 Registry().register_function('theme_update_global', self.change_global_from_tab)156 Registry().register_function('theme_update_global', self.change_global_from_tab)
157 self.load_themes()157 self.load_themes()
158158
159 def upgrade_themes(self):
160 """
161 Upgrade the xml files to json.
162
163 :rtype: None
164 """
165 xml_file_paths = AppLocation.get_section_data_path('themes').glob('*/*.xml')
166 for xml_file_path in xml_file_paths:
167 theme_data = get_text_file_string(xml_file_path)
168 theme = self._create_theme_from_xml(theme_data, self.theme_path)
169 self._write_theme(theme)
170 xml_file_path.unlink()
171
159 def build_theme_path(self):172 def build_theme_path(self):
160 """173 """
161 Set up the theme path variables174 Set up the theme path variables
175
176 :rtype: None
162 """177 """
163 self.path = str(AppLocation.get_section_data_path(self.settings_section))178 self.theme_path = AppLocation.get_section_data_path(self.settings_section)
164 check_directory_exists(Path(self.path))179 check_directory_exists(self.theme_path)
165 self.thumb_path = os.path.join(self.path, 'thumbnails')180 self.thumb_path = self.theme_path / 'thumbnails'
166 check_directory_exists(Path(self.thumb_path))181 check_directory_exists(self.thumb_path)
167182
168 def check_list_state(self, item, field=None):183 def check_list_state(self, item, field=None):
169 """184 """
@@ -298,17 +313,18 @@
298 """313 """
299 Takes a theme and makes a new copy of it as well as saving it.314 Takes a theme and makes a new copy of it as well as saving it.
300315
301 :param theme_data: The theme to be used316 :param Theme theme_data: The theme to be used
302 :param new_theme_name: The new theme name to save the data to317 :param str new_theme_name: The new theme name of the theme
318 :rtype: None
303 """319 """
304 save_to = None320 destination_path = None
305 save_from = None321 source_path = None
306 if theme_data.background_type == 'image' or theme_data.background_type == 'video':322 if theme_data.background_type == 'image' or theme_data.background_type == 'video':
307 save_to = os.path.join(self.path, new_theme_name, os.path.split(str(theme_data.background_filename))[1])323 destination_path = self.theme_path / new_theme_name / theme_data.background_filename.name
308 save_from = theme_data.background_filename324 source_path = theme_data.background_filename
309 theme_data.theme_name = new_theme_name325 theme_data.theme_name = new_theme_name
310 theme_data.extend_image_filename(self.path)326 theme_data.extend_image_filename(self.theme_path)
311 self.save_theme(theme_data, save_from, save_to)327 self.save_theme(theme_data, source_path, destination_path)
312 self.load_themes()328 self.load_themes()
313329
314 def on_edit_theme(self, field=None):330 def on_edit_theme(self, field=None):
@@ -322,10 +338,10 @@
322 item = self.theme_list_widget.currentItem()338 item = self.theme_list_widget.currentItem()
323 theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))339 theme = self.get_theme_data(item.data(QtCore.Qt.UserRole))
324 if theme.background_type == 'image' or theme.background_type == 'video':340 if theme.background_type == 'image' or theme.background_type == 'video':
325 self.old_background_image = theme.background_filename341 self.old_background_image_path = theme.background_filename
326 self.theme_form.theme = theme342 self.theme_form.theme = theme
327 self.theme_form.exec(True)343 self.theme_form.exec(True)
328 self.old_background_image = None344 self.old_background_image_path = None
329 self.renderer.update_theme(theme.theme_name)345 self.renderer.update_theme(theme.theme_name)
330 self.load_themes()346 self.load_themes()
331347
@@ -355,77 +371,76 @@
355 """371 """
356 self.theme_list.remove(theme)372 self.theme_list.remove(theme)
357 thumb = '{name}.png'.format(name=theme)373 thumb = '{name}.png'.format(name=theme)
358 delete_file(Path(self.path, thumb))374 delete_file(self.theme_path / thumb)
359 delete_file(Path(self.thumb_path, thumb))375 delete_file(self.thumb_path / thumb)
360 try:376 try:
361 # Windows is always unicode, so no need to encode filenames377 rmtree(self.theme_path / theme)
362 if is_win():378 except OSError:
363 shutil.rmtree(os.path.join(self.path, theme))
364 else:
365 encoding = get_filesystem_encoding()
366 shutil.rmtree(os.path.join(self.path, theme).encode(encoding))
367 except OSError as os_error:
368 shutil.Error = os_error
369 self.log_exception('Error deleting theme {name}'.format(name=theme))379 self.log_exception('Error deleting theme {name}'.format(name=theme))
370380
371 def on_export_theme(self, field=None):381 def on_export_theme(self, checked=None):
372 """382 """
373 Export the theme in a zip file383 Export the theme to a zip file
374 :param field:384
385 :param bool checked: Sent by the QAction.triggered signal. It's not used in this method.
386 :rtype: None
375 """387 """
376 item = self.theme_list_widget.currentItem()388 item = self.theme_list_widget.currentItem()
377 if item is None:389 if item is None:
378 critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))390 critical_error_message_box(message=translate('OpenLP.ThemeManager', 'You have not selected a theme.'))
379 return391 return
380 theme = item.data(QtCore.Qt.UserRole)392 theme_name = item.data(QtCore.Qt.UserRole)
381 export_path, filter_used = \393 export_path, filter_used = \
382 FileDialog.getSaveFileName(self.main_window,394 FileDialog.getSaveFileName(self.main_window,
383 translate('OpenLP.ThemeManager', 'Save Theme - ({name})').395 translate('OpenLP.ThemeManager',
384 format(name=theme),396 'Save Theme - ({name})').format(name=theme_name),
385 Settings().value(self.settings_section + '/last directory export'),397 Settings().value(self.settings_section + '/last directory export'),
398 translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'),
386 translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))399 translate('OpenLP.ThemeManager', 'OpenLP Themes (*.otz)'))
387 self.application.set_busy_cursor()400 self.application.set_busy_cursor()
388 if export_path:401 if export_path:
389 Settings().setValue(self.settings_section + '/last directory export', export_path.parent)402 Settings().setValue(self.settings_section + '/last directory export', export_path.parent)
390 if self._export_theme(str(export_path), theme):403 if self._export_theme(export_path.with_suffix('.otz'), theme_name):
391 QtWidgets.QMessageBox.information(self,404 QtWidgets.QMessageBox.information(self,
392 translate('OpenLP.ThemeManager', 'Theme Exported'),405 translate('OpenLP.ThemeManager', 'Theme Exported'),
393 translate('OpenLP.ThemeManager',406 translate('OpenLP.ThemeManager',
394 'Your theme has been successfully exported.'))407 'Your theme has been successfully exported.'))
395 self.application.set_normal_cursor()408 self.application.set_normal_cursor()
396409
397 def _export_theme(self, theme_path, theme):410 def _export_theme(self, theme_path, theme_name):
398 """411 """
399 Create the zipfile with the theme contents.412 Create the zipfile with the theme contents.
400 :param theme_path: Location where the zip file will be placed413
401 :param theme: The name of the theme to be exported414 :param openlp.core.common.path.Path theme_path: Location where the zip file will be placed
415 :param str theme_name: The name of the theme to be exported
416 :return: The success of creating the zip file
417 :rtype: bool
402 """418 """
403 theme_zip = None
404 try:419 try:
405 theme_zip = zipfile.ZipFile(theme_path, 'w')420 with zipfile.ZipFile(str(theme_path), 'w') as theme_zip:
406 source = os.path.join(self.path, theme)421 source_path = self.theme_path / theme_name
407 for files in os.walk(source):422 for file_path in source_path.iterdir():
408 for name in files[2]:423 theme_zip.write(str(file_path), os.path.join(theme_name, file_path.name))
409 theme_zip.write(os.path.join(source, name), os.path.join(theme, name))
410 theme_zip.close()
411 return True424 return True
412 except OSError as ose:425 except OSError as ose:
413 self.log_exception('Export Theme Failed')426 self.log_exception('Export Theme Failed')
414 critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),427 critical_error_message_box(translate('OpenLP.ThemeManager', 'Theme Export Failed'),
415 translate('OpenLP.ThemeManager', 'The theme export failed because this error '428 translate('OpenLP.ThemeManager',
416 'occurred: {err}').format(err=ose.strerror))429 'The theme_name export failed because this error occurred: {err}')
417 if theme_zip:430 .format(err=ose.strerror))
418 theme_zip.close()431 if theme_path.exists():
419 shutil.rmtree(theme_path, True)432 rmtree(theme_path, True)
420 return False433 return False
421434
422 def on_import_theme(self, field=None):435 def on_import_theme(self, checked=None):
423 """436 """
424 Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from437 Opens a file dialog to select the theme file(s) to import before attempting to extract OpenLP themes from
425 those files. This process will only load version 2 themes.438 those files. This process will only load version 2 themes.
426 :param field:439
440 :param bool checked: Sent by the QAction.triggered signal. It's not used in this method.
441 :rtype: None
427 """442 """
428 file_paths, selected_filter = FileDialog.getOpenFileNames(443 file_paths, filter_used = FileDialog.getOpenFileNames(
429 self,444 self,
430 translate('OpenLP.ThemeManager', 'Select Theme Import File'),445 translate('OpenLP.ThemeManager', 'Select Theme Import File'),
431 Settings().value(self.settings_section + '/last directory import'),446 Settings().value(self.settings_section + '/last directory import'),
@@ -435,8 +450,8 @@
435 return450 return
436 self.application.set_busy_cursor()451 self.application.set_busy_cursor()
437 for file_path in file_paths:452 for file_path in file_paths:
438 self.unzip_theme(path_to_str(file_path), self.path)453 self.unzip_theme(file_path, self.theme_path)
439 Settings().setValue(self.settings_section + '/last directory import', file_path)454 Settings().setValue(self.settings_section + '/last directory import', file_path.parent)
440 self.load_themes()455 self.load_themes()
441 self.application.set_normal_cursor()456 self.application.set_normal_cursor()
442457
@@ -445,17 +460,17 @@
445 Imports any themes on start up and makes sure there is at least one theme460 Imports any themes on start up and makes sure there is at least one theme
446 """461 """
447 self.application.set_busy_cursor()462 self.application.set_busy_cursor()
448 files = AppLocation.get_files(self.settings_section, '.otz')463 theme_paths = AppLocation.get_files(self.settings_section, '.otz')
449 for theme_file in files:464 for theme_path in theme_paths:
450 theme_file = os.path.join(self.path, str(theme_file))465 theme_path = self.theme_path / theme_path
451 self.unzip_theme(theme_file, self.path)466 self.unzip_theme(theme_path, self.theme_path)
452 delete_file(Path(theme_file))467 delete_file(theme_path)
453 files = AppLocation.get_files(self.settings_section, '.png')468 theme_paths = AppLocation.get_files(self.settings_section, '.png')
454 # No themes have been found so create one469 # No themes have been found so create one
455 if not files:470 if not theme_paths:
456 theme = Theme()471 theme = Theme()
457 theme.theme_name = UiStrings().Default472 theme.theme_name = UiStrings().Default
458 self._write_theme(theme, None, None)473 self._write_theme(theme)
459 Settings().setValue(self.settings_section + '/global theme', theme.theme_name)474 Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
460 self.application.set_normal_cursor()475 self.application.set_normal_cursor()
461476
@@ -471,22 +486,21 @@
471 # Sort the themes by its name considering language specific486 # Sort the themes by its name considering language specific
472 files.sort(key=lambda file_name: get_locale_key(str(file_name)))487 files.sort(key=lambda file_name: get_locale_key(str(file_name)))
473 # now process the file list of png files488 # now process the file list of png files
474 for name in files:489 for file in files:
475 name = str(name)
476 # check to see file is in theme root directory490 # check to see file is in theme root directory
477 theme = os.path.join(self.path, name)491 theme_path = self.theme_path / file
478 if os.path.exists(theme):492 if theme_path.exists():
479 text_name = os.path.splitext(name)[0]493 text_name = theme_path.stem
480 if text_name == self.global_theme:494 if text_name == self.global_theme:
481 name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)495 name = translate('OpenLP.ThemeManager', '{name} (default)').format(name=text_name)
482 else:496 else:
483 name = text_name497 name = text_name
484 thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=text_name))498 thumb = self.thumb_path / '{name}.png'.format(name=text_name)
485 item_name = QtWidgets.QListWidgetItem(name)499 item_name = QtWidgets.QListWidgetItem(name)
486 if validate_thumb(Path(theme), Path(thumb)):500 if validate_thumb(theme_path, thumb):
487 icon = build_icon(thumb)501 icon = build_icon(thumb)
488 else:502 else:
489 icon = create_thumb(theme, thumb)503 icon = create_thumb(str(theme_path), str(thumb))
490 item_name.setIcon(icon)504 item_name.setIcon(icon)
491 item_name.setData(QtCore.Qt.UserRole, text_name)505 item_name.setData(QtCore.Qt.UserRole, text_name)
492 self.theme_list_widget.addItem(item_name)506 self.theme_list_widget.addItem(item_name)
@@ -507,27 +521,19 @@
507521
508 def get_theme_data(self, theme_name):522 def get_theme_data(self, theme_name):
509 """523 """
510 Returns a theme object from an XML or JSON file524 Returns a theme object from a JSON file
511525
512 :param theme_name: Name of the theme to load from file526 :param str theme_name: Name of the theme to load from file
513 :return: The theme object.527 :return: The theme object.
528 :rtype: Theme
514 """529 """
515 self.log_debug('get theme data for theme {name}'.format(name=theme_name))530 theme_name = str(theme_name)
516 theme_file_path = Path(self.path, str(theme_name), '{file_name}.json'.format(file_name=theme_name))531 theme_file_path = self.theme_path / theme_name / '{file_name}.json'.format(file_name=theme_name)
517 theme_data = get_text_file_string(theme_file_path)532 theme_data = get_text_file_string(theme_file_path)
518 jsn = True
519 if not theme_data:
520 theme_file_path = theme_file_path.with_suffix('.xml')
521 theme_data = get_text_file_string(theme_file_path)
522 jsn = False
523 if not theme_data:533 if not theme_data:
524 self.log_debug('No theme data - using default theme')534 self.log_debug('No theme data - using default theme')
525 return Theme()535 return Theme()
526 else:536 return self._create_theme_from_json(theme_data, self.theme_path)
527 if jsn:
528 return self._create_theme_from_json(theme_data, self.path)
529 else:
530 return self._create_theme_from_xml(theme_data, self.path)
531537
532 def over_write_message_box(self, theme_name):538 def over_write_message_box(self, theme_name):
533 """539 """
@@ -543,172 +549,148 @@
543 defaultButton=QtWidgets.QMessageBox.No)549 defaultButton=QtWidgets.QMessageBox.No)
544 return ret == QtWidgets.QMessageBox.Yes550 return ret == QtWidgets.QMessageBox.Yes
545551
546 def unzip_theme(self, file_name, directory):552 def unzip_theme(self, file_path, directory_path):
547 """553 """
548 Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version554 Unzip the theme, remove the preview file if stored. Generate a new preview file. Check the XML theme version
549 and upgrade if necessary.555 and upgrade if necessary.
550 :param file_name:556 :param openlp.core.common.path.Path file_path:
551 :param directory:557 :param openlp.core.common.path.Path directory_path:
552 """558 """
553 self.log_debug('Unzipping theme {name}'.format(name=file_name))559 self.log_debug('Unzipping theme {name}'.format(name=file_path))
554 theme_zip = None
555 out_file = None
556 file_xml = None560 file_xml = None
557 abort_import = True561 abort_import = True
558 json_theme = False562 json_theme = False
559 theme_name = ""563 theme_name = ""
560 try:564 try:
561 theme_zip = zipfile.ZipFile(file_name)565 with zipfile.ZipFile(str(file_path)) as theme_zip:
562 json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']566 json_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.json']
563 if len(json_file) != 1:567 if len(json_file) != 1:
564 # TODO: remove XML handling at some point but would need a auto conversion to run first.568 # TODO: remove XML handling after the 2.6 release.
565 xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']569 xml_file = [name for name in theme_zip.namelist() if os.path.splitext(name)[1].lower() == '.xml']
566 if len(xml_file) != 1:570 if len(xml_file) != 1:
567 self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))571 self.log_error('Theme contains "{val:d}" theme files'.format(val=len(xml_file)))
568 raise ValidationError572 raise ValidationError
569 xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()573 xml_tree = ElementTree(element=XML(theme_zip.read(xml_file[0]))).getroot()
570 theme_version = xml_tree.get('version', default=None)574 theme_version = xml_tree.get('version', default=None)
571 if not theme_version or float(theme_version) < 2.0:575 if not theme_version or float(theme_version) < 2.0:
572 self.log_error('Theme version is less than 2.0')576 self.log_error('Theme version is less than 2.0')
573 raise ValidationError577 raise ValidationError
574 theme_name = xml_tree.find('name').text.strip()578 theme_name = xml_tree.find('name').text.strip()
575 else:579 else:
576 new_theme = Theme()580 new_theme = Theme()
577 new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8"))581 new_theme.load_theme(theme_zip.read(json_file[0]).decode("utf-8"))
578 theme_name = new_theme.theme_name582 theme_name = new_theme.theme_name
579 json_theme = True583 json_theme = True
580 theme_folder = os.path.join(directory, theme_name)584 theme_folder = directory_path / theme_name
581 theme_exists = os.path.exists(theme_folder)585 if theme_folder.exists() and not self.over_write_message_box(theme_name):
582 if theme_exists and not self.over_write_message_box(theme_name):586 abort_import = True
583 abort_import = True587 return
584 return588 else:
585 else:589 abort_import = False
586 abort_import = False590 for zipped_file in theme_zip.namelist():
587 for name in theme_zip.namelist():591 zipped_file_rel_path = Path(zipped_file)
588 out_name = name.replace('/', os.path.sep)592 split_name = zipped_file_rel_path.parts
589 split_name = out_name.split(os.path.sep)593 if split_name[-1] == '' or len(split_name) == 1:
590 if split_name[-1] == '' or len(split_name) == 1:594 # is directory or preview file
591 # is directory or preview file595 continue
592 continue596 full_name = directory_path / zipped_file_rel_path
593 full_name = os.path.join(directory, out_name)597 check_directory_exists(full_name.parent)
594 check_directory_exists(Path(os.path.dirname(full_name)))598 if zipped_file_rel_path.suffix.lower() == '.xml' or zipped_file_rel_path.suffix.lower() == '.json':
595 if os.path.splitext(name)[1].lower() == '.xml' or os.path.splitext(name)[1].lower() == '.json':599 file_xml = str(theme_zip.read(zipped_file), 'utf-8')
596 file_xml = str(theme_zip.read(name), 'utf-8')600 with full_name.open('w', encoding='utf-8') as out_file:
597 out_file = open(full_name, 'w', encoding='utf-8')601 out_file.write(file_xml)
598 out_file.write(file_xml)602 else:
599 else:603 with full_name.open('wb') as out_file:
600 out_file = open(full_name, 'wb')604 out_file.write(theme_zip.read(zipped_file))
601 out_file.write(theme_zip.read(name))
602 out_file.close()
603 except (IOError, zipfile.BadZipfile):605 except (IOError, zipfile.BadZipfile):
604 self.log_exception('Importing theme from zip failed {name}'.format(name=file_name))606 self.log_exception('Importing theme from zip failed {name}'.format(name=file_path))
605 raise ValidationError607 raise ValidationError
606 except ValidationError:608 except ValidationError:
607 critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),609 critical_error_message_box(translate('OpenLP.ThemeManager', 'Validation Error'),
608 translate('OpenLP.ThemeManager', 'File is not a valid theme.'))610 translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
609 finally:611 finally:
610 # Close the files, to be able to continue creating the theme.
611 if theme_zip:
612 theme_zip.close()
613 if out_file:
614 out_file.close()
615 if not abort_import:612 if not abort_import:
616 # As all files are closed, we can create the Theme.613 # As all files are closed, we can create the Theme.
617 if file_xml:614 if file_xml:
618 if json_theme:615 if json_theme:
619 theme = self._create_theme_from_json(file_xml, self.path)616 theme = self._create_theme_from_json(file_xml, self.theme_path)
620 else:617 else:
621 theme = self._create_theme_from_xml(file_xml, self.path)618 theme = self._create_theme_from_xml(file_xml, self.theme_path)
622 self.generate_and_save_image(theme_name, theme)619 self.generate_and_save_image(theme_name, theme)
623 # Only show the error message, when IOError was not raised (in
624 # this case the error message has already been shown).
625 elif theme_zip is not None:
626 critical_error_message_box(
627 translate('OpenLP.ThemeManager', 'Validation Error'),
628 translate('OpenLP.ThemeManager', 'File is not a valid theme.'))
629 self.log_error('Theme file does not contain XML data {name}'.format(name=file_name))
630620
631 def check_if_theme_exists(self, theme_name):621 def check_if_theme_exists(self, theme_name):
632 """622 """
633 Check if theme already exists and displays error message623 Check if theme already exists and displays error message
634624
635 :param theme_name: Name of the Theme to test625 :param str theme_name: Name of the Theme to test
636 :return: True or False if theme exists626 :return: True or False if theme exists
627 :rtype: bool
637 """628 """
638 theme_dir = os.path.join(self.path, theme_name)629 if (self.theme_path / theme_name).exists():
639 if os.path.exists(theme_dir):
640 critical_error_message_box(630 critical_error_message_box(
641 translate('OpenLP.ThemeManager', 'Validation Error'),631 translate('OpenLP.ThemeManager', 'Validation Error'),
642 translate('OpenLP.ThemeManager', 'A theme with this name already exists.'))632 translate('OpenLP.ThemeManager', 'A theme with this name already exists.'))
643 return False633 return False
644 return True634 return True
645635
646 def save_theme(self, theme, image_from, image_to):636 def save_theme(self, theme, image_source_path, image_destination_path):
647 """637 """
648 Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list638 Called by theme maintenance Dialog to save the theme and to trigger the reload of the theme list
649639
650 :param theme: The theme data object.640 :param Theme theme: The theme data object.
651 :param image_from: Where the theme image is currently located.641 :param openlp.core.common.path.Path image_source_path: Where the theme image is currently located.
652 :param image_to: Where the Theme Image is to be saved to642 :param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to
643 :rtype: None
653 """644 """
654 self._write_theme(theme, image_from, image_to)645 self._write_theme(theme, image_source_path, image_destination_path)
655 if theme.background_type == BackgroundType.to_string(BackgroundType.Image):646 if theme.background_type == BackgroundType.to_string(BackgroundType.Image):
656 self.image_manager.update_image_border(theme.background_filename,647 self.image_manager.update_image_border(path_to_str(theme.background_filename),
657 ImageSource.Theme,648 ImageSource.Theme,
658 QtGui.QColor(theme.background_border_color))649 QtGui.QColor(theme.background_border_color))
659 self.image_manager.process_updates()650 self.image_manager.process_updates()
660651
661 def _write_theme(self, theme, image_from, image_to):652 def _write_theme(self, theme, image_source_path=None, image_destination_path=None):
662 """653 """
663 Writes the theme to the disk and handles the background image if necessary654 Writes the theme to the disk and handles the background image if necessary
664655
665 :param theme: The theme data object.656 :param Theme theme: The theme data object.
666 :param image_from: Where the theme image is currently located.657 :param openlp.core.common.path.Path image_source_path: Where the theme image is currently located.
667 :param image_to: Where the Theme Image is to be saved to658 :param openlp.core.common.path.Path image_destination_path: Where the Theme Image is to be saved to
659 :rtype: None
668 """660 """
669 name = theme.theme_name661 name = theme.theme_name
670 theme_pretty = theme.export_theme()662 theme_pretty = theme.export_theme(self.theme_path)
671 theme_dir = os.path.join(self.path, name)663 theme_dir = self.theme_path / name
672 check_directory_exists(Path(theme_dir))664 check_directory_exists(theme_dir)
673 theme_file = os.path.join(theme_dir, name + '.json')665 theme_path = theme_dir / '{file_name}.json'.format(file_name=name)
674 if self.old_background_image and image_to != self.old_background_image:
675 delete_file(Path(self.old_background_image))
676 out_file = None
677 try:666 try:
678 out_file = open(theme_file, 'w', encoding='utf-8')667 theme_path.write_text(theme_pretty)
679 out_file.write(theme_pretty)
680 except IOError:668 except IOError:
681 self.log_exception('Saving theme to file failed')669 self.log_exception('Saving theme to file failed')
682 finally:670 if image_source_path and image_destination_path:
683 if out_file:671 if self.old_background_image_path and image_destination_path != self.old_background_image_path:
684 out_file.close()672 delete_file(self.old_background_image_path)
685 if image_from and os.path.abspath(image_from) != os.path.abspath(image_to):673 if image_source_path != image_destination_path:
686 try:674 try:
687 # Windows is always unicode, so no need to encode filenames675 copyfile(image_source_path, image_destination_path)
688 if is_win():676 except IOError:
689 shutil.copyfile(image_from, image_to)677 self.log_exception('Failed to save theme image')
690 else:
691 encoding = get_filesystem_encoding()
692 shutil.copyfile(image_from.encode(encoding), image_to.encode(encoding))
693 except IOError as xxx_todo_changeme:
694 shutil.Error = xxx_todo_changeme
695 self.log_exception('Failed to save theme image')
696 self.generate_and_save_image(name, theme)678 self.generate_and_save_image(name, theme)
697679
698 def generate_and_save_image(self, name, theme):680 def generate_and_save_image(self, theme_name, theme):
699 """681 """
700 Generate and save a preview image682 Generate and save a preview image
701683
702 :param name: The name of the theme.684 :param str theme_name: The name of the theme.
703 :param theme: The theme data object.685 :param theme: The theme data object.
704 """686 """
705 frame = self.generate_image(theme)687 frame = self.generate_image(theme)
706 sample_path_name = os.path.join(self.path, name + '.png')688 sample_path_name = self.theme_path / '{file_name}.png'.format(file_name=theme_name)
707 if os.path.exists(sample_path_name):689 if sample_path_name.exists():
708 os.unlink(sample_path_name)690 sample_path_name.unlink()
709 frame.save(sample_path_name, 'png')691 frame.save(str(sample_path_name), 'png')
710 thumb = os.path.join(self.thumb_path, '{name}.png'.format(name=name))692 thumb_path = self.thumb_path / '{name}.png'.format(name=theme_name)
711 create_thumb(sample_path_name, thumb, False)693 create_thumb(str(sample_path_name), str(thumb_path), False)
712694
713 def update_preview_images(self):695 def update_preview_images(self):
714 """696 """
@@ -730,39 +712,32 @@
730 """712 """
731 return self.renderer.generate_preview(theme_data, force_page)713 return self.renderer.generate_preview(theme_data, force_page)
732714
733 def get_preview_image(self, theme):
734 """
735 Return an image representing the look of the theme
736
737 :param theme: The theme to return the image for.
738 """
739 return os.path.join(self.path, theme + '.png')
740
741 @staticmethod715 @staticmethod
742 def _create_theme_from_xml(theme_xml, image_path):716 def _create_theme_from_xml(theme_xml, image_path):
743 """717 """
744 Return a theme object using information parsed from XML718 Return a theme object using information parsed from XML
745719
746 :param theme_xml: The Theme data object.720 :param theme_xml: The Theme data object.
747 :param image_path: Where the theme image is stored721 :param openlp.core.common.path.Path image_path: Where the theme image is stored
748 :return: Theme data.722 :return: Theme data.
723 :rtype: Theme
749 """724 """
750 theme = Theme()725 theme = Theme()
751 theme.parse(theme_xml)726 theme.parse(theme_xml)
752 theme.extend_image_filename(image_path)727 theme.extend_image_filename(image_path)
753 return theme728 return theme
754729
755 @staticmethod730 def _create_theme_from_json(self, theme_json, image_path):
756 def _create_theme_from_json(theme_json, image_path):
757 """731 """
758 Return a theme object using information parsed from JSON732 Return a theme object using information parsed from JSON
759733
760 :param theme_json: The Theme data object.734 :param theme_json: The Theme data object.
761 :param image_path: Where the theme image is stored735 :param openlp.core.common.path.Path image_path: Where the theme image is stored
762 :return: Theme data.736 :return: Theme data.
737 :rtype: Theme
763 """738 """
764 theme = Theme()739 theme = Theme()
765 theme.load_theme(theme_json)740 theme.load_theme(theme_json, self.theme_path)
766 theme.extend_image_filename(image_path)741 theme.extend_image_filename(image_path)
767 return theme742 return theme
768743
769744
=== modified file 'openlp/core/ui/themestab.py'
--- openlp/core/ui/themestab.py 2016-12-31 11:01:36 +0000
+++ openlp/core/ui/themestab.py 2017-09-26 17:09:04 +0000
@@ -211,8 +211,8 @@
211 """211 """
212 Utility method to update the global theme preview image.212 Utility method to update the global theme preview image.
213 """213 """
214 image = self.theme_manager.get_preview_image(self.global_theme)214 image_path = self.theme_manager.theme_path / '{file_name}.png'.format(file_name=self.global_theme)
215 preview = QtGui.QPixmap(str(image))215 preview = QtGui.QPixmap(str(image_path))
216 if not preview.isNull():216 if not preview.isNull():
217 preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)217 preview = preview.scaled(300, 255, QtCore.Qt.KeepAspectRatio, QtCore.Qt.SmoothTransformation)
218 self.default_list_view.setPixmap(preview)218 self.default_list_view.setPixmap(preview)
219219
=== modified file 'openlp/plugins/images/lib/upgrade.py'
--- openlp/plugins/images/lib/upgrade.py 2017-09-23 13:06:42 +0000
+++ openlp/plugins/images/lib/upgrade.py 2017-09-26 17:09:04 +0000
@@ -48,7 +48,6 @@
48 """48 """
49 Version 2 upgrade - Move file path from old db to JSON encoded path to new db. Added during 2.5 dev49 Version 2 upgrade - Move file path from old db to JSON encoded path to new db. Added during 2.5 dev
50 """50 """
51 # TODO: Update tests
52 log.debug('Starting upgrade_2 for file_path to JSON')51 log.debug('Starting upgrade_2 for file_path to JSON')
53 old_table = Table('image_filenames', metadata, autoload=True)52 old_table = Table('image_filenames', metadata, autoload=True)
54 if 'file_path' not in [col.name for col in old_table.c.values()]:53 if 'file_path' not in [col.name for col in old_table.c.values()]:
5554
=== modified file 'tests/functional/openlp_core_lib/test_theme.py'
--- tests/functional/openlp_core_lib/test_theme.py 2017-05-24 20:04:48 +0000
+++ tests/functional/openlp_core_lib/test_theme.py 2017-09-26 17:09:04 +0000
@@ -22,8 +22,9 @@
22"""22"""
23Package to test the openlp.core.lib.theme package.23Package to test the openlp.core.lib.theme package.
24"""24"""
25import os
26from pathlib import Path
25from unittest import TestCase27from unittest import TestCase
26import os
2728
28from openlp.core.lib.theme import Theme29from openlp.core.lib.theme import Theme
2930
@@ -79,16 +80,16 @@
79 """80 """
80 # GIVEN: A theme object81 # GIVEN: A theme object
81 theme = Theme()82 theme = Theme()
82 theme.theme_name = 'MyBeautifulTheme '83 theme.theme_name = 'MyBeautifulTheme'
83 theme.background_filename = ' video.mp4'84 theme.background_filename = Path('video.mp4')
84 theme.background_type = 'video'85 theme.background_type = 'video'
85 path = os.path.expanduser('~')86 path = Path.home()
8687
87 # WHEN: Theme.extend_image_filename is run88 # WHEN: Theme.extend_image_filename is run
88 theme.extend_image_filename(path)89 theme.extend_image_filename(path)
8990
90 # THEN: The filename of the background should be correct91 # THEN: The filename of the background should be correct
91 expected_filename = os.path.join(path, 'MyBeautifulTheme', 'video.mp4')92 expected_filename = path / 'MyBeautifulTheme' / 'video.mp4'
92 self.assertEqual(expected_filename, theme.background_filename)93 self.assertEqual(expected_filename, theme.background_filename)
93 self.assertEqual('MyBeautifulTheme', theme.theme_name)94 self.assertEqual('MyBeautifulTheme', theme.theme_name)
9495
9596
=== modified file 'tests/functional/openlp_core_ui/test_maindisplay.py'
--- tests/functional/openlp_core_ui/test_maindisplay.py 2017-07-08 13:12:31 +0000
+++ tests/functional/openlp_core_ui/test_maindisplay.py 2017-09-26 17:09:04 +0000
@@ -27,10 +27,10 @@
2727
28from PyQt5 import QtCore28from PyQt5 import QtCore
2929
30from openlp.core.common import Registry, is_macosx, Settings30from openlp.core.common import Registry, is_macosx
31from openlp.core.common.path import Path
31from openlp.core.lib import ScreenList, PluginManager32from openlp.core.lib import ScreenList, PluginManager
32from openlp.core.ui import MainDisplay, AudioPlayer33from openlp.core.ui import MainDisplay, AudioPlayer
33from openlp.core.ui.media import MediaController
34from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET34from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
3535
36from tests.helpers.testmixin import TestMixin36from tests.helpers.testmixin import TestMixin
@@ -184,7 +184,7 @@
184 self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,184 self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,
185 'Window collection behavior should be NSWindowCollectionBehaviorManaged')185 'Window collection behavior should be NSWindowCollectionBehaviorManaged')
186186
187 @patch(u'openlp.core.ui.maindisplay.Settings')187 @patch('openlp.core.ui.maindisplay.Settings')
188 def test_show_display_startup_logo(self, MockedSettings):188 def test_show_display_startup_logo(self, MockedSettings):
189 # GIVEN: Mocked show_display, setting for logo visibility189 # GIVEN: Mocked show_display, setting for logo visibility
190 display = MagicMock()190 display = MagicMock()
@@ -204,7 +204,7 @@
204 # THEN: setVisible should had been called with "True"204 # THEN: setVisible should had been called with "True"
205 main_display.setVisible.assert_called_once_with(True)205 main_display.setVisible.assert_called_once_with(True)
206206
207 @patch(u'openlp.core.ui.maindisplay.Settings')207 @patch('openlp.core.ui.maindisplay.Settings')
208 def test_show_display_hide_startup_logo(self, MockedSettings):208 def test_show_display_hide_startup_logo(self, MockedSettings):
209 # GIVEN: Mocked show_display, setting for logo visibility209 # GIVEN: Mocked show_display, setting for logo visibility
210 display = MagicMock()210 display = MagicMock()
@@ -224,8 +224,8 @@
224 # THEN: setVisible should had not been called224 # THEN: setVisible should had not been called
225 main_display.setVisible.assert_not_called()225 main_display.setVisible.assert_not_called()
226226
227 @patch(u'openlp.core.ui.maindisplay.Settings')227 @patch('openlp.core.ui.maindisplay.Settings')
228 @patch(u'openlp.core.ui.maindisplay.build_html')228 @patch('openlp.core.ui.maindisplay.build_html')
229 def test_build_html_no_video(self, MockedSettings, Mocked_build_html):229 def test_build_html_no_video(self, MockedSettings, Mocked_build_html):
230 # GIVEN: Mocked display230 # GIVEN: Mocked display
231 display = MagicMock()231 display = MagicMock()
@@ -252,8 +252,8 @@
252 self.assertEquals(main_display.media_controller.video.call_count, 0,252 self.assertEquals(main_display.media_controller.video.call_count, 0,
253 'Media Controller video should not have been called')253 'Media Controller video should not have been called')
254254
255 @patch(u'openlp.core.ui.maindisplay.Settings')255 @patch('openlp.core.ui.maindisplay.Settings')
256 @patch(u'openlp.core.ui.maindisplay.build_html')256 @patch('openlp.core.ui.maindisplay.build_html')
257 def test_build_html_video(self, MockedSettings, Mocked_build_html):257 def test_build_html_video(self, MockedSettings, Mocked_build_html):
258 # GIVEN: Mocked display258 # GIVEN: Mocked display
259 display = MagicMock()259 display = MagicMock()
@@ -270,7 +270,7 @@
270 service_item.theme_data = MagicMock()270 service_item.theme_data = MagicMock()
271 service_item.theme_data.background_type = 'video'271 service_item.theme_data.background_type = 'video'
272 service_item.theme_data.theme_name = 'name'272 service_item.theme_data.theme_name = 'name'
273 service_item.theme_data.background_filename = 'background_filename'273 service_item.theme_data.background_filename = Path('background_filename')
274 mocked_plugin = MagicMock()274 mocked_plugin = MagicMock()
275 display.plugin_manager = PluginManager()275 display.plugin_manager = PluginManager()
276 display.plugin_manager.plugins = [mocked_plugin]276 display.plugin_manager.plugins = [mocked_plugin]
277277
=== modified file 'tests/functional/openlp_core_ui/test_themeform.py'
--- tests/functional/openlp_core_ui/test_themeform.py 2017-08-25 20:03:25 +0000
+++ tests/functional/openlp_core_ui/test_themeform.py 2017-09-26 17:09:04 +0000
@@ -49,5 +49,5 @@
49 self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h'))49 self.instance.on_image_path_edit_path_changed(Path('/', 'new', 'pat.h'))
5050
51 # THEN: The theme background file should be set and `set_background_page_values` should have been called51 # THEN: The theme background file should be set and `set_background_page_values` should have been called
52 self.assertEqual(self.instance.theme.background_filename, '/new/pat.h')52 self.assertEqual(self.instance.theme.background_filename, Path('/', 'new', 'pat.h'))
53 mocked_set_background_page_values.assert_called_once_with()53 mocked_set_background_page_values.assert_called_once_with()
5454
=== modified file 'tests/functional/openlp_core_ui/test_thememanager.py'
--- tests/functional/openlp_core_ui/test_thememanager.py 2017-08-12 17:45:56 +0000
+++ tests/functional/openlp_core_ui/test_thememanager.py 2017-09-26 17:09:04 +0000
@@ -30,8 +30,9 @@
3030
31from PyQt5 import QtWidgets31from PyQt5 import QtWidgets
3232
33from openlp.core.common import Registry
34from openlp.core.common.path import Path
33from openlp.core.ui import ThemeManager35from openlp.core.ui import ThemeManager
34from openlp.core.common import Registry
3536
36from tests.utils.constants import TEST_RESOURCES_PATH37from tests.utils.constants import TEST_RESOURCES_PATH
3738
@@ -57,13 +58,13 @@
57 """58 """
58 # GIVEN: A new ThemeManager instance.59 # GIVEN: A new ThemeManager instance.
59 theme_manager = ThemeManager()60 theme_manager = ThemeManager()
60 theme_manager.path = os.path.join(TEST_RESOURCES_PATH, 'themes')61 theme_manager.theme_path = Path(TEST_RESOURCES_PATH, 'themes')
61 with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \62 with patch('zipfile.ZipFile.__init__') as mocked_zipfile_init, \
62 patch('zipfile.ZipFile.write') as mocked_zipfile_write:63 patch('zipfile.ZipFile.write') as mocked_zipfile_write:
63 mocked_zipfile_init.return_value = None64 mocked_zipfile_init.return_value = None
6465
65 # WHEN: The theme is exported66 # WHEN: The theme is exported
66 theme_manager._export_theme(os.path.join('some', 'path', 'Default.otz'), 'Default')67 theme_manager._export_theme(Path('some', 'path', 'Default.otz'), 'Default')
6768
68 # THEN: The zipfile should be created at the given path69 # THEN: The zipfile should be created at the given path
69 mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')70 mocked_zipfile_init.assert_called_with(os.path.join('some', 'path', 'Default.otz'), 'w')
@@ -86,57 +87,49 @@
86 """87 """
87 Test that we don't try to overwrite a theme background image with itself88 Test that we don't try to overwrite a theme background image with itself
88 """89 """
89 # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,90 # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
90 # theme, check_directory_exists and thememanager-attributes.91 # theme, check_directory_exists and thememanager-attributes.
91 with patch('builtins.open') as mocked_open, \92 with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
92 patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
93 patch('openlp.core.ui.thememanager.check_directory_exists'):93 patch('openlp.core.ui.thememanager.check_directory_exists'):
94 mocked_open.return_value = MagicMock()
95 theme_manager = ThemeManager(None)94 theme_manager = ThemeManager(None)
96 theme_manager.old_background_image = None95 theme_manager.old_background_image = None
97 theme_manager.generate_and_save_image = MagicMock()96 theme_manager.generate_and_save_image = MagicMock()
98 theme_manager.path = ''97 theme_manager.theme_path = MagicMock()
99 mocked_theme = MagicMock()98 mocked_theme = MagicMock()
100 mocked_theme.theme_name = 'themename'99 mocked_theme.theme_name = 'themename'
101 mocked_theme.extract_formatted_xml = MagicMock()100 mocked_theme.extract_formatted_xml = MagicMock()
102 mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()101 mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
103102
104 # WHEN: Calling _write_theme with path to the same image, but the path written slightly different103 # WHEN: Calling _write_theme with path to the same image, but the path written slightly different
105 file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')104 file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
106 # Do replacement from end of string to avoid problems with path start105 theme_manager._write_theme(mocked_theme, file_name1, file_name1)
107 file_name2 = file_name1[::-1].replace(os.sep, os.sep + os.sep, 2)[::-1]
108 theme_manager._write_theme(mocked_theme, file_name1, file_name2)
109106
110 # THEN: The mocked_copyfile should not have been called107 # THEN: The mocked_copyfile should not have been called
111 self.assertFalse(mocked_copyfile.called, 'shutil.copyfile should not be called')108 self.assertFalse(mocked_copyfile.called, 'copyfile should not be called')
112109
113 def test_write_theme_diff_images(self):110 def test_write_theme_diff_images(self):
114 """111 """
115 Test that we do overwrite a theme background image when a new is submitted112 Test that we do overwrite a theme background image when a new is submitted
116 """113 """
117 # GIVEN: A new theme manager instance, with mocked builtins.open, shutil.copyfile,114 # GIVEN: A new theme manager instance, with mocked builtins.open, copyfile,
118 # theme, check_directory_exists and thememanager-attributes.115 # theme, check_directory_exists and thememanager-attributes.
119 with patch('builtins.open') as mocked_open, \116 with patch('openlp.core.ui.thememanager.copyfile') as mocked_copyfile, \
120 patch('openlp.core.ui.thememanager.shutil.copyfile') as mocked_copyfile, \
121 patch('openlp.core.ui.thememanager.check_directory_exists'):117 patch('openlp.core.ui.thememanager.check_directory_exists'):
122 mocked_open.return_value = MagicMock()
123 theme_manager = ThemeManager(None)118 theme_manager = ThemeManager(None)
124 theme_manager.old_background_image = None119 theme_manager.old_background_image = None
125 theme_manager.generate_and_save_image = MagicMock()120 theme_manager.generate_and_save_image = MagicMock()
126 theme_manager.path = ''121 theme_manager.theme_path = MagicMock()
127 mocked_theme = MagicMock()122 mocked_theme = MagicMock()
128 mocked_theme.theme_name = 'themename'123 mocked_theme.theme_name = 'themename'
129 mocked_theme.filename = "filename"124 mocked_theme.filename = "filename"
130 # mocked_theme.extract_formatted_xml = MagicMock()
131 # mocked_theme.extract_formatted_xml.return_value = 'fake_theme_xml'.encode()
132125
133 # WHEN: Calling _write_theme with path to different images126 # WHEN: Calling _write_theme with path to different images
134 file_name1 = os.path.join(TEST_RESOURCES_PATH, 'church.jpg')127 file_name1 = Path(TEST_RESOURCES_PATH, 'church.jpg')
135 file_name2 = os.path.join(TEST_RESOURCES_PATH, 'church2.jpg')128 file_name2 = Path(TEST_RESOURCES_PATH, 'church2.jpg')
136 theme_manager._write_theme(mocked_theme, file_name1, file_name2)129 theme_manager._write_theme(mocked_theme, file_name1, file_name2)
137130
138 # THEN: The mocked_copyfile should not have been called131 # THEN: The mocked_copyfile should not have been called
139 self.assertTrue(mocked_copyfile.called, 'shutil.copyfile should be called')132 self.assertTrue(mocked_copyfile.called, 'copyfile should be called')
140133
141 def test_write_theme_special_char_name(self):134 def test_write_theme_special_char_name(self):
142 """135 """
@@ -146,7 +139,7 @@
146 theme_manager = ThemeManager(None)139 theme_manager = ThemeManager(None)
147 theme_manager.old_background_image = None140 theme_manager.old_background_image = None
148 theme_manager.generate_and_save_image = MagicMock()141 theme_manager.generate_and_save_image = MagicMock()
149 theme_manager.path = self.temp_folder142 theme_manager.theme_path = Path(self.temp_folder)
150 mocked_theme = MagicMock()143 mocked_theme = MagicMock()
151 mocked_theme.theme_name = 'theme 愛 name'144 mocked_theme.theme_name = 'theme 愛 name'
152 mocked_theme.export_theme.return_value = "{}"145 mocked_theme.export_theme.return_value = "{}"
@@ -208,17 +201,17 @@
208 theme_manager = ThemeManager(None)201 theme_manager = ThemeManager(None)
209 theme_manager._create_theme_from_xml = MagicMock()202 theme_manager._create_theme_from_xml = MagicMock()
210 theme_manager.generate_and_save_image = MagicMock()203 theme_manager.generate_and_save_image = MagicMock()
211 theme_manager.path = ''204 theme_manager.theme_path = None
212 folder = mkdtemp()205 folder = Path(mkdtemp())
213 theme_file = os.path.join(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')206 theme_file = Path(TEST_RESOURCES_PATH, 'themes', 'Moss_on_tree.otz')
214207
215 # WHEN: We try to unzip it208 # WHEN: We try to unzip it
216 theme_manager.unzip_theme(theme_file, folder)209 theme_manager.unzip_theme(theme_file, folder)
217210
218 # THEN: Files should be unpacked211 # THEN: Files should be unpacked
219 self.assertTrue(os.path.exists(os.path.join(folder, 'Moss on tree', 'Moss on tree.xml')))212 self.assertTrue((folder / 'Moss on tree' / 'Moss on tree.xml').exists())
220 self.assertEqual(mocked_critical_error_message_box.call_count, 0, 'No errors should have happened')213 self.assertEqual(mocked_critical_error_message_box.call_count, 0, 'No errors should have happened')
221 shutil.rmtree(folder)214 shutil.rmtree(str(folder))
222215
223 def test_unzip_theme_invalid_version(self):216 def test_unzip_theme_invalid_version(self):
224 """217 """
225218
=== modified file 'tests/interfaces/openlp_core_ui/test_thememanager.py'
--- tests/interfaces/openlp_core_ui/test_thememanager.py 2017-04-24 05:17:55 +0000
+++ tests/interfaces/openlp_core_ui/test_thememanager.py 2017-09-26 17:09:04 +0000
@@ -26,7 +26,8 @@
26from unittest.mock import patch, MagicMock26from unittest.mock import patch, MagicMock
2727
28from openlp.core.common import Registry, Settings28from openlp.core.common import Registry, Settings
29from openlp.core.ui import ThemeManager, ThemeForm, FileRenameForm29from openlp.core.common.path import Path
30from openlp.core.ui import ThemeManager
3031
31from tests.helpers.testmixin import TestMixin32from tests.helpers.testmixin import TestMixin
3233
@@ -91,6 +92,23 @@
91 assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \92 assert self.theme_manager.thumb_path.startswith(self.theme_manager.path) is True, \
92 'The thumb path and the main path should start with the same value'93 'The thumb path and the main path should start with the same value'
9394
95 def test_build_theme_path(self):
96 """
97 Test the thememanager build_theme_path - basic test
98 """
99 # GIVEN: A new a call to initialise
100 with patch('openlp.core.common.AppLocation.get_section_data_path', return_value=Path('test/path')):
101 Settings().setValue('themes/global theme', 'my_theme')
102
103 self.theme_manager.theme_form = MagicMock()
104 self.theme_manager.load_first_time_themes = MagicMock()
105
106 # WHEN: the build_theme_path is run
107 self.theme_manager.build_theme_path()
108
109 # THEN: The thumbnail path should be a sub path of the test path
110 self.assertEqual(self.theme_manager.thumb_path, Path('test/path/thumbnails'))
111
94 def test_click_on_new_theme(self):112 def test_click_on_new_theme(self):
95 """113 """
96 Test the on_add_theme event handler is called by the UI114 Test the on_add_theme event handler is called by the UI
@@ -109,17 +127,16 @@
109127
110 @patch('openlp.core.ui.themeform.ThemeForm._setup')128 @patch('openlp.core.ui.themeform.ThemeForm._setup')
111 @patch('openlp.core.ui.filerenameform.FileRenameForm._setup')129 @patch('openlp.core.ui.filerenameform.FileRenameForm._setup')
112 def test_bootstrap_post(self, mocked_theme_form, mocked_rename_form):130 def test_bootstrap_post(self, mocked_rename_form, mocked_theme_form):
113 """131 """
114 Test the functions of bootstrap_post_setup are called.132 Test the functions of bootstrap_post_setup are called.
115 """133 """
116 # GIVEN:134 # GIVEN:
117 self.theme_manager.load_themes = MagicMock()135 self.theme_manager.load_themes = MagicMock()
118 self.theme_manager.path = MagicMock()136 self.theme_manager.theme_path = MagicMock()
119137
120 # WHEN:138 # WHEN:
121 self.theme_manager.bootstrap_post_set_up()139 self.theme_manager.bootstrap_post_set_up()
122140
123 # THEN:141 # THEN:
124 self.assertEqual(self.theme_manager.path, self.theme_manager.theme_form.path)
125 self.assertEqual(1, self.theme_manager.load_themes.call_count, "load_themes should have been called once")142 self.assertEqual(1, self.theme_manager.load_themes.call_count, "load_themes should have been called once")