diff --git a/gramps/cli/grampscli.py b/gramps/cli/grampscli.py
index 562f031d3..0d7c858c5 100644
--- a/gramps/cli/grampscli.py
+++ b/gramps/cli/grampscli.py
@@ -163,6 +163,9 @@ class CLIDbLoader(object):
self._errordialog( _("Cannot open database"), str(msg))
except gen.db.exceptions.BsddbUpgradeRequiredError as msg:
self.dbstate.no_database()
+ except gen.db.exceptions.BsddbDowngradeRequiredError, msg:
+ self.dbstate.no_database()
+ self._errordialog( _("Cannot open database"), str(msg))
self._errordialog( _("Cannot open database"), str(msg))
except gen.db.exceptions.BsddbDowngradeError as msg:
self.dbstate.no_database()
diff --git a/gramps/gen/db/exceptions.py b/gramps/gen/db/exceptions.py
index e66091d25..cdbc96d01 100644
--- a/gramps/gen/db/exceptions.py
+++ b/gramps/gen/db/exceptions.py
@@ -79,13 +79,13 @@ class DbVersionError(Exception):
self.max_vers = max_vers
def __str__(self):
- return _("The schema version is not supported by this version of "
- "Gramps.\n\n"
- "This Family Tree is schema version %(tree_vers)s, and this "
- "version of Gramps supports versions %(min_vers)s to "
- "%(max_vers)s\n\n"
- "Please upgrade to the corresponding version or use "
- "XML for porting data between different database versions.") %\
+ return _('The schema version is not supported by this version of '
+ 'Gramps.\n\n'
+ 'This Family Tree is schema version %(tree_vers)s, and this '
+ 'version of Gramps supports versions %(min_vers)s to '
+ '%(max_vers)s\n\n'
+ 'Please upgrade to the corresponding version or use '
+ 'XML for porting data between different schema versions.') %\
{'tree_vers': self.tree_vers,
'min_vers': self.min_vers,
'max_vers': self.max_vers}
@@ -101,17 +101,42 @@ class BsddbDowngradeError(Exception):
self.bdb_version = str(bdb_version)
def __str__(self):
- return _('Gramps stores its data in a Berkeley Database. '
- 'The Family Tree you try to load was created with version '
- '%(env_version)s of the Berkeley DB. However, the Gramps '
- 'version in use right now employs version %(bdb_version)s '
- 'of the Berkeley DB. So you are trying to load data created '
- 'in a newer format into an older program; this is bound to '
- 'fail. The right approach in this case is to use XML export '
- 'and import. So try to open the Family Tree on that computer '
- 'with that software that created the Family Tree, export it '
- 'to XML and load that XML into the version of Gramps you '
- 'intend to use.') % {'env_version': self.env_version,
+ return _('The Family Tree you are trying to load is in the Bsddb '
+ 'version %(env_version)s format. This version of Gramps uses '
+ 'Bsddb version %(bdb_version)s. So you are trying to load '
+ 'data created in a newer format into an older program, and '
+ 'this is bound to fail.\n\n'
+ 'You should start your newer version of Gramps and '
+ ''
+ 'make a backup of your Family Tree. You can then import '
+ 'this backup into this version of Gramps.') % \
+ {'env_version': self.env_version,
+ 'bdb_version': self.bdb_version}
+
+class BsddbDowngradeRequiredError(Exception):
+ """
+ Error used to report that the Berkeley database used to create the family
+ tree is of a version that is newer than the current version, but it may be
+ possible to open the tree, because the difference is only a point upgrade
+ (i.e. a difference in the last digit of the version tuple).
+ """
+ def __init__(self, env_version, bdb_version):
+ Exception.__init__(self)
+ self.env_version = str(env_version)
+ self.bdb_version = str(bdb_version)
+
+ def __str__(self):
+ return _('The Family Tree you are trying to load is in the Bsddb '
+ 'version %(env_version)s format. This version of Gramps uses '
+ 'Bsddb version %(bdb_version)s. So you are trying to load '
+ 'data created in a newer format into an older program. In '
+ 'this particular case, the difference is very small, so it '
+ 'may work.\n\n'
+ 'If you have not already made a backup of your Family Tree, '
+ 'then you should start your newer version of Gramps and '
+ ''
+ 'make a backup of your Family Tree.') % \
+ {'env_version': self.env_version,
'bdb_version': self.bdb_version}
class BsddbUpgradeRequiredError(Exception):
@@ -125,18 +150,20 @@ class BsddbUpgradeRequiredError(Exception):
self.bsddb_version = str(bsddb_version)
def __str__(self):
- return _('The Bsddb version of the Family Tree you are trying to open '
- 'needs to be upgraded from %(env_version)s to %(bdb_version)s.\n\n'
- 'This probably means that the Family Tree was created with '
- 'an old version of Gramps. Opening the tree with this version '
- 'of Gramps may irretrievably corrupt your tree. You are '
- 'strongly advised to backup your tree before proceeding, '
- 'see: \n'
- 'http://www.gramps-project.org/wiki/index.php?title=How_to_make_a_backup\n\n'
- 'If you have made a backup, then you can get Gramps to try '
- 'to open the tree and upgrade it') % \
- {'env_version': self.env_version,
- 'bdb_version': self.bsddb_version}
+ return _('The Family Tree you are trying to load is in the Bsddb '
+ 'version %(env_version)s format. This version of Gramps uses '
+ 'Bsddb version %(bdb_version)s. Therefore you cannot load '
+ 'this Family Tree without upgrading the Bsddb version of the '
+ 'Family Tree.\n\n'
+ 'Opening the Family Tree with this version of Gramps might '
+ 'irretrievably corrupt your Family Tree. You are strongly '
+ 'advised to backup your Family Tree.\n\n'
+ 'If you have not already made a backup of your Family Tree, '
+ 'then you should start your old version of Gramps and '
+ ''
+ 'make a backup of your Family Tree.') % \
+ {'env_version': self.env_version,
+ 'bdb_version': self.bsddb_version}
class DbEnvironmentError(Exception):
"""
@@ -167,17 +194,27 @@ class DbUpgradeRequiredError(Exception):
Error used to report that a database needs to be upgraded before it can be
used.
"""
- def __init__(self):
+ def __init__(self, oldschema, newschema):
Exception.__init__(self)
+ self.oldschema = oldschema
+ self.newschema = newschema
def __str__(self):
- return _("The Family Tree structure has changed since the version of"
- " Gramps you used to create this tree.\n\n"
- "Therefore, you cannot open this Family Tree without upgrading the"
- " definition of the structure.\n"
- "If you upgrade then you won't be able to use previous "
- "versions of Gramps, also not with the .gramps xml export!\n\n"
- "Upgrading is a difficult task that may not be interrupted, or "
- "Gramps will irretrievably corrupt your tree. Therefore, create "
- "a backup copy first. See: \n"
- "http://www.gramps-project.org/wiki/index.php?title=How_to_make_a_backup")
+ return _('The Family Tree you are trying to load is in the schema '
+ 'version %(oldschema)s format. This version of Gramps uses '
+ 'schema version %(newschema)s. Therefore you cannot load this '
+ 'Family Tree without upgrading the schema version of the '
+ 'Family Tree.\n\n'
+ 'If you upgrade then you won\'t be able to use the previous '
+ 'version of Gramps, even if you subsequently '
+ 'backup '
+ 'or export '
+ 'your upgraded Family Tree.\n\n'
+ 'Upgrading is a difficult task which could irretrievably '
+ 'corrupt your Family Tree if it is interrupted or fails.\n\n'
+ 'If you have not already made a backup of your Family Tree, '
+ 'then you should start your old version of Gramps and '
+ 'make a backup '
+ 'of your Family Tree.') % \
+ {'oldschema': self.oldschema,
+ 'newschema': self.newschema}
diff --git a/gramps/gen/db/write.py b/gramps/gen/db/write.py
index 217677d19..7eb43e9d8 100644
--- a/gramps/gen/db/write.py
+++ b/gramps/gen/db/write.py
@@ -407,7 +407,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
return
(grampsdb_path, db_code) = os.path.split(dirname)
dotgramps_path = os.path.dirname(grampsdb_path)
- zipname = title + time.strftime("%Y-%m-%d %H-%M-%S") + ".zip"
+ zipname = title + time.strftime("_%Y-%m-%d_%H-%M-%S") + ".zip"
if sys.version_info[0] < 3:
zipname = zipname.encode(glocale.getfilesystemencoding())
zippath = os.path.join(dotgramps_path, zipname)
@@ -420,7 +420,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
"delete the zip file at %s" %
zippath)
- def __check_bdb_version(self, name, force_bsddb_upgrade=False):
+ def __check_bdb_version(self, name, force_bsddb_upgrade=False,
+ force_bsddb_downgrade=False):
"""Older version of Berkeley DB can't read data created by a newer
version."""
bdb_version = db.version()
@@ -434,31 +435,58 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
with open(versionpath, "r") as version_file:
bsddb_version = version_file.read().strip()
env_version = tuple(map(int, bsddb_version[1:-1].split(', ')))
- if (env_version[0] > bdb_version[0]) or \
- (env_version[0] == bdb_version[0] and
- env_version[1] > bdb_version[1]):
- clear_lock_file(name)
- raise BsddbDowngradeError(env_version, bdb_version)
- elif env_version == bdb_version:
- return
else:
# bsddb version is unknown
- bsddb_version = "Unknown"
+ env_version = "Unknown"
- # An upgrade is needed, raise an exception unless the user has allowed
- # an upgrade
- if not force_bsddb_upgrade:
- _LOG.debug("Bsddb upgrade required from %s to %s" %
- (bsddb_version, str(bdb_version)))
- raise exceptions.BsddbUpgradeRequiredError(bsddb_version,
- str(bdb_version))
-
- if not self.readonly:
- _LOG.warning("Bsddb upgrade requested from %s to %s" %
- (bsddb_version, str(bdb_version)))
- self.update_env_version = True
- # Make a backup of the database files anyway
- self.__make_zip_backup(name)
+ if env_version == "Unknown" or \
+ (env_version[0] < bdb_version[0]) or \
+ (env_version[0] == bdb_version[0] and
+ env_version[1] < bdb_version[1]) or \
+ (env_version[0] == bdb_version[0] and
+ env_version[1] == bdb_version[1] and
+ env_version[2] < bdb_version[2]):
+ # an upgrade is needed
+ if not force_bsddb_upgrade:
+ _LOG.debug("Bsddb upgrade required from %s to %s" %
+ (bsddb_version, str(bdb_version)))
+ clear_lock_file(name)
+ raise exceptions.BsddbUpgradeRequiredError(bsddb_version,
+ str(bdb_version))
+ if not self.readonly:
+ _LOG.warning("Bsddb upgrade requested from %s to %s" %
+ (bsddb_version, str(bdb_version)))
+ self.update_env_version = True
+ # Make a backup of the database files anyway
+ self.__make_zip_backup(name)
+ elif (env_version[0] > bdb_version[0]) or \
+ (env_version[0] == bdb_version[0] and
+ env_version[1] > bdb_version[1]):
+ clear_lock_file(name)
+ raise BsddbDowngradeError(env_version, bdb_version)
+ elif (env_version[0] == bdb_version[0] and
+ env_version[1] == bdb_version[1] and
+ env_version[2] > bdb_version[2]):
+ # A down-grade may be possible
+ if not force_bsddb_downgrade:
+ _LOG.debug("Bsddb downgrade required from %s to %s" %
+ (bsddb_version, str(bdb_version)))
+ clear_lock_file(name)
+ raise exceptions.BsddbDowngradeRequiredError(bsddb_version,
+ str(bdb_version))
+ # Try to do a down-grade
+ if not self.readonly:
+ _LOG.warning("Bsddb downgrade requested from %s to %s" %
+ (bsddb_version, str(bdb_version)))
+ self.update_env_version = True
+ # Make a backup of the database files anyway
+ self.__make_zip_backup(name)
+ elif env_version == bdb_version:
+ # Bsddb version is OK
+ pass
+ else:
+ # This can't happen
+ raise "Comparison between Bsddb version failed"
@catch_db_error
def version_supported(self):
@@ -493,7 +521,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
@catch_db_error
def load(self, name, callback, mode=DBMODE_W, force_schema_upgrade=False,
- force_bsddb_upgrade=False):
+ force_bsddb_upgrade=False, force_bsddb_downgrade=False):
if self.__check_readonly(name):
mode = DBMODE_R
@@ -513,7 +541,8 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
self.path = self.full_name
self.brief_name = os.path.basename(name)
- self.__check_bdb_version(name, force_bsddb_upgrade)
+ self.__check_bdb_version(name, force_bsddb_upgrade,
+ force_bsddb_downgrade)
# Set up database environment
self.env = db.DBEnv()
@@ -632,14 +661,16 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
# or rebuilt by upgrade as well. In any case, the
# self.secondary_connected flag should be set accordingly.
if self.need_schema_upgrade():
+ oldschema = self.metadata.get(b'version', default=0)
+ newschema = _DBVERSION
_LOG.debug("Schema upgrade required from %s to %s" %
- (self.metadata.get(b'version', default=0), _DBVERSION))
+ (oldschema, newschema))
if force_schema_upgrade == True:
self.gramps_upgrade(callback)
else:
self.__close_early()
clear_lock_file(name)
- raise DbUpgradeRequiredError()
+ raise DbUpgradeRequiredError(oldschema, newschema)
if callback:
callback(50)
diff --git a/gramps/gui/dbloader.py b/gramps/gui/dbloader.py
index 92d27b9f0..ce596820a 100644
--- a/gramps/gui/dbloader.py
+++ b/gramps/gui/dbloader.py
@@ -62,7 +62,8 @@ from gramps.gen.db.exceptions import (DbUpgradeRequiredError,
BsddbDowngradeError,
DbVersionError,
DbEnvironmentError,
- BsddbUpgradeRequiredError)
+ BsddbUpgradeRequiredError,
+ BsddbDowngradeRequiredError)
from gramps.gen.constfunc import STRTYPE
from gramps.gen.utils.file import get_unicode_path_from_file_chooser
from .pluginmanager import GuiPluginManager
@@ -307,40 +308,60 @@ class DbLoader(CLIDbLoader):
force_schema_upgrade = False
force_bsddb_upgrade = False
+ force_bsddb_downgrade = False
try:
while True:
try:
db.load(filename, self._pulse_progress,
mode, force_schema_upgrade,
- force_bsddb_upgrade)
+ force_bsddb_upgrade,
+ force_bsddb_downgrade)
db.set_save_path(filename)
self.dbstate.change_database(db)
break
except DbUpgradeRequiredError as msg:
- if QuestionDialog2(_("Need to upgrade database!"),
+ if QuestionDialog2(_("Are you sure you want to upgrade "
+ "this Family Tree?"),
str(msg),
- _("Upgrade now"),
- _("Cancel")).run():
+ _("I have made a backup,\n"
+ "please upgrade my Family Tree"),
+ _("Cancel"), self.uistate.window).run():
force_schema_upgrade = True
force_bsddb_upgrade = False
+ force_bsddb_downgrade = False
else:
self.dbstate.no_database()
break
except BsddbUpgradeRequiredError as msg:
- if QuestionDialog2(_("Need to upgrade Bsddb database!"),
+ if QuestionDialog2(_("Are you sure you want to upgrade "
+ "this Family Tree?"),
str(msg),
- _("I have made a backup, "
+ _("I have made a backup,\n"
"please upgrade my tree"),
- _("Cancel")).run():
+ _("Cancel"), self.uistate.window).run():
force_schema_upgrade = False
force_bsddb_upgrade = True
+ force_bsddb_downgrade = False
+ else:
+ self.dbstate.no_database()
+ break
+ except BsddbDowngradeRequiredError as msg:
+ if QuestionDialog2(_("Are you sure you want to downgrade "
+ "this Family Tree?"),
+ str(msg),
+ _("I have made a backup,\n"
+ "please downgrade my Family Tree"),
+ _("Cancel"), self.uistate.window).run():
+ force_schema_upgrade = False
+ force_bsddb_upgrade = False
+ force_bsddb_downgrade = True
else:
self.dbstate.no_database()
break
# Get here is there is an exception the while loop does not handle
except BsddbDowngradeError as msg:
self.dbstate.no_database()
- self._errordialog( _("Cannot open database"), str(msg))
+ self._warn( _("Cannot open database"), str(msg))
except DbVersionError as msg:
self.dbstate.no_database()
self._errordialog( _("Cannot open database"), str(msg))
diff --git a/gramps/gui/dialog.py b/gramps/gui/dialog.py
index f3555a572..11dd6aa31 100644
--- a/gramps/gui/dialog.py
+++ b/gramps/gui/dialog.py
@@ -112,6 +112,12 @@ class QuestionDialog(object):
if response == Gtk.ResponseType.ACCEPT:
task()
+from display import display_url
+def on_activate_link(label, uri):
+ # see aboutdialog.py _show_url()
+ display_url(uri)
+ return True
+
class QuestionDialog2(object):
def __init__(self, msg1, msg2, label_msg1, label_msg2, parent=None):
self.xml = Glade(toplevel='questiondialog')
@@ -125,6 +131,8 @@ class QuestionDialog2(object):
label1.set_use_markup(True)
label2 = self.xml.get_object('qd_label2')
+ # see https://github.com/emesene/emesene/issues/723
+ label2.connect('activate-link', on_activate_link)
label2.set_text(msg2)
label2.set_use_markup(True)
@@ -228,6 +236,11 @@ class WarningDialog(Gtk.MessageDialog):
buttons=Gtk.ButtonsType.CLOSE)
self.set_markup('%s' % msg1)
self.format_secondary_markup(msg2)
+ # FIXME: Hyper-links in the secondary text display as underlined text,
+ # but clicking on the link fails with
+ # GtkWarning: Unable to show 'http://www.gramps-project.org/wiki/index.php?title=How_to_make_a_backup': Operation not supported
+ # self.connect('activate-link'... fails with
+ # : unknown signal name: activate-link
self.set_icon(ICON)
self.set_title("%s - Gramps" % msg1)
self.show()