diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py index 85fe240d4..3cbdf4c33 100644 --- a/openlp/core/ui/slidecontroller.py +++ b/openlp/core/ui/slidecontroller.py @@ -493,6 +493,11 @@ class SlideController(DisplayController, RegistryProperties): """ self.display.setVisible(False) self.media_controller.media_stop(self) + # Stop looping if active + if self.play_slides_loop.isChecked(): + self.on_play_slides_loop(False) + elif self.play_slides_once.isChecked(): + self.on_play_slides_once(False) def toggle_display(self, action): """ diff --git a/openlp/plugins/remotes/html/openlp.js b/openlp/plugins/remotes/html/openlp.js index 9f18c1552..2014988b8 100644 --- a/openlp/plugins/remotes/html/openlp.js +++ b/openlp/plugins/remotes/html/openlp.js @@ -67,8 +67,12 @@ window.OpenLP = { var ul = $("#service-manager > div[data-role=content] > ul[data-role=listview]"); ul.html(""); $.each(data.results.items, function (idx, value) { + var text = value["title"]; + if (value["notes"]) { + text += ' - ' + value["notes"]; + } var li = $("
  • ").append( - $("").attr("value", parseInt(idx, 10)).text(value["title"])); + $("").attr("value", parseInt(idx, 10)).text(text)); li.attr("uuid", value["id"]) li.children("a").click(OpenLP.setItem); ul.append(li); @@ -98,8 +102,8 @@ window.OpenLP = { } else { text += slide["text"]; } - if (slide["notes"]) { - text += ("
    " + slide["notes"] + "
    "); + if (slide["slide_notes"]) { + text += ("
    " + slide["slide_notes"] + "
    "); } text = text.replace(/\n/g, '
    '); if (slide["img"]) { diff --git a/openlp/plugins/remotes/html/stage.js b/openlp/plugins/remotes/html/stage.js index e63025b80..4834b4664 100644 --- a/openlp/plugins/remotes/html/stage.js +++ b/openlp/plugins/remotes/html/stage.js @@ -114,8 +114,8 @@ window.OpenLP = { text += "

    "; } // use notes if available - if (slide["notes"]) { - text += '
    ' + slide["notes"]; + if (slide["slide_notes"]) { + text += '
    ' + slide["slide_notes"]; } text = text.replace(/\n/g, "
    "); $("#currentslide").html(text); diff --git a/openlp/plugins/remotes/lib/httprouter.py b/openlp/plugins/remotes/lib/httprouter.py index 22e2495c0..1313c9f9c 100644 --- a/openlp/plugins/remotes/lib/httprouter.py +++ b/openlp/plugins/remotes/lib/httprouter.py @@ -521,7 +521,7 @@ class HttpRouter(RegistryProperties): if current_item.is_capable(ItemCapabilities.HasDisplayTitle): item['title'] = str(frame['display_title']) if current_item.is_capable(ItemCapabilities.HasNotes): - item['notes'] = str(frame['notes']) + item['slide_notes'] = str(frame['notes']) if current_item.is_capable(ItemCapabilities.HasThumbnails) and \ Settings().value('remotes/thumbnails'): # If the file is under our app directory tree send the portion after the match @@ -531,8 +531,6 @@ class HttpRouter(RegistryProperties): item['text'] = str(frame['title']) item['html'] = str(frame['title']) item['selected'] = (self.live_controller.selected_row == index) - if current_item.notes: - item['notes'] = item.get('notes', '') + '\n' + current_item.notes data.append(item) json_data = {'results': {'slides': data}} if current_item: diff --git a/openlp/plugins/songs/lib/upgrade.py b/openlp/plugins/songs/lib/upgrade.py index 5b7255266..b8945f8a9 100644 --- a/openlp/plugins/songs/lib/upgrade.py +++ b/openlp/plugins/songs/lib/upgrade.py @@ -32,10 +32,11 @@ backend for the Songs plugin """ import logging -from sqlalchemy import Column, ForeignKey, types +from sqlalchemy import Table, Column, ForeignKey, types from sqlalchemy.sql.expression import func, false, null, text from openlp.core.lib.db import get_upgrade_op +from openlp.core.common import trace_error_handler log = logging.getLogger(__name__) __version__ = 4 @@ -57,12 +58,16 @@ def upgrade_1(session, metadata): :param metadata: """ op = get_upgrade_op(session) - op.drop_table('media_files_songs') - op.add_column('media_files', Column('song_id', types.Integer(), server_default=null())) - op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) - if metadata.bind.url.get_dialect().name != 'sqlite': - # SQLite doesn't support ALTER TABLE ADD CONSTRAINT - op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id']) + songs_table = Table('songs', metadata, autoload=True) + if 'media_files_songs' in [t.name for t in metadata.tables.values()]: + op.drop_table('media_files_songs') + op.add_column('media_files', Column('song_id', types.Integer(), server_default=null())) + op.add_column('media_files', Column('weight', types.Integer(), server_default=text('0'))) + if metadata.bind.url.get_dialect().name != 'sqlite': + # SQLite doesn't support ALTER TABLE ADD CONSTRAINT + op.create_foreign_key('fk_media_files_song_id', 'media_files', 'songs', ['song_id', 'id']) + else: + log.warning('Skipping upgrade_1 step of upgrading the song db') def upgrade_2(session, metadata): @@ -72,8 +77,12 @@ def upgrade_2(session, metadata): This upgrade adds a create_date and last_modified date to the songs table """ op = get_upgrade_op(session) - op.add_column('songs', Column('create_date', types.DateTime(), default=func.now())) - op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now())) + songs_table = Table('songs', metadata, autoload=True) + if 'create_date' not in [col.name for col in songs_table.c.values()]: + op.add_column('songs', Column('create_date', types.DateTime(), default=func.now())) + op.add_column('songs', Column('last_modified', types.DateTime(), default=func.now())) + else: + log.warning('Skipping upgrade_2 step of upgrading the song db') def upgrade_3(session, metadata): @@ -83,10 +92,14 @@ def upgrade_3(session, metadata): This upgrade adds a temporary song flag to the songs table """ op = get_upgrade_op(session) - if metadata.bind.url.get_dialect().name == 'sqlite': - op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) + songs_table = Table('songs', metadata, autoload=True) + if 'temporary' not in [col.name for col in songs_table.c.values()]: + if metadata.bind.url.get_dialect().name == 'sqlite': + op.add_column('songs', Column('temporary', types.Boolean(create_constraint=False), server_default=false())) + else: + op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) else: - op.add_column('songs', Column('temporary', types.Boolean(), server_default=false())) + log.warning('Skipping upgrade_3 step of upgrading the song db') def upgrade_4(session, metadata): @@ -98,11 +111,15 @@ def upgrade_4(session, metadata): # Since SQLite doesn't support changing the primary key of a table, we need to recreate the table # and copy the old values op = get_upgrade_op(session) - op.create_table('authors_songs_tmp', - Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True), - Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), - Column('author_type', types.String(), primary_key=True, - nullable=False, server_default=text('""'))) - op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs') - op.drop_table('authors_songs') - op.rename_table('authors_songs_tmp', 'authors_songs') + songs_table = Table('songs', metadata) + if 'author_type' not in [col.name for col in songs_table.c.values()]: + op.create_table('authors_songs_tmp', + Column('author_id', types.Integer(), ForeignKey('authors.id'), primary_key=True), + Column('song_id', types.Integer(), ForeignKey('songs.id'), primary_key=True), + Column('author_type', types.String(), primary_key=True, + nullable=False, server_default=text('""'))) + op.execute('INSERT INTO authors_songs_tmp SELECT author_id, song_id, "" FROM authors_songs') + op.drop_table('authors_songs') + op.rename_table('authors_songs_tmp', 'authors_songs') + else: + log.warning('Skipping upgrade_4 step of upgrading the song db') diff --git a/tests/functional/openlp_core_ui/test_slidecontroller.py b/tests/functional/openlp_core_ui/test_slidecontroller.py index 1d241a317..d8663c20c 100644 --- a/tests/functional/openlp_core_ui/test_slidecontroller.py +++ b/tests/functional/openlp_core_ui/test_slidecontroller.py @@ -225,6 +225,10 @@ class TestSlideController(TestCase): Registry().register('media_controller', mocked_media_controller) slide_controller = SlideController(None) slide_controller.display = mocked_display + play_slides = MagicMock() + play_slides.isChecked.return_value = False + slide_controller.play_slides_loop = play_slides + slide_controller.play_slides_once = play_slides # WHEN: live_escape() is called slide_controller.live_escape() diff --git a/tests/functional/openlp_plugins/songs/test_db.py b/tests/functional/openlp_plugins/songs/test_db.py index e696ea94b..690b89a18 100644 --- a/tests/functional/openlp_plugins/songs/test_db.py +++ b/tests/functional/openlp_plugins/songs/test_db.py @@ -29,9 +29,15 @@ """ This module contains tests for the db submodule of the Songs plugin. """ +import os +import shutil from unittest import TestCase +from tempfile import mkdtemp from openlp.plugins.songs.lib.db import Song, Author, AuthorType +from openlp.plugins.songs.lib import upgrade +from openlp.core.lib.db import upgrade_db +from tests.utils.constants import TEST_RESOURCES_PATH class TestDB(TestCase): @@ -39,6 +45,18 @@ class TestDB(TestCase): Test the functions in the :mod:`db` module. """ + def setUp(self): + """ + Setup for tests + """ + self.tmp_folder = mkdtemp() + + def tearDown(self): + """ + Clean up after tests + """ + shutil.rmtree(self.tmp_folder) + def test_add_author(self): """ Test adding an author to a song @@ -153,3 +171,37 @@ class TestDB(TestCase): # THEN: It should return the name with the type in brackets self.assertEqual("John Doe (Words)", display_name) + + def test_upgrade_old_song_db(self): + """ + Test that we can upgrade an old song db to the current schema + """ + # GIVEN: An old song db + old_db_path = os.path.join(TEST_RESOURCES_PATH, "songs", 'songs-1.9.7.sqlite') + old_db_tmp_path = os.path.join(self.tmp_folder, 'songs-1.9.7.sqlite') + shutil.copyfile(old_db_path, old_db_tmp_path) + db_url = 'sqlite:///' + old_db_tmp_path + + # WHEN: upgrading the db + updated_to_version, latest_version = upgrade_db(db_url, upgrade) + + # Then the song db should have been upgraded to the latest version + self.assertEqual(updated_to_version, latest_version, + 'The song DB should have been upgrade to the latest version') + + def test_upgrade_invalid_song_db(self): + """ + Test that we can upgrade an invalid song db to the current schema + """ + # GIVEN: A song db with invalid version + invalid_db_path = os.path.join(TEST_RESOURCES_PATH, "songs", 'songs-2.2-invalid.sqlite') + invalid_db_tmp_path = os.path.join(self.tmp_folder, 'songs-2.2-invalid.sqlite') + shutil.copyfile(invalid_db_path, invalid_db_tmp_path) + db_url = 'sqlite:///' + invalid_db_tmp_path + + # WHEN: upgrading the db + updated_to_version, latest_version = upgrade_db(db_url, upgrade) + + # Then the song db should have been upgraded to the latest version without errors + self.assertEqual(updated_to_version, latest_version, + 'The song DB should have been upgrade to the latest version') diff --git a/tests/resources/songs/songs-1.9.7.sqlite b/tests/resources/songs/songs-1.9.7.sqlite new file mode 100644 index 000000000..98505464b Binary files /dev/null and b/tests/resources/songs/songs-1.9.7.sqlite differ diff --git a/tests/resources/songs/songs-2.2-invalid.sqlite b/tests/resources/songs/songs-2.2-invalid.sqlite new file mode 100644 index 000000000..0c991d5a3 Binary files /dev/null and b/tests/resources/songs/songs-2.2-invalid.sqlite differ