diff --git a/openlp/core/__init__.py b/openlp/core/__init__.py index 5e05b365c..6cce20e9d 100644 --- a/openlp/core/__init__.py +++ b/openlp/core/__init__.py @@ -39,11 +39,12 @@ import sys import logging from optparse import OptionParser from traceback import format_exception - +import shutil +import time from PyQt4 import QtCore, QtGui from openlp.core.common import Registry, OpenLPMixin, AppLocation, Settings, UiStrings, check_directory_exists, \ - is_macosx, is_win + is_macosx, is_win, translate from openlp.core.lib import ScreenList from openlp.core.resources import qInitResources from openlp.core.ui.mainwindow import MainWindow @@ -136,6 +137,8 @@ class OpenLP(OpenLPMixin, QtGui.QApplication): self.splash.show() # make sure Qt really display the splash screen self.processEvents() + # Check if OpenLP has been upgrade and if a backup of data should be created + self.backup_on_upgrade(has_run_wizard) # start the main app window self.main_window = MainWindow() Registry().execute('bootstrap_initialise') @@ -193,6 +196,40 @@ class OpenLP(OpenLPMixin, QtGui.QApplication): self.set_normal_cursor() self.exception_form.exec_() + def backup_on_upgrade(self, has_run_wizard): + """ + Check if OpenLP has been upgraded, and ask if a backup of data should be made + + :param has_run_wizard: OpenLP has been run before + """ + data_version = Settings().value('core/application version') + openlp_version = get_application_version()['version'] + # New installation, no need to create backup + if not has_run_wizard: + Settings().setValue('core/application version', openlp_version) + # If data_version is different from the current version ask if we should backup the data folder + elif data_version != openlp_version: + if QtGui.QMessageBox.question(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'OpenLP has been upgraded, ' + 'do you want to create a backup of OpenLPs data folder?'), + QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, + QtGui.QMessageBox.Yes) == QtGui.QMessageBox.Yes: + # Create copy of data folder + data_folder_path = AppLocation.get_data_path() + timestamp = time.strftime("%Y%m%d-%H%M%S") + data_folder_backup_path = data_folder_path + '-' + timestamp + try: + shutil.copytree(data_folder_path, data_folder_backup_path) + except OSError: + QtGui.QMessageBox.warning(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'Backup of the data folder failed!')) + return + QtGui.QMessageBox.information(None, translate('OpenLP', 'Backup'), + translate('OpenLP', 'A backup of the data folder has been created at %s') + % data_folder_backup_path) + # Update the version in the settings + Settings().setValue('core/application version', openlp_version) + def process_events(self): """ Wrapper to make ProcessEvents visible and named correctly diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py index f7202b590..dc61b10fd 100644 --- a/openlp/core/common/settings.py +++ b/openlp/core/common/settings.py @@ -137,6 +137,7 @@ class Settings(QtCore.QSettings): # circular dependency. 'core/display on monitor': True, 'core/override position': False, + 'core/application version': '0.0', 'images/background color': '#000000', 'media/players': 'webkit', 'media/override player': QtCore.Qt.Unchecked, diff --git a/tests/functional/test_init.py b/tests/functional/test_init.py index 6fc7d7f70..7897895c7 100644 --- a/tests/functional/test_init.py +++ b/tests/functional/test_init.py @@ -33,9 +33,10 @@ import os from unittest import TestCase from unittest.mock import MagicMock, patch -from PyQt4 import QtCore +from PyQt4 import QtCore, QtGui from openlp.core import OpenLP +from openlp.core.common import Settings from tests.helpers.testmixin import TestMixin @@ -44,11 +45,13 @@ TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', 'resou class TestInit(TestCase, TestMixin): def setUp(self): + self.build_settings() with patch('openlp.core.common.OpenLPMixin.__init__') as constructor: constructor.return_value = None self.openlp = OpenLP(list()) def tearDown(self): + self.destroy_settings() del self.openlp def event_test(self): @@ -68,3 +71,51 @@ class TestInit(TestCase, TestMixin): self.assertTrue(result, "The method should have returned True.") mocked_file_method.assert_called_once_with() self.assertEqual(self.openlp.args[0], file_path, "The path should be in args.") + + def backup_on_upgrade_first_install_test(self): + """ + Test that we don't try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are the same + old_install = False + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.2.0') + with patch('openlp.core.get_application_version') as mocked_get_application_version,\ + patch('openlp.core.QtGui.QMessageBox.question') as mocked_question: + mocked_get_application_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtGui.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install) + + # THEN: It should not ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be the same!') + self.assertEqual(mocked_question.call_count, 0, 'No question should have been asked!') + + def backup_on_upgrade_test(self): + """ + Test that we try to backup on a new install + """ + # GIVEN: Mocked data version and OpenLP version which are different + old_install = True + MOCKED_VERSION = { + 'full': '2.2.0-bzr000', + 'version': '2.2.0', + 'build': 'bzr000' + } + Settings().setValue('core/application version', '2.0.5') + with patch('openlp.core.get_application_version') as mocked_get_application_version,\ + patch('openlp.core.QtGui.QMessageBox.question') as mocked_question: + mocked_get_application_version.return_value = MOCKED_VERSION + mocked_question.return_value = QtGui.QMessageBox.No + + # WHEN: We check if a backup should be created + self.openlp.backup_on_upgrade(old_install) + + # THEN: It should ask if we want to create a backup + self.assertEqual(Settings().value('core/application version'), '2.2.0', 'Version should be upgraded!') + self.assertEqual(mocked_question.call_count, 1, 'A question should have been asked!')