diff --git a/gramps/cli/arghandler.py b/gramps/cli/arghandler.py
index 0db169d30..47e9b8da6 100644
--- a/gramps/cli/arghandler.py
+++ b/gramps/cli/arghandler.py
@@ -44,7 +44,6 @@ import sys
 #-------------------------------------------------------------------------
 from gramps.gen.recentfiles import recent_files
 from gramps.gen.utils.file import rm_tempdir, get_empty_tempdir
-from gramps.gen.db import DbBsddb
 from .clidbman import CLIDbManager, NAME_FILE, find_locker_name
 
 from gramps.gen.plug import BasePluginManager
@@ -491,7 +490,8 @@ class ArgHandler(object):
                     self.imp_db_path, title = self.dbman.create_new_db_cli()
                 else:
                     self.imp_db_path = get_empty_tempdir("import_dbdir")
-                    newdb = DbBsddb()
+
+                    newdb = self.dbstate.make_database("bsddb")
                     newdb.write_version(self.imp_db_path)
                 
                 try:
diff --git a/gramps/cli/clidbman.py b/gramps/cli/clidbman.py
index 404104afe..5bb87e632 100644
--- a/gramps/cli/clidbman.py
+++ b/gramps/cli/clidbman.py
@@ -54,7 +54,6 @@ _LOG = logging.getLogger(DBLOGNAME)
 #-------------------------------------------------------------------------
 from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
-from gramps.gen.db import DbBsddb
 from gramps.gen.plug import BasePluginManager
 from gramps.gen.config import config
 from gramps.gen.constfunc import win, conv_to_unicode
@@ -134,61 +133,42 @@ class CLIDbManager(object):
 
     def get_dbdir_summary(self, dirpath, name):
         """
-        Returns (people_count, bsddb_version, schema_version) of
-        current DB.
-        Returns ("Unknown", "Unknown", "Unknown") if invalid DB or other error.
+        dirpath: full path to database
+        name: proper name of family tree
+
+        Returns dictionary of summary item.
+        Should include at least, if possible:
+
+        _("Path")
+        _("Family Tree")
+        _("Last accessed")
+        _("Database backend")
+        _("Locked?")
+
+        and these details:
+
+        _("Number of people")
+        _("Version")
+        _("Schema version")
         """
-        from bsddb3 import dbshelve, db
-
-        from gramps.gen.db import META, PERSON_TBL
-        from  gramps.gen.db.dbconst import BDBVERSFN
-
-        bdbversion_file = os.path.join(dirpath, BDBVERSFN)
-        if os.path.isfile(bdbversion_file):
-            vers_file = open(bdbversion_file)
-            bsddb_version = vers_file.readline().strip()
-        else:
-            return "Unknown", "Unknown", "Unknown"
-        
-        current_bsddb_version = str(db.version())
-        if bsddb_version != current_bsddb_version:
-            return "Unknown", bsddb_version, "Unknown"
-        
-        env = db.DBEnv()
-        flags = db.DB_CREATE | db.DB_PRIVATE |\
-            db.DB_INIT_MPOOL |\
-            db.DB_INIT_LOG | db.DB_INIT_TXN
+        dbid = "bsddb"
+        dbid_path = os.path.join(dirpath, "database.txt")
+        if os.path.isfile(dbid_path):
+            dbid = open(dbid_path).read().strip()
         try:
-            env.open(dirpath, flags)
+            database = self.dbstate.make_database(dbid)
+            database.load(dirpath, None)
+            retval = database.get_summary()
         except Exception as msg:
-            LOG.warning("Error opening db environment for '%s': %s" %
-                        (name, str(msg)))
-            try:
-                env.close()
-            except Exception as msg:
-                LOG.warning("Error closing db environment for '%s': %s" %
-                        (name, str(msg)))
-            return "Unknown", bsddb_version, "Unknown"
-        dbmap1 = dbshelve.DBShelf(env)
-        fname = os.path.join(dirpath, META + ".db")
-        try:
-            dbmap1.open(fname, META, db.DB_HASH, db.DB_RDONLY)
-        except:
-            env.close()
-            return "Unknown", bsddb_version, "Unknown"
-        schema_version = dbmap1.get(b'version', default=None)
-        dbmap1.close()
-        dbmap2 = dbshelve.DBShelf(env)
-        fname = os.path.join(dirpath, PERSON_TBL + ".db")
-        try:
-            dbmap2.open(fname, PERSON_TBL, db.DB_HASH, db.DB_RDONLY)
-        except:
-            env.close()
-            return "Unknown", bsddb_version, schema_version
-        count = len(dbmap2)
-        dbmap2.close()
-        env.close()
-        return (count, bsddb_version, schema_version)
+            retval = {"Unavailable": str(msg)[:74] + "..."}
+        retval.update({
+            _("Family Tree"): name,
+            _("Path"): dirpath,
+            _("Database backend"): dbid,
+            _("Last accessed"): time_val(dirpath)[1],
+            _("Locked?"): self.is_locked(dirpath),
+        })
+        return retval
 
     def family_tree_summary(self):
         """
@@ -199,19 +179,7 @@ class CLIDbManager(object):
         for item in self.current_names:
             (name, dirpath, path_name, last, 
              tval, enable, stock_id) = item
-            count, bsddb_version, schema_version = self.get_dbdir_summary(dirpath, name)
-            retval = {}
-            retval[_("Number of people")] = count
-            if enable:
-                retval[_("Locked?")] = _("yes")
-            else:
-                retval[_("Locked?")] = _("no")
-            retval[_("Bsddb version")] = bsddb_version
-            retval[_("Schema version")] = schema_version
-            retval[_("Family Tree")] = name
-            retval[_("Path")] = dirpath
-            retval[_("Last accessed")] = time.strftime('%x %X', 
-                                                    time.localtime(tval))
+            retval = self.get_dbdir_summary(dirpath, name)
             summary_list.append( retval )
         return summary_list
 
@@ -275,7 +243,7 @@ class CLIDbManager(object):
         """
         print(_('Import finished...'))
 
-    def create_new_db_cli(self, title=None, create_db=True):
+    def create_new_db_cli(self, title=None, create_db=True, dbid=None):
         """
         Create a new database.
         """
@@ -294,7 +262,9 @@ class CLIDbManager(object):
 
         if create_db:
             # write the version number into metadata
-            newdb = DbBsddb()
+            if dbid is None:
+                dbid = "bsddb"
+            newdb = self.dbstate.make_database(dbid)
             newdb.write_version(new_path)
 
         (tval, last) = time_val(new_path)
@@ -303,11 +273,11 @@ class CLIDbManager(object):
                                    last, tval, False, ""))
         return new_path, title
 
-    def _create_new_db(self, title=None):
+    def _create_new_db(self, title=None, dbid=None):
         """
         Create a new database, do extra stuff needed
         """
-        return self.create_new_db_cli(title)
+        return self.create_new_db_cli(title, dbid=dbid)
 
     def import_new_db(self, filename, user):
         """
@@ -360,8 +330,8 @@ class CLIDbManager(object):
     
                 # Create a new database
                 self.__start_cursor(_("Importing data..."))
-                dbclass = DbBsddb
-                dbase = dbclass()
+
+                dbase = self.dbstate.make_database("bsddb")
                 dbase.load(new_path, user.callback)
     
                 import_function = plugin.get_import_function()
diff --git a/gramps/cli/grampscli.py b/gramps/cli/grampscli.py
index a45564a19..d7b9afea6 100644
--- a/gramps/cli/grampscli.py
+++ b/gramps/cli/grampscli.py
@@ -47,9 +47,9 @@ LOG = logging.getLogger(".grampscli")
 from gramps.gen.display.name import displayer as name_displayer
 from gramps.gen.config import config
 from gramps.gen.const import PLUGINS_DIR, USER_PLUGINS
+from gramps.gen.db.dbconst import DBBACKEND
 from gramps.gen.errors import DbError
 from gramps.gen.dbstate import DbState
-from gramps.gen.db import DbBsddb
 from gramps.gen.db.exceptions import (DbUpgradeRequiredError, 
                                       BsddbDowngradeError, 
                                       DbVersionError, 
@@ -152,9 +152,16 @@ class CLIDbLoader(object):
         else:
             mode = 'w'
 
-        dbclass = DbBsddb
+        dbid_path = os.path.join(filename, DBBACKEND)
+        if os.path.isfile(dbid_path):
+            with open(dbid_path) as fp:
+                dbid = fp.read().strip()
+        else:
+            dbid = "bsddb"
+
+        db = self.dbstate.make_database(dbid)
         
-        self.dbstate.change_database(dbclass())
+        self.dbstate.change_database(db)
         self.dbstate.db.disable_signals()
 
         self._begin_progress()
diff --git a/gramps/gen/db/__init__.py b/gramps/gen/db/__init__.py
index 5b79ce334..d9fcf1ad1 100644
--- a/gramps/gen/db/__init__.py
+++ b/gramps/gen/db/__init__.py
@@ -86,11 +86,27 @@ More details can be found in the manual's
 
 from .base import *
 from .dbconst import *
-from .cursor import *
-from .read import *
-from .bsddbtxn import *
 from .txn import *
-from .undoredo import *
 from .exceptions import *
-from .write import *
-from .backup import backup, restore
+from .undoredo import *
+
+def find_surname_name(key, data):
+    """
+    Creating a surname from raw name, to use for sort and index
+    returns a byte string
+    """
+    return __index_surname(data[5])
+
+def __index_surname(surn_list):
+    """
+    All non pa/matronymic surnames are used in indexing.
+    pa/matronymic not as they change for every generation!
+    returns a byte string
+    """
+    from gramps.gen.lib import NameOriginType
+    if surn_list:
+        surn = " ".join([x[0] for x in surn_list if not (x[3][0] in [
+                    NameOriginType.PATRONYMIC, NameOriginType.MATRONYMIC]) ])
+    else:
+        surn = ""
+    return surn
diff --git a/gramps/gen/db/backup.py b/gramps/gen/db/backup.py
deleted file mode 100644
index 9329caaff..000000000
--- a/gramps/gen/db/backup.py
+++ /dev/null
@@ -1,213 +0,0 @@
-#
-# Gramps - a GTK+/GNOME based genealogy program
-#
-# Copyright (C) 2007  Donald N. Allingham
-# Copyright (C) 2011       Tim G L Lyons
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-# gen/db/backup.py
-
-"""
-Description
-===========
-
-This module Provides backup and restore functions for a database. The
-backup function saves the data into backup files, while the restore
-function loads the data back into a database.
-
-You should only restore the data into an empty database.
-
-Implementation
-==============
-
-Not all of the database tables need to be backed up, since many are
-automatically generated from the others. The tables that are backed up
-are the primary tables and the metadata table.
-
-The database consists of a table of "pickled" tuples. Each of the
-primary tables is "walked", and the pickled tuple is extracted, and
-written to the backup file.
-
-Restoring the data is just as simple. The backup file is parsed an
-entry at a time, and inserted into the associated database table. The
-derived tables are built automatically as the items are entered into
-db.
-"""
-
-#-------------------------------------------------------------------------
-#
-# load standard python libraries
-#
-#-------------------------------------------------------------------------
-import os
-import pickle
-
-#------------------------------------------------------------------------
-#
-# Gramps libs
-#
-#------------------------------------------------------------------------
-from .exceptions import DbException
-from .write import FAMILY_TBL, PLACES_TBL, SOURCES_TBL, MEDIA_TBL, \
-    EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, TAG_TBL, META, CITATIONS_TBL
-
-#------------------------------------------------------------------------
-#
-# Set up logging
-#
-#------------------------------------------------------------------------
-import logging
-LOG = logging.getLogger(".Backup")
-
-def backup(database):
-    """
-    Exports the database to a set of backup files. These files consist
-    of the pickled database tables, one file for each table.
-
-    The heavy lifting is done by the private :py:func:`__do__export` function.
-    The purpose of this function is to catch any exceptions that occur.
-
-    :param database: database instance to backup
-    :type database: DbDir
-    """
-    try:
-        __do_export(database)
-    except (OSError, IOError) as msg:
-        raise DbException(str(msg))
-
-def __mk_backup_name(database, base):
-    """
-    Return the backup name of the database table
-
-    :param database: database instance 
-    :type database: DbDir
-    :param base: base name of the table
-    :type base: str
-    """
-    return os.path.join(database.get_save_path(), base + ".gbkp")
-
-def __mk_tmp_name(database, base):
-    """
-    Return the temporary backup name of the database table
-
-    :param database: database instance 
-    :type database: DbDir
-    :param base: base name of the table
-    :type base: str
-    """
-    return os.path.join(database.get_save_path(), base + ".gbkp.new")
-
-def __do_export(database):
-    """
-    Loop through each table of the database, saving the pickled data
-    a file.
-
-    :param database: database instance to backup
-    :type database: DbDir
-    """
-    try:
-        for (base, tbl) in __build_tbl_map(database):
-            backup_name = __mk_tmp_name(database, base)
-            backup_table = open(backup_name, 'wb')
-    
-            cursor = tbl.cursor()
-            data = cursor.first()
-            while data:
-                pickle.dump(data, backup_table, 2)
-                data = cursor.next()
-            cursor.close()
-            backup_table.close()
-    except (IOError,OSError):
-        return
-
-    for (base, tbl) in __build_tbl_map(database):
-        new_name = __mk_backup_name(database, base)
-        old_name = __mk_tmp_name(database, base)
-        if os.path.isfile(new_name):
-            os.unlink(new_name)
-        os.rename(old_name, new_name)
-
-def restore(database):
-    """
-    Restores the database to a set of backup files. These files consist
-    of the pickled database tables, one file for each table.
-
-    The heavy lifting is done by the private :py:func:`__do__restore` function.
-    The purpose of this function is to catch any exceptions that occur.
-
-    :param database: database instance to restore
-    :type database: DbDir
-    """
-    try:
-        __do_restore(database)
-    except (OSError, IOError) as msg:
-        raise DbException(str(msg))
-
-def __do_restore(database):
-    """
-    Loop through each table of the database, restoring the pickled data
-    to the appropriate database file.
-
-    :param database: database instance to backup
-    :type database: DbDir
-    """
-    for (base, tbl) in __build_tbl_map(database):
-        backup_name = __mk_backup_name(database, base)
-        backup_table = open(backup_name, 'rb')
-        __load_tbl_txn(database, backup_table, tbl)
-
-    database.rebuild_secondary()
-
-def __load_tbl_txn(database, backup_table, tbl):
-    """
-    Return the temporary backup name of the database table
-
-    :param database: database instance 
-    :type database: DbDir
-    :param backup_table: file containing the backup data
-    :type backup_table: file
-    :param tbl: Berkeley db database table
-    :type tbl: Berkeley db database table
-    """
-    try:
-        while True:
-            data = pickle.load(backup_table)
-            txn = database.env.txn_begin()
-            tbl.put(data[0], data[1], txn=txn)
-            txn.commit()
-    except EOFError:
-        backup_table.close()
-
-def __build_tbl_map(database):
-    """
-    Builds a table map of names to database tables.
-
-    :param database: database instance to backup
-    :type database: DbDir
-    """
-    return [
-        ( PERSON_TBL,  database.person_map.db),
-        ( FAMILY_TBL,  database.family_map.db),
-        ( PLACES_TBL,  database.place_map.db),
-        ( SOURCES_TBL, database.source_map.db),
-        ( CITATIONS_TBL, database.citation_map.db),
-        ( REPO_TBL,    database.repository_map.db),
-        ( NOTE_TBL,    database.note_map.db),
-        ( MEDIA_TBL,   database.media_map.db),
-        ( EVENTS_TBL,  database.event_map.db),
-        ( TAG_TBL,     database.tag_map.db),
-        ( META,        database.metadata.db),
-        ]
diff --git a/gramps/gen/db/dbconst.py b/gramps/gen/db/dbconst.py
index ba3514afb..21a17db80 100644
--- a/gramps/gen/db/dbconst.py
+++ b/gramps/gen/db/dbconst.py
@@ -28,26 +28,23 @@ Declare constants used by database modules
 # constants
 #
 #-------------------------------------------------------------------------
-__all__ = (
-            ('DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO',
-             'DBEXT', 'DBMODE_R', 'DBMODE_W', 'DBUNDOFN', 'DBLOCKFN',
-             'DBRECOVFN','BDBVERSFN', 'DBLOGNAME', 'DBFLAGS_O',  'DBFLAGS_R',
-             'DBFLAGS_D', 'SCHVERSFN', 'PCKVERSFN'
-            ) +
-            
-            ('PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'CITATION_KEY',
-             'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY',
-             'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY'
-            ) +
-
-            ('TXNADD', 'TXNUPD', 'TXNDEL')
-          )
+__all__ = ( 'DBPAGE', 'DBMODE', 'DBCACHE', 'DBLOCKS', 'DBOBJECTS', 'DBUNDO',
+            'DBEXT', 'DBMODE_R', 'DBMODE_W', 'DBUNDOFN', 'DBLOCKFN',
+            'DBRECOVFN','BDBVERSFN', 'DBLOGNAME', 'SCHVERSFN', 'PCKVERSFN',
+            'DBBACKEND',
+            'PERSON_KEY', 'FAMILY_KEY', 'SOURCE_KEY', 'CITATION_KEY',
+            'EVENT_KEY', 'MEDIA_KEY', 'PLACE_KEY', 'REPOSITORY_KEY',
+            'NOTE_KEY', 'REFERENCE_KEY', 'TAG_KEY',
+            'TXNADD', 'TXNUPD', 'TXNDEL',
+            "CLASS_TO_KEY_MAP", "KEY_TO_CLASS_MAP", "KEY_TO_NAME_MAP"
+        )
 
 DBEXT     = ".db"           # File extension to be used for database files
 DBUNDOFN  = "undo.db"       # File name of 'undo' database
 DBLOCKFN  = "lock"          # File name of lock file
 DBRECOVFN = "need_recover"  # File name of recovery file
 BDBVERSFN = "bdbversion.txt"# File name of Berkeley DB version file
+DBBACKEND = "database.txt"  # File name of Database backend file
 SCHVERSFN = "schemaversion.txt"# File name of schema version file
 PCKVERSFN = "pickleupgrade.txt" # Indicator that pickle has been upgrade t Python3
 DBLOGNAME = ".Db"           # Name of logger
@@ -60,18 +57,6 @@ DBLOCKS   = 100000          # Maximum number of locks supported
 DBOBJECTS = 100000          # Maximum number of simultaneously locked objects
 DBUNDO    = 1000            # Maximum size of undo buffer
 
-try:
-    from bsddb3.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
-    DBFLAGS_O = DB_CREATE | DB_AUTO_COMMIT  # Default flags for database open
-    DBFLAGS_R = DB_RDONLY                   # Flags to open a database read-only
-    DBFLAGS_D = DB_DUP | DB_DUPSORT         # Default flags for duplicate keys
-except:
-    print("WARNING: no bsddb support")
-    # FIXME: make this more abstract to deal with other backends, or do not import
-    DBFLAGS_O = DB_CREATE = DB_AUTO_COMMIT = 0
-    DBFLAGS_R = DB_RDONLY = 0
-    DBFLAGS_D = DB_DUP = DB_DUPSORT = 0
-
 PERSON_KEY     = 0
 FAMILY_KEY     = 1
 SOURCE_KEY     = 2
@@ -85,3 +70,37 @@ TAG_KEY        = 9
 CITATION_KEY   = 10
 
 TXNADD, TXNUPD, TXNDEL = 0, 1, 2
+
+CLASS_TO_KEY_MAP = {"Person": PERSON_KEY, 
+                    "Family": FAMILY_KEY, 
+                    "Source": SOURCE_KEY, 
+                    "Citation": CITATION_KEY, 
+                    "Event": EVENT_KEY, 
+                    "MediaObject": MEDIA_KEY, 
+                    "Place": PLACE_KEY, 
+                    "Repository": REPOSITORY_KEY,
+                    "Note" : NOTE_KEY,
+                    "Tag": TAG_KEY}
+
+KEY_TO_CLASS_MAP = {PERSON_KEY: "Person", 
+                    FAMILY_KEY: "Family", 
+                    SOURCE_KEY: "Source", 
+                    CITATION_KEY: "Citation", 
+                    EVENT_KEY: "Event", 
+                    MEDIA_KEY: "MediaObject", 
+                    PLACE_KEY: "Place", 
+                    REPOSITORY_KEY: "Repository",
+                    NOTE_KEY: "Note",
+                    TAG_KEY: "Tag"}
+
+KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
+                   FAMILY_KEY: 'family',
+                   EVENT_KEY: 'event',
+                   SOURCE_KEY: 'source',
+                   CITATION_KEY: 'citation',
+                   PLACE_KEY: 'place',
+                   MEDIA_KEY: 'media',
+                   REPOSITORY_KEY: 'repository',
+                   #REFERENCE_KEY: 'reference',
+                   NOTE_KEY: 'note',
+                   TAG_KEY: 'tag'}
diff --git a/gramps/gen/db/undoredo.py b/gramps/gen/db/undoredo.py
index 7fed4d876..bfcd652d6 100644
--- a/gramps/gen/db/undoredo.py
+++ b/gramps/gen/db/undoredo.py
@@ -1,77 +1,12 @@
-#
-# Gramps - a GTK+/GNOME based genealogy program
-#
-# Copyright (C) 2004-2006 Donald N. Allingham
-# Copyright (C) 2011       Tim G L Lyons
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-#
-
-"""
-Exports the DbUndo class for managing Gramps transactions
-undos and redos.
-"""
-
 #-------------------------------------------------------------------------
 #
 # Standard python modules
 #
 #-------------------------------------------------------------------------
-import time, os
+import time
 import pickle
 from collections import deque
 
-try:
-    from bsddb3 import db
-except:
-    # FIXME: make this more abstract to deal with other backends
-    class db:
-        DBRunRecoveryError = 0
-        DBAccessError = 0
-        DBPageNotFoundError = 0
-        DBInvalidArgError = 0
-        
-from ..const import GRAMPS_LOCALE as glocale
-_ = glocale.translation.gettext
-
-#-------------------------------------------------------------------------
-#
-# Gramps modules
-#
-#-------------------------------------------------------------------------
-from ..constfunc import conv_to_unicode, handle2internal, win
-from .dbconst import *
-from . import BSDDBTxn
-from ..errors import DbError
-
-#-------------------------------------------------------------------------
-#
-# Local Constants
-#
-#-------------------------------------------------------------------------
-DBERRS      = (db.DBRunRecoveryError, db.DBAccessError, 
-               db.DBPageNotFoundError, db.DBInvalidArgError)
-               
-_SIGBASE = ('person', 'family', 'source', 'event', 'media',
-            'place', 'repository', 'reference', 'note', 'tag', 'citation')
-
-#-------------------------------------------------------------------------
-#
-# DbUndo class
-#
-#-------------------------------------------------------------------------            
 class DbUndo(object):
     """
     Base class for the Gramps undo/redo manager.  Needs to be subclassed
@@ -100,7 +35,6 @@ class DbUndo(object):
                         self.db.media_map,
                         self.db.place_map,
                         self.db.repository_map,
-                        self.db.reference_map,
                         self.db.note_map,
                         self.db.tag_map,
                         self.db.citation_map,
@@ -171,6 +105,16 @@ class DbUndo(object):
         """         
         raise NotImplementedError
 
+    def __redo(self, update_history):
+        """
+        """
+        raise NotImplementedError
+
+    def __undo(self, update_history):
+        """
+        """
+        raise NotImplementedError
+
     def commit(self, txn, msg):
         """
         Commit the transaction to the undo/redo database.  "txn" should be
@@ -196,105 +140,6 @@ class DbUndo(object):
             return False
         return self.__redo(update_history)
 
-    def undoredo(func):
-        """
-        Decorator function to wrap undo and redo operations within a bsddb
-        transaction.  It also catches bsddb errors and raises an exception
-        as appropriate
-        """
-        def try_(self, *args, **kwargs):
-            try:
-                with BSDDBTxn(self.db.env) as txn:
-                    self.txn = self.db.txn = txn.txn
-                    status = func(self, *args, **kwargs)
-                    if not status:
-                        txn.abort()
-                    self.db.txn = None
-                    return status
-
-            except DBERRS as msg:
-                self.db._log_error()
-                raise DbError(msg)
-
-        return try_        
-
-    @undoredo
-    def __undo(self, update_history=True):
-        """
-        Access the last committed transaction, and revert the data to the 
-        state before the transaction was committed.
-        """
-        txn = self.undoq.pop()
-        self.redoq.append(txn)
-        transaction = txn
-        db = self.db
-        subitems = transaction.get_recnos(reverse=True)
-
-        # Process all records in the transaction
-        for record_id in subitems:
-            (key, trans_type, handle, old_data, new_data) = \
-                    pickle.loads(self.undodb[record_id])
-
-            if key == REFERENCE_KEY:
-                self.undo_reference(old_data, handle, self.mapbase[key])
-            else:
-                self.undo_data(old_data, handle, self.mapbase[key],
-                                db.emit, _SIGBASE[key])
-        # Notify listeners
-        if db.undo_callback:
-            if self.undo_count > 0:
-                db.undo_callback(_("_Undo %s")
-                                   % self.undoq[-1].get_description())
-            else:
-                db.undo_callback(None)    
-
-        if db.redo_callback:
-            db.redo_callback(_("_Redo %s")
-                                   % transaction.get_description())
-
-        if update_history and db.undo_history_callback:
-            db.undo_history_callback()
-        return True
-
-    @undoredo
-    def __redo(self, db=None, update_history=True):
-        """
-        Access the last undone transaction, and revert the data to the state 
-        before the transaction was undone.
-        """
-        txn = self.redoq.pop()
-        self.undoq.append(txn)
-        transaction = txn
-        db = self.db
-        subitems = transaction.get_recnos()
-
-        # Process all records in the transaction
-        for record_id in subitems:
-            (key, trans_type, handle, old_data, new_data) = \
-                pickle.loads(self.undodb[record_id])
-
-            if key == REFERENCE_KEY:
-                self.undo_reference(new_data, handle, self.mapbase[key])
-            else:
-                self.undo_data(new_data, handle, self.mapbase[key],
-                                    db.emit, _SIGBASE[key])
-        # Notify listeners
-        if db.undo_callback:
-            db.undo_callback(_("_Undo %s")
-                                   % transaction.get_description())
-
-        if db.redo_callback:
-            if self.redo_count > 1:
-                new_transaction = self.redoq[-2]
-                db.redo_callback(_("_Redo %s")
-                                   % new_transaction.get_description())
-            else:
-                db.redo_callback(None)       
-
-        if update_history and db.undo_history_callback:
-            db.undo_history_callback()
-        return True        
-
     def undo_reference(self, data, handle, db_map):
         """
         Helper method to undo a reference map entry
@@ -332,185 +177,3 @@ class DbUndo(object):
 
     undo_count = property(lambda self:len(self.undoq))
     redo_count = property(lambda self:len(self.redoq))
-
-class DbUndoList(DbUndo):
-    """
-    Implementation of the Gramps undo database using a Python list
-    """
-    def __init__(self, grampsdb):
-        """
-        Class constructor
-        """
-        super(DbUndoList, self).__init__(grampsdb)
-        self.undodb = []
-
-    def open(self):
-        """
-        A list does not need to be opened
-        """
-        pass
-
-    def close(self):
-        """
-        Close the list by resetting it to empty
-        """
-        self.undodb = []
-        self.clear()
-
-    def append(self, value):
-        """
-        Add an entry on the end of the list
-        """
-        self.undodb.append(value)
-        return len(self.undodb)-1
-
-    def __getitem__(self, index):
-        """
-        Return an item at the specified index
-        """
-        return self.undodb[index]
-
-    def __setitem__(self, index, value):
-        """
-        Set an item at the speficied index to the given value
-        """
-        self.undodb[index] = value
-
-    def __iter__(self):
-        """
-        Iterator
-        """
-        for item in self.undodb:
-            yield item
-
-    def __len__(self):
-        """
-        Return number of entries in the list
-        """
-        return len(self.undodb)
-
-class DbUndoBSDDB(DbUndo):
-    """
-    Class constructor for Gramps undo/redo database using a bsddb recno
-    database as the backing store.
-    """
-
-    def __init__(self, grampsdb, path):
-        """
-        Class constructor
-        """
-        super(DbUndoBSDDB, self).__init__(grampsdb)
-        self.undodb = db.DB()
-        self.path = path
-
-    def open(self):
-        """
-        Open the undo/redo database
-        """
-        path = self.path
-        self.undodb.open(path, db.DB_RECNO, db.DB_CREATE)
-        
-    def close(self):
-        """
-        Close the undo/redo database
-        """
-        self.undodb.close()
-        self.undodb = None
-        self.mapbase = None
-        self.db = None
-        
-        try:
-            os.remove(self.path)
-        except OSError:
-            pass
-        self.clear()      
-
-    def append(self, value):
-        """
-        Add an entry on the end of the database
-        """
-        return self.undodb.append(value)
-
-    def __len__(self):
-        """
-        Returns the number of entries in the database
-        """
-        x = self.undodb.stat()['nkeys']
-        y = len(self.undodb)
-        assert x == y
-        return x
-
-    def __getitem__(self, index):
-        """
-        Returns the entry stored at the specified index
-        """
-        return self.undodb.get(index)
-
-    def __setitem__(self, index, value):
-        """
-        Sets the entry stored at the specified index to the value given.
-        """
-        self.undodb.put(index, value)
-
-    def __iter__(self):
-        """
-        Iterator
-        """
-        cursor = self.undodb.cursor()
-        data = cursor.first()
-        while data:
-            yield data
-            data = next(cursor)
-
-def testundo():
-    class T:
-        def __init__(self):
-            self.msg = ''
-            self.timetstamp = 0
-        def set_description(self, msg):
-            self.msg = msg
-
-    class D:
-        def __init__(self):
-            self.person_map = {}
-            self.family_map = {}
-            self.source_map = {}
-            self.event_map  = {}
-            self.media_map  = {}
-            self.place_map  = {}
-            self.note_map   = {}
-            self.tag_map   = {}
-            self.repository_map = {}
-            self.reference_map  = {}
-
-    print("list tests")
-    undo = DbUndoList(D())
-    print(undo.append('foo'))
-    print(undo.append('bar'))
-    print(undo[0])
-    undo[0] = 'foobar'
-    print(undo[0])
-    print("len", len(undo))
-    print("iter")
-    for data in undo:
-        print(data)
-    print()
-    print("bsddb tests")
-    undo = DbUndoBSDDB(D(), '/tmp/testundo')
-    undo.open()
-    print(undo.append('foo'))
-    print(undo.append('fo2'))
-    print(undo.append('fo3'))
-    print(undo[1])
-    undo[1] = 'bar'
-    print(undo[1])
-    for data in undo:
-        print(data)
-    print("len", len(undo))
-
-    print("test commit")
-    undo.commit(T(), msg="test commit")
-    undo.close()
-
-if __name__ == '__main__':
-    testundo()
diff --git a/gramps/gen/dbstate.py b/gramps/gen/dbstate.py
index 839d05cd2..b232d612e 100644
--- a/gramps/gen/dbstate.py
+++ b/gramps/gen/dbstate.py
@@ -22,13 +22,23 @@
 """
 Provide the database state class
 """
+import sys
+import os
+import io
 
-from .db import DbBsddbRead
 from .db import DbReadBase
 from .proxy.proxybase import ProxyDbBase
 from .utils.callback import Callback
 from .config import config
 
+#-------------------------------------------------------------------------
+#
+# set up logging
+#
+#-------------------------------------------------------------------------
+import logging
+LOG = logging.getLogger(".dbstate")
+
 class DbState(Callback):
     """
     Provide a class to encapsulate the state of the database.
@@ -45,7 +55,7 @@ class DbState(Callback):
         just a place holder until a real DB is assigned.
         """
         Callback.__init__(self)
-        self.db      = DbBsddbRead()
+        self.db      = self.make_database("bsddb")
         self.open    = False
         self.stack = []
 
@@ -54,9 +64,10 @@ class DbState(Callback):
         Closes the existing db, and opens a new one.
         Retained for backward compatibility.
         """
-        self.emit('no-database', ())
-        self.db.close()
-        self.change_database_noclose(database)
+        if database:
+            self.emit('no-database', ())
+            self.db.close()
+            self.change_database_noclose(database)
 
     def change_database_noclose(self, database):
         """
@@ -88,7 +99,7 @@ class DbState(Callback):
         """
         self.emit('no-database', ())
         self.db.close()
-        self.db = DbBsddbRead()
+        self.db = self.make_database("bsddb")
         self.db.db_is_open = False
         self.open = False
         self.emit('database-changed', (self.db, ))
@@ -122,3 +133,100 @@ class DbState(Callback):
         """
         self.db = self.stack.pop()
         self.emit('database-changed', (self.db, ))
+
+    def make_database(self, id):
+        """
+        Make a database, given a plugin id.
+        """
+        from .plug import BasePluginManager
+        from .const import PLUGINS_DIR, USER_PLUGINS
+
+        pmgr = BasePluginManager.get_instance()
+        pdata = pmgr.get_plugin(id)
+        
+        if not pdata:
+            # This might happen if using gramps from outside, and
+            # we haven't loaded plugins yet
+            pmgr.reg_plugins(PLUGINS_DIR, self, None)
+            pmgr.reg_plugins(USER_PLUGINS, self, None, load_on_reg=True)
+            pdata = pmgr.get_plugin(id)
+
+        if pdata:
+            if pdata.reset_system:
+                if self.modules_is_set():
+                    self.reset_modules()
+                else:
+                    self.save_modules()
+            mod = pmgr.load_plugin(pdata)
+            database = getattr(mod, pdata.databaseclass)
+            return database()
+
+    def open_database(self, dbname, force_unlock=False, callback=None):
+        """
+        Open a database by name and return the database.
+        """
+        data = self.lookup_family_tree(dbname)
+        database = None
+        if data:
+            dbpath, locked, locked_by, backend = data
+            if (not locked) or (locked and force_unlock):
+                database = self.make_database(backend)
+                database.load(dbpath, callback=callback)
+        return database
+
+    def lookup_family_tree(self, dbname):
+        """
+        Find a Family Tree given its name, and return properties.
+        """
+        dbdir = os.path.expanduser(config.get('behavior.database-path'))
+        for dpath in os.listdir(dbdir):
+            dirpath = os.path.join(dbdir, dpath)
+            path_name = os.path.join(dirpath, "name.txt")
+            if os.path.isfile(path_name):
+                file = io.open(path_name, 'r', encoding='utf8')
+                name = file.readline().strip()
+                file.close()
+                if dbname == name:
+                    locked = False
+                    locked_by = None
+                    backend = None
+                    fname = os.path.join(dirpath, "database.txt")
+                    if os.path.isfile(fname):
+                        ifile = io.open(fname, 'r', encoding='utf8')
+                        backend = ifile.read().strip()
+                        ifile.close()
+                    else:
+                        backend = "bsddb"
+                    try:
+                        fname = os.path.join(dirpath, "lock")
+                        ifile = io.open(fname, 'r', encoding='utf8')
+                        locked_by = ifile.read().strip()
+                        locked = True
+                        ifile.close()
+                    except (OSError, IOError):
+                        pass
+                    return (dirpath, locked, locked_by, backend)
+        return None
+
+    ## Work-around for databases that need sys refresh (django):
+    def modules_is_set(self):
+        LOG.info("modules_is_set?")
+        if hasattr(self, "_modules"):
+            return self._modules != None
+        else:
+            self._modules = None
+            return False
+
+    def reset_modules(self):
+        LOG.info("reset_modules!")
+        # First, clear out old modules:
+        for key in list(sys.modules.keys()):
+            del(sys.modules[key])
+        # Next, restore previous:
+        for key in self._modules:
+            sys.modules[key] = self._modules[key]
+
+    def save_modules(self):
+        LOG.info("save_modules!")
+        self._modules = sys.modules.copy()
+
diff --git a/gramps/gen/merge/diff.py b/gramps/gen/merge/diff.py
index 1ba5a45f7..0993638c3 100644
--- a/gramps/gen/merge/diff.py
+++ b/gramps/gen/merge/diff.py
@@ -28,7 +28,7 @@ from gramps.cli.user import User
 from ..dbstate import DbState
 from gramps.cli.grampscli import CLIManager
 from ..plug import BasePluginManager
-from ..db.dictionary import DictionaryDb
+from gramps.plugins.database.dictionarydb import DictionaryDb
 from gramps.gen.lib.handle import HandleClass, Handle
 from gramps.gen.lib import *
 from gramps.gen.lib.personref import PersonRef
diff --git a/gramps/gen/plug/_gramplet.py b/gramps/gen/plug/_gramplet.py
index c040bae7d..0159a8d03 100644
--- a/gramps/gen/plug/_gramplet.py
+++ b/gramps/gen/plug/_gramplet.py
@@ -70,7 +70,6 @@ class Gramplet(object):
         self.connect(self.gui.textview, "motion-notify-event", 
                      self.gui.on_motion)
         self.connect_signal('Person', self._active_changed)
-        
         self._db_changed(self.dbstate.db)
         active_person = self.get_active('Person')
         if active_person: # already changed
@@ -321,8 +320,6 @@ class Gramplet(object):
             self._idle_id = 0
             LOG.debug("gramplet updater: %s : One time, done!" % self.gui.title)
             return False
-        # FIXME: find out why Data Entry has this error, or just ignore it
-        import bsddb3 as bsddb
         try:
             retval = next(self._generator)
             if not retval:
@@ -333,10 +330,6 @@ class Gramplet(object):
             LOG.debug("gramplet updater: %s: return %s" % 
                       (self.gui.title, retval))
             return retval
-        except bsddb.db.DBCursorClosedError:
-            # not sure why---caused by Data Entry Gramplet
-            LOG.warn("bsddb.db.DBCursorClosedError in: %s" % self.gui.title)
-            return False
         except StopIteration:
             self._idle_id = 0
             self._generator.close()
diff --git a/gramps/gen/plug/_manager.py b/gramps/gen/plug/_manager.py
index e047f6166..e30a089ae 100644
--- a/gramps/gen/plug/_manager.py
+++ b/gramps/gen/plug/_manager.py
@@ -412,6 +412,11 @@ class BasePluginManager(object):
         """
         return self.__pgr.sidebar_plugins()
     
+    def get_reg_databases(self):
+        """ Return list of registered database backends
+        """
+        return self.__pgr.database_plugins()
+    
     def get_external_opt_dict(self):
         """ Return the dictionary of external options. """
         return self.__external_opt_dict
diff --git a/gramps/gen/plug/_pluginreg.py b/gramps/gen/plug/_pluginreg.py
index 04167cafc..70a93f0f2 100644
--- a/gramps/gen/plug/_pluginreg.py
+++ b/gramps/gen/plug/_pluginreg.py
@@ -70,8 +70,9 @@ VIEW        = 8
 RELCALC     = 9
 GRAMPLET    = 10
 SIDEBAR     = 11
+DATABASE    = 12
 PTYPE       = [REPORT , QUICKREPORT, TOOL, IMPORT, EXPORT, DOCGEN, GENERAL,
-               MAPSERVICE, VIEW, RELCALC, GRAMPLET, SIDEBAR]
+               MAPSERVICE, VIEW, RELCALC, GRAMPLET, SIDEBAR, DATABASE]
 PTYPE_STR   = {
         REPORT: _('Report') , 
         QUICKREPORT: _('Quickreport'), 
@@ -85,6 +86,7 @@ PTYPE_STR   = {
         RELCALC: _('Relationships'), 
         GRAMPLET: _('Gramplet'),
         SIDEBAR: _('Sidebar'),
+        DATABASE: _('Database'),
         }
 
 #possible report categories
@@ -206,7 +208,7 @@ class PluginData(object):
        The python path where the plugin implementation can be found
     .. attribute:: ptype
        The plugin type. One of REPORT , QUICKREPORT, TOOL, IMPORT,
-        EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, GRAMPLET
+        EXPORT, DOCGEN, GENERAL, MAPSERVICE, VIEW, GRAMPLET, DATABASE
     .. attribute:: authors
        List of authors of the plugin, default=[]
     .. attribute:: authors_email
@@ -349,6 +351,14 @@ class PluginData(object):
        the plugin is appended to the list of plugins. If START, then the
        plugin is prepended. Only set START if you want a plugin to be the
        first in the order of plugins
+
+    Attributes for DATABASE plugins
+
+    .. attribute:: databaseclass
+       The class in the module that is the database class
+    .. attribute:: reset_system
+       Boolean to indicate that the system (sys.modules) should
+       be reset.
     """
 
     def __init__(self):
@@ -421,6 +431,9 @@ class PluginData(object):
         self._menu_label = ''
         #VIEW and SIDEBAR attr
         self._order = END
+        #DATABASE attr
+        self._databaseclass = None
+        self._reset_system = False
         #GENERAL attr
         self._data = []
         self._process = None
@@ -931,6 +944,26 @@ class PluginData(object):
 
     order = property(_get_order, _set_order)
   
+    #DATABASE attributes
+    def _set_databaseclass(self, databaseclass):
+        if not self._ptype == DATABASE:
+            raise ValueError('databaseclass may only be set for DATABASE plugins')
+        self._databaseclass = databaseclass
+
+    def _get_databaseclass(self):
+        return self._databaseclass
+
+    def _set_reset_system(self, reset_system):
+        if not self._ptype == DATABASE:
+            raise ValueError('reset_system may only be set for DATABASE plugins')
+        self._reset_system = reset_system
+
+    def _get_reset_system(self):
+        return self._reset_system
+
+    databaseclass = property(_get_databaseclass, _set_databaseclass)
+    reset_system = property(_get_reset_system, _set_reset_system)
+
     #GENERAL attr
     def _set_data(self, data):
         if not self._ptype in (GENERAL,):
@@ -1032,6 +1065,7 @@ def make_environment(**kwargs):
         'REPORT_MODE_CLI': REPORT_MODE_CLI,
         'TOOL_MODE_GUI': TOOL_MODE_GUI, 
         'TOOL_MODE_CLI': TOOL_MODE_CLI,
+        'DATABASE': DATABASE,
         'GRAMPSVERSION': GRAMPSVERSION,
         'START': START,
         'END': END,
@@ -1297,6 +1331,12 @@ class PluginRegister(object):
         """
         return self.type_plugins(SIDEBAR)
 
+    def database_plugins(self):
+        """
+        Return a list of :class:`PluginData` that are of type DATABASE
+        """
+        return self.type_plugins(DATABASE)
+
     def filter_load_on_reg(self):
         """
         Return a list of :class:`PluginData` that have load_on_reg == True
diff --git a/gramps/gen/utils/callman.py b/gramps/gen/utils/callman.py
index ca6a94d86..939a0f6fa 100644
--- a/gramps/gen/utils/callman.py
+++ b/gramps/gen/utils/callman.py
@@ -303,7 +303,8 @@ class CallbackManager(object):
         Do a custom db connect signal outside of the primary object ones
         managed automatically.
         """
-        self.custom_signal_keys.append(self.database.connect(name, callback))
+        if self.database:
+            self.custom_signal_keys.append(self.database.connect(name, callback))
     
     def __callbackcreator(self, signal, noarg=False):
         """
diff --git a/gramps/grampsapp.py b/gramps/grampsapp.py
index b9e4a6015..e2d7215e7 100644
--- a/gramps/grampsapp.py
+++ b/gramps/grampsapp.py
@@ -136,14 +136,6 @@ if not sys.version_info >= MIN_PYTHON_VERSION :
              'v3': MIN_PYTHON_VERSION[2]})
     sys.exit(1)
 
-try:
-    import bsddb3
-except ImportError:
-    logging.warning(_("\nYou don't have the python3 bsddb3 package installed."
-        " This package is needed to start Gramps.\n\n"
-         "Gramps will terminate now."))
-    sys.exit(1)
-    
 #-------------------------------------------------------------------------
 #
 # Gramps libraries
diff --git a/gramps/gui/aboutdialog.py b/gramps/gui/aboutdialog.py
index 9bd977d55..bf0fc7053 100644
--- a/gramps/gui/aboutdialog.py
+++ b/gramps/gui/aboutdialog.py
@@ -28,7 +28,6 @@
 import os
 import sys
 import io
-import bsddb3 as bsddb
 
 ##import logging
 ##_LOG = logging.getLogger(".GrampsAboutDialog")
@@ -60,6 +59,20 @@ _ = glocale.translation.gettext
 from gramps.gen.constfunc import get_env_var
 from .display import display_url
 
+def ellipses(text):
+    """
+    Ellipsize text on length 40
+    """
+    if len(text) > 40:
+        return text[:40] + "..."
+    return text
+
+try:
+    import bsddb3 as bsddb ## ok, in try/except
+    BSDDB_STR = ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version()))
+except:
+    BSDDB_STR = 'not found'
+
 #-------------------------------------------------------------------------
 #
 # GrampsAboutDialog
@@ -125,19 +138,11 @@ class GrampsAboutDialog(Gtk.AboutDialog):
                  "Distribution: %s")
                 % (ellipses(str(VERSION)),
                    ellipses(str(sys.version).replace('\n','')),
-                   ellipses(str(bsddb.__version__) + " " + str(bsddb.db.version())),
+                   BSDDB_STR,
                    ellipses(get_env_var('LANG','')),
                    ellipses(operatingsystem),
                    ellipses(distribution)))
 
-def ellipses(text):
-    """
-    Ellipsize text on length 40
-    """
-    if len(text) > 40:
-        return text[:40] + "..."
-    return text
-
 #-------------------------------------------------------------------------
 #
 # AuthorParser
diff --git a/gramps/gui/dbloader.py b/gramps/gui/dbloader.py
index 6e7f85d5e..d95c6bb9a 100644
--- a/gramps/gui/dbloader.py
+++ b/gramps/gui/dbloader.py
@@ -52,10 +52,10 @@ from gi.repository import GObject
 #
 #-------------------------------------------------------------------------
 from gramps.gen.const import GRAMPS_LOCALE as glocale
+from gramps.gen.db.dbconst import DBBACKEND
 _ = glocale.translation.gettext
 from gramps.cli.grampscli import CLIDbLoader
 from gramps.gen.config import config
-from gramps.gen.db import DbBsddb
 from gramps.gen.db.exceptions import (DbUpgradeRequiredError, 
                                       BsddbDowngradeError, 
                                       DbVersionError, 
@@ -305,7 +305,14 @@ class DbLoader(CLIDbLoader):
         else:
             mode = 'w'
 
-        db = DbBsddb()
+        dbid_path = os.path.join(filename, DBBACKEND)
+        if os.path.isfile(dbid_path):
+            with open(dbid_path) as fp:
+                dbid = fp.read().strip()
+        else:
+            dbid = "bsddb"
+
+        db = self.dbstate.make_database(dbid)
         db.disable_signals()
         self.dbstate.no_database()
 
diff --git a/gramps/gui/dbman.py b/gramps/gui/dbman.py
index 223aa8b14..eda65fe06 100644
--- a/gramps/gui/dbman.py
+++ b/gramps/gui/dbman.py
@@ -69,20 +69,18 @@ from gi.repository import Pango
 #
 #-------------------------------------------------------------------------
 from gramps.gen.const import GRAMPS_LOCALE as glocale
+from gramps.gen.plug import BasePluginManager
 _ = glocale.translation.gettext
 from gramps.gen.const import URL_WIKISTRING
 from .user import User
-from .dialog import ErrorDialog, QuestionDialog, QuestionDialog2
-from gramps.gen.db import DbBsddb
+from .dialog import ErrorDialog, QuestionDialog, QuestionDialog2, ICON
 from .pluginmanager import GuiPluginManager
 from gramps.cli.clidbman import CLIDbManager, NAME_FILE, time_val
 from .ddtargets import DdTargets
 from gramps.gen.recentfiles import rename_filename, remove_filename
 from .glade import Glade
-from gramps.gen.db.backup import restore
 from gramps.gen.db.exceptions import DbException
 
-
 _RETURN = Gdk.keyval_from_name("Return")
 _KP_ENTER = Gdk.keyval_from_name("KP_Enter")
 
@@ -106,6 +104,25 @@ ICON_COL = 6
 
 RCS_BUTTON = { True : _('_Extract'), False : _('_Archive') }
 
+class DatabaseDialog(Gtk.MessageDialog):
+    def __init__(self, parent, options):
+        """
+        options = [(pdata, number), ...]
+        """
+        Gtk.MessageDialog.__init__(self,
+                                parent,
+                                flags=Gtk.DialogFlags.MODAL,
+                                type=Gtk.MessageType.QUESTION,
+                                   )
+        self.set_icon(ICON)
+        self.set_title('')
+        self.set_markup('<span size="larger" weight="bold">%s</span>' %
+                        _('Database Backend for New Tree'))
+        self.format_secondary_text(
+            _("Please select a database backend type:"))
+        for option, number in options:
+            self.add_button(option.name, number)
+
 class DbManager(CLIDbManager):
     """
     Database Manager. Opens a database manager window that allows users to
@@ -531,8 +548,8 @@ class DbManager(CLIDbManager):
         new_path, newname = self._create_new_db("%s : %s" % (parent_name, name))
         
         self.__start_cursor(_("Extracting archive..."))
-        dbclass = DbBsddb
-        dbase = dbclass()
+
+        dbase = self.dbstate.make_database("bsddb")
         dbase.load(new_path, None)
         
         self.__start_cursor(_("Importing archive..."))
@@ -719,18 +736,17 @@ class DbManager(CLIDbManager):
                 fname = os.path.join(dirname, filename)
                 os.unlink(fname)
 
-        newdb = DbBsddb()
+        newdb = self.dbstate.make_database("bsddb")
         newdb.write_version(dirname)
 
-        dbclass = DbBsddb
-        dbase = dbclass()
+        dbase = self.dbstate.make_database("bsddb")
         dbase.set_save_path(dirname)
         dbase.load(dirname, None)
 
         self.__start_cursor(_("Rebuilding database from backup files"))
         
         try:
-            restore(dbase)
+            dbase.restore()
         except DbException as msg:
             DbManager.ERROR(_("Error restoring backup data"), msg)
 
@@ -764,19 +780,37 @@ class DbManager(CLIDbManager):
         message.
         """
         self.new.set_sensitive(False)
-        try:
-            self._create_new_db()
-        except (OSError, IOError) as msg:
-            DbManager.ERROR(_("Could not create Family Tree"),
-                                       str(msg))
+        dbid = None
+        pmgr = BasePluginManager.get_instance()
+        pdata = pmgr.get_reg_databases()
+        # If just one database backend, just use it:
+        if len(pdata) == 0:
+            DbManager.ERROR(_("No available database backends"),
+                            _("Please check your dependencies."))
+        elif len(pdata) == 1:
+            dbid = pdata[0].id
+        elif len(pdata) > 1:
+            options = sorted(list(zip(pdata, range(1, len(pdata) + 1))), key=lambda items: items[0].name)
+            d = DatabaseDialog(self.top, options)
+            number = d.run()
+            d.destroy()
+            if number >= 0:
+                dbid = [option[0].id for option in options if option[1] == number][0]
+        ### Now, let's load it up
+        if dbid:
+            try:
+                self._create_new_db(dbid=dbid)
+            except (OSError, IOError) as msg:
+                DbManager.ERROR(_("Could not create Family Tree"),
+                                str(msg))
         self.new.set_sensitive(True)
 
-    def _create_new_db(self, title=None, create_db=True):
+    def _create_new_db(self, title=None, create_db=True, dbid=None):
         """
         Create a new database, append to model
         """
         new_path, title = self.create_new_db_cli(conv_to_unicode(title, 'utf8'),
-                                                 create_db)
+                                                 create_db, dbid)
         path_name = os.path.join(new_path, NAME_FILE)
         (tval, last) = time_val(new_path)
         node = self.model.append(None, [title, new_path, path_name, 
diff --git a/gramps/gui/editors/editfamily.py b/gramps/gui/editors/editfamily.py
index 707bf0853..2c5110b1b 100644
--- a/gramps/gui/editors/editfamily.py
+++ b/gramps/gui/editors/editfamily.py
@@ -26,7 +26,6 @@
 # python modules
 #
 #-------------------------------------------------------------------------
-from bsddb3 import db as bsddb_db
 import pickle
 
 #-------------------------------------------------------------------------
@@ -1026,10 +1025,11 @@ class EditFamily(EditPrimary):
                )
             
     def save(self, *obj):
-        try:
-            self.__do_save()
-        except bsddb_db.DBRunRecoveryError as msg:
-            RunDatabaseRepair(msg[1])
+        ## FIXME: how to catch a specific error?
+        #try:
+        self.__do_save()
+        #except bsddb_db.DBRunRecoveryError as msg:
+        #    RunDatabaseRepair(msg[1])
 
     def __do_save(self):
         self.ok_button.set_sensitive(False)
diff --git a/gramps/gui/logger/_errorreportassistant.py b/gramps/gui/logger/_errorreportassistant.py
index e5c81597a..14c6ba1a2 100644
--- a/gramps/gui/logger/_errorreportassistant.py
+++ b/gramps/gui/logger/_errorreportassistant.py
@@ -30,7 +30,12 @@ from gi.repository import GdkPixbuf
 from gi.repository import GObject
 import cairo
 import sys, os
-import bsddb3 as bsddb
+
+try:
+    import bsddb3 as bsddb # ok, in try/except
+    BSDDB_STR = str(bsddb.__version__) + " " + str(bsddb.db.version())
+except:
+    BSDDB_STR = 'not found'
     
 #-------------------------------------------------------------------------
 #
@@ -166,7 +171,7 @@ class ErrorReportAssistant(Gtk.Assistant):
                "gobject version: %s\n"\
                "cairo version  : %s"\
                % (str(sys.version).replace('\n',''),
-                  str(bsddb.__version__) + " " + str(bsddb.db.version()),
+                  BSDDB_STR,
                   str(VERSION),
                   get_env_var('LANG',''),
                   operatingsystem,
diff --git a/gramps/gui/pluginmanager.py b/gramps/gui/pluginmanager.py
index edde82dac..bb609e984 100644
--- a/gramps/gui/pluginmanager.py
+++ b/gramps/gui/pluginmanager.py
@@ -205,7 +205,12 @@ class GuiPluginManager(Callback):
         return [plg for plg in self.basemgr.get_reg_docgens()
                                 if plg.id not in self.__hidden_plugins]
 
+    def get_reg_databases(self):
+        """ Return list of non hidden registered database backends
+        """
+        return [plg for plg in self.basemgr.get_reg_databases()
+                                if plg.id not in self.__hidden_plugins]
+
     def get_reg_general(self, category=None):
         return [plg for plg in self.basemgr.get_reg_general(category)
                                 if plg.id not in self.__hidden_plugins]
-
diff --git a/gramps/gui/viewmanager.py b/gramps/gui/viewmanager.py
index 721706faf..11e37a1c9 100644
--- a/gramps/gui/viewmanager.py
+++ b/gramps/gui/viewmanager.py
@@ -87,7 +87,6 @@ from gramps.gen.utils.file import media_path_full
 from .dbloader import DbLoader
 from .display import display_help, display_url
 from .configure import GrampsPreferences
-from gramps.gen.db.backup import backup
 from gramps.gen.db.exceptions import DbException
 from .aboutdialog import GrampsAboutDialog
 from .navigator import Navigator
@@ -762,7 +761,7 @@ class ViewManager(CLIManager):
             self.uistate.progress.show()
             self.uistate.push_message(self.dbstate, _("Autobackup..."))
             try:
-                backup(self.dbstate.db)
+                self.dbstate.db.backup()
             except DbException as msg:
                 ErrorDialog(_("Error saving backup data"), msg)
             self.uistate.set_busy_cursor(False)
@@ -1594,6 +1593,11 @@ def run_plugin(pdata, dbstate, uistate):
     mod = pmgr.load_plugin(pdata)
     if not mod:
         #import of plugin failed
+        failed = pmgr.get_fail_list()
+        if failed:
+            error_msg = failed[-1][1][1]
+        else:
+            error_msg = "(no error message)"
         ErrorDialog(
             _('Failed Loading Plugin'),
             _('The plugin %(name)s did not load and reported an error.\n\n'
@@ -1608,7 +1612,7 @@ def run_plugin(pdata, dbstate, uistate):
                 'gramps_bugtracker_url' : URL_BUGHOME,
                 'firstauthoremail': pdata.authors_email[0] if
                         pdata.authors_email else '...',
-                'error_msg': pmgr.get_fail_list()[-1][1][1]})
+                  'error_msg': error_msg})
         return
 
     if pdata.ptype == REPORT:
diff --git a/gramps/gen/db/test/__init__.py b/gramps/plugins/database/__init__.py
similarity index 100%
rename from gramps/gen/db/test/__init__.py
rename to gramps/plugins/database/__init__.py
diff --git a/gramps/plugins/database/bsddb.gpr.py b/gramps/plugins/database/bsddb.gpr.py
new file mode 100644
index 000000000..82a23573c
--- /dev/null
+++ b/gramps/plugins/database/bsddb.gpr.py
@@ -0,0 +1,31 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2015 Douglas Blank <doug.blank@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+plg = newplugin()
+plg.id    = 'bsddb'
+plg.name  = _("BSDDB Database Backend")
+plg.name_accell  = _("_BSDDB Database Backend")
+plg.description =  _("Berkeley Software Distribution Database Backend")
+plg.version = '1.0'
+plg.gramps_target_version = "5.0"
+plg.status = STABLE
+plg.fname = 'bsddb.py'
+plg.ptype = DATABASE
+plg.databaseclass = 'DbBsddb'
diff --git a/gramps/plugins/database/bsddb.py b/gramps/plugins/database/bsddb.py
new file mode 100644
index 000000000..9cbca25ce
--- /dev/null
+++ b/gramps/plugins/database/bsddb.py
@@ -0,0 +1,3 @@
+
+from bsddb_support import DbBsddb
+
diff --git a/gramps/plugins/database/bsddb_support/__init__.py b/gramps/plugins/database/bsddb_support/__init__.py
new file mode 100644
index 000000000..1dd4bce56
--- /dev/null
+++ b/gramps/plugins/database/bsddb_support/__init__.py
@@ -0,0 +1,95 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2000-2007  Donald N. Allingham
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+"""
+Gramps Database API.
+
+Database Architecture
+=====================
+
+Access to the database is made through Python classes. Exactly
+what functionality you have is dependent on the properties of the
+database. For example, if you are accessing a read-only view, then
+you will only have access to a subset of the methods available.
+
+At the root of any database interface is either :py:class:`.DbReadBase` and/or
+:py:class:`.DbWriteBase`. These define the methods to read and write to a
+database, respectively.
+
+The full database hierarchy is:
+
+- :py:class:`.DbBsddb` - read and write implementation to BSDDB databases
+
+  * :py:class:`.DbWriteBase` - virtual and implementation-independent methods
+    for reading data
+
+  * :py:class:`.DbBsddbRead` - read-only (accessors, getters) implementation
+    to BSDDB databases
+
+    + :py:class:`.DbReadBase` - virtual and implementation-independent
+      methods for reading data
+
+    + :py:class:`.Callback` - callback and signal functions
+
+  * :py:class:`.UpdateCallback` - callback functionality
+
+- :py:class:`.DbDjango` - read and write implementation to Django-based
+  databases
+
+  * :py:class:`.DbWriteBase` - virtual and implementation-independent methods
+    for reading data
+
+  * :py:class:`.DbReadBase` - virtual and implementation-independent methods
+    for reading data
+
+DbBsddb
+=======
+
+The :py:class:`.DbBsddb` interface defines a hierarchical database
+(non-relational) written in
+`PyBSDDB <http://www.jcea.es/programacion/pybsddb.htm>`_. There is no
+such thing as a database schema, and the meaning of the data is
+defined in the Python classes above. The data is stored as pickled
+tuples and unserialized into the primary data types (below).
+
+DbDjango
+========
+
+The DbDjango interface defines the Gramps data in terms of
+*models* and *relations* from the
+`Django project <http://www.djangoproject.com/>`_. The database
+backend can be any implementation that supports Django, including
+such popular SQL implementations as sqlite, MySQL, Postgresql, and
+Oracle. The data is retrieved from the SQL fields, serialized and
+then unserialized into the primary data types (below).
+
+More details can be found in the manual's
+`Using database API <http://www.gramps-project.org/wiki/index.php?title=Using_database_API>`_.
+"""
+
+from gramps.gen.db.base import *
+from gramps.gen.db.dbconst import *
+from .cursor import *
+from .read import *
+from .bsddbtxn import *
+from gramps.gen.db.txn import *
+from .undoredo import *
+from gramps.gen.db.exceptions import *
+from .write import *
diff --git a/gramps/plugins/database/bsddb_support/backup.py b/gramps/plugins/database/bsddb_support/backup.py
new file mode 100644
index 000000000..94350da56
--- /dev/null
+++ b/gramps/plugins/database/bsddb_support/backup.py
@@ -0,0 +1,74 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2007  Donald N. Allingham
+# Copyright (C) 2011       Tim G L Lyons
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# gen/db/backup.py
+
+"""
+Description
+===========
+
+This module Provides backup and restore functions for a database. The
+backup function saves the data into backup files, while the restore
+function loads the data back into a database.
+
+You should only restore the data into an empty database.
+
+Implementation
+==============
+
+Not all of the database tables need to be backed up, since many are
+automatically generated from the others. The tables that are backed up
+are the primary tables and the metadata table.
+
+The database consists of a table of "pickled" tuples. Each of the
+primary tables is "walked", and the pickled tuple is extracted, and
+written to the backup file.
+
+Restoring the data is just as simple. The backup file is parsed an
+entry at a time, and inserted into the associated database table. The
+derived tables are built automatically as the items are entered into
+db.
+"""
+
+#-------------------------------------------------------------------------
+#
+# load standard python libraries
+#
+#-------------------------------------------------------------------------
+import os
+import pickle
+
+#------------------------------------------------------------------------
+#
+# Gramps libs
+#
+#------------------------------------------------------------------------
+from gramps.gen.db.exceptions import DbException
+from .write import FAMILY_TBL, PLACES_TBL, SOURCES_TBL, MEDIA_TBL, \
+    EVENTS_TBL, PERSON_TBL, REPO_TBL, NOTE_TBL, TAG_TBL, META, CITATIONS_TBL
+
+#------------------------------------------------------------------------
+#
+# Set up logging
+#
+#------------------------------------------------------------------------
+import logging
+LOG = logging.getLogger(".Backup")
+
diff --git a/gramps/gen/db/bsddbtxn.py b/gramps/plugins/database/bsddb_support/bsddbtxn.py
similarity index 100%
rename from gramps/gen/db/bsddbtxn.py
rename to gramps/plugins/database/bsddb_support/bsddbtxn.py
diff --git a/gramps/gen/db/cursor.py b/gramps/plugins/database/bsddb_support/cursor.py
similarity index 100%
rename from gramps/gen/db/cursor.py
rename to gramps/plugins/database/bsddb_support/cursor.py
diff --git a/gramps/gen/db/read.py b/gramps/plugins/database/bsddb_support/read.py
similarity index 98%
rename from gramps/gen/db/read.py
rename to gramps/plugins/database/bsddb_support/read.py
index 87dc112d6..9280dbc09 100644
--- a/gramps/gen/db/read.py
+++ b/gramps/plugins/database/bsddb_support/read.py
@@ -53,30 +53,32 @@ import logging
 # GRAMPS libraries
 #
 #-------------------------------------------------------------------------
-from ..lib.mediaobj import MediaObject
-from ..lib.person import Person
-from ..lib.family import Family
-from ..lib.src import Source
-from ..lib.citation import Citation
-from ..lib.event import Event
-from ..lib.place import Place
-from ..lib.repo import Repository
-from ..lib.note import Note
-from ..lib.tag import Tag
-from ..lib.genderstats import GenderStats
-from ..lib.researcher import Researcher 
-from ..lib.nameorigintype import NameOriginType
+from gramps.gen.lib.mediaobj import MediaObject
+from gramps.gen.lib.person import Person
+from gramps.gen.lib.family import Family
+from gramps.gen.lib.src import Source
+from gramps.gen.lib.citation import Citation
+from gramps.gen.lib.event import Event
+from gramps.gen.lib.place import Place
+from gramps.gen.lib.repo import Repository
+from gramps.gen.lib.note import Note
+from gramps.gen.lib.tag import Tag
+from gramps.gen.lib.genderstats import GenderStats
+from gramps.gen.lib.researcher import Researcher 
+from gramps.gen.lib.nameorigintype import NameOriginType
 
-from .dbconst import *
-from ..utils.callback import Callback
-from ..utils.cast import conv_dbstr_to_unicode
-from . import (BsddbBaseCursor, DbReadBase)
-from ..utils.id import create_id
-from ..errors import DbError
-from ..constfunc import handle2internal, get_env_var
-from ..const import GRAMPS_LOCALE as glocale
+from gramps.gen.utils.callback import Callback
+from gramps.gen.utils.cast import conv_dbstr_to_unicode
+from . import BsddbBaseCursor
+from gramps.gen.db.base import DbReadBase
+from gramps.gen.utils.id import create_id
+from gramps.gen.errors import DbError
+from gramps.gen.constfunc import handle2internal, get_env_var
+from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
 
+from gramps.gen.db.dbconst import *
+
 LOG = logging.getLogger(DBLOGNAME)
 LOG = logging.getLogger(".citation")
 #-------------------------------------------------------------------------
@@ -84,7 +86,6 @@ LOG = logging.getLogger(".citation")
 # constants
 #
 #-------------------------------------------------------------------------
-from .dbconst import *
 
 _SIGBASE = ('person', 'family', 'source', 'citation', 
             'event',  'media', 'place', 'repository',
@@ -1975,3 +1976,16 @@ class DbBsddbRead(DbReadBase, Callback):
             self.__log_error()
             name = None
         return name
+
+    def get_summary(self):
+        """
+        Returns dictionary of summary item.
+        Should include, if possible:
+
+        _("Number of people")
+        _("Version")
+        _("Schema version")
+        """
+        return {
+            _("Number of people"): self.get_number_of_people(),
+        }
diff --git a/gramps/plugins/database/bsddb_support/summary.py b/gramps/plugins/database/bsddb_support/summary.py
new file mode 100644
index 000000000..ccc94d2dc
--- /dev/null
+++ b/gramps/plugins/database/bsddb_support/summary.py
@@ -0,0 +1,62 @@
+## Removed from clidbman.py
+## specific to bsddb
+
+from bsddb3 import dbshelve, db
+import os
+
+from gramps.gen.db import META, PERSON_TBL
+from  gramps.gen.db.dbconst import BDBVERSFN
+
+def get_dbdir_summary(dirpath, name):
+    """
+    Returns (people_count, bsddb_version, schema_version) of
+    current DB.
+    Returns ("Unknown", "Unknown", "Unknown") if invalid DB or other error.
+    """
+
+    bdbversion_file = os.path.join(dirpath, BDBVERSFN)
+    if os.path.isfile(bdbversion_file):
+        vers_file = open(bdbversion_file)
+        bsddb_version = vers_file.readline().strip()
+    else:
+        return "Unknown", "Unknown", "Unknown"
+        
+    current_bsddb_version = str(db.version())
+    if bsddb_version != current_bsddb_version:
+        return "Unknown", bsddb_version, "Unknown"
+        
+    env = db.DBEnv()
+    flags = db.DB_CREATE | db.DB_PRIVATE |\
+        db.DB_INIT_MPOOL |\
+        db.DB_INIT_LOG | db.DB_INIT_TXN
+    try:
+        env.open(dirpath, flags)
+    except Exception as msg:
+        LOG.warning("Error opening db environment for '%s': %s" %
+                    (name, str(msg)))
+        try:
+            env.close()
+        except Exception as msg:
+            LOG.warning("Error closing db environment for '%s': %s" %
+                    (name, str(msg)))
+        return "Unknown", bsddb_version, "Unknown"
+    dbmap1 = dbshelve.DBShelf(env)
+    fname = os.path.join(dirpath, META + ".db")
+    try:
+        dbmap1.open(fname, META, db.DB_HASH, db.DB_RDONLY)
+    except:
+        env.close()
+        return "Unknown", bsddb_version, "Unknown"
+    schema_version = dbmap1.get(b'version', default=None)
+    dbmap1.close()
+    dbmap2 = dbshelve.DBShelf(env)
+    fname = os.path.join(dirpath, PERSON_TBL + ".db")
+    try:
+        dbmap2.open(fname, PERSON_TBL, db.DB_HASH, db.DB_RDONLY)
+    except:
+        env.close()
+        return "Unknown", bsddb_version, schema_version
+    count = len(dbmap2)
+    dbmap2.close()
+    env.close()
+    return (count, bsddb_version, schema_version)
diff --git a/gramps/plugins/database/bsddb_support/test/__init__.py b/gramps/plugins/database/bsddb_support/test/__init__.py
new file mode 100644
index 000000000..e69de29bb
diff --git a/gramps/gen/db/test/cursor_test.py b/gramps/plugins/database/bsddb_support/test/cursor_test.py
similarity index 100%
rename from gramps/gen/db/test/cursor_test.py
rename to gramps/plugins/database/bsddb_support/test/cursor_test.py
diff --git a/gramps/gen/db/test/db_test.py b/gramps/plugins/database/bsddb_support/test/db_test.py
similarity index 100%
rename from gramps/gen/db/test/db_test.py
rename to gramps/plugins/database/bsddb_support/test/db_test.py
diff --git a/gramps/gen/db/test/grampsdbtestbase.py b/gramps/plugins/database/bsddb_support/test/grampsdbtestbase.py
similarity index 100%
rename from gramps/gen/db/test/grampsdbtestbase.py
rename to gramps/plugins/database/bsddb_support/test/grampsdbtestbase.py
diff --git a/gramps/gen/db/test/reference_map_test.py b/gramps/plugins/database/bsddb_support/test/reference_map_test.py
similarity index 100%
rename from gramps/gen/db/test/reference_map_test.py
rename to gramps/plugins/database/bsddb_support/test/reference_map_test.py
diff --git a/gramps/plugins/database/bsddb_support/undoredo.py b/gramps/plugins/database/bsddb_support/undoredo.py
new file mode 100644
index 000000000..da6b3368e
--- /dev/null
+++ b/gramps/plugins/database/bsddb_support/undoredo.py
@@ -0,0 +1,516 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2004-2006 Donald N. Allingham
+# Copyright (C) 2011       Tim G L Lyons
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+"""
+Exports the DbUndo class for managing Gramps transactions
+undos and redos.
+"""
+
+#-------------------------------------------------------------------------
+#
+# Standard python modules
+#
+#-------------------------------------------------------------------------
+import time, os
+import pickle
+from collections import deque
+
+try:
+    from bsddb3 import db
+except:
+    # FIXME: make this more abstract to deal with other backends
+    class db:
+        DBRunRecoveryError = 0
+        DBAccessError = 0
+        DBPageNotFoundError = 0
+        DBInvalidArgError = 0
+        
+from gramps.gen.const import GRAMPS_LOCALE as glocale
+_ = glocale.translation.gettext
+
+#-------------------------------------------------------------------------
+#
+# Gramps modules
+#
+#-------------------------------------------------------------------------
+from gramps.gen.constfunc import conv_to_unicode, handle2internal, win
+from gramps.gen.db.dbconst import *
+from . import BSDDBTxn
+from gramps.gen.errors import DbError
+
+#-------------------------------------------------------------------------
+#
+# Local Constants
+#
+#-------------------------------------------------------------------------
+DBERRS      = (db.DBRunRecoveryError, db.DBAccessError, 
+               db.DBPageNotFoundError, db.DBInvalidArgError)
+               
+_SIGBASE = ('person', 'family', 'source', 'event', 'media',
+            'place', 'repository', 'reference', 'note', 'tag', 'citation')
+
+#-------------------------------------------------------------------------
+#
+# DbUndo class
+#
+#-------------------------------------------------------------------------            
+class DbUndo(object):
+    """
+    Base class for the Gramps undo/redo manager.  Needs to be subclassed
+    for use with a real backend.
+    """
+
+    __slots__ = ('undodb', 'db', 'mapbase', 'undo_history_timestamp',
+                 'txn', 'undoq', 'redoq')
+
+    def __init__(self, grampsdb):
+        """
+        Class constructor. Set up main instance variables
+        """
+        self.db = grampsdb
+        self.undoq = deque()
+        self.redoq = deque()
+        self.undo_history_timestamp = time.time()
+        self.txn = None
+        # N.B. the databases have to be in the same order as the numbers in
+        # xxx_KEY in gen/db/dbconst.py
+        self.mapbase = (
+                        self.db.person_map,
+                        self.db.family_map,
+                        self.db.source_map,
+                        self.db.event_map,
+                        self.db.media_map,
+                        self.db.place_map,
+                        self.db.repository_map,
+                        self.db.reference_map,
+                        self.db.note_map,
+                        self.db.tag_map,
+                        self.db.citation_map,
+                        )
+
+    def clear(self):
+        """
+        Clear the undo/redo list (but not the backing storage)
+        """
+        self.undoq.clear()
+        self.redoq.clear()
+        self.undo_history_timestamp = time.time()
+        self.txn = None
+
+    def __enter__(self, value):
+        """
+        Context manager method to establish the context
+        """
+        self.open(value)
+        return self
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
+        """
+        Context manager method to finish the context
+        """
+        if exc_type is None:
+            self.close()
+        return exc_type is None
+
+    def open(self, value):
+        """
+        Open the backing storage.  Needs to be overridden in the derived
+        class.
+        """
+        raise NotImplementedError
+
+    def close(self):
+        """
+        Close the backing storage.  Needs to be overridden in the derived
+        class.
+        """        
+        raise NotImplementedError
+
+    def append(self, value):
+        """
+        Add a new entry on the end.  Needs to be overridden in the derived
+        class.
+        """        
+        raise NotImplementedError
+
+    def __getitem__(self, index):
+        """
+        Returns an entry by index number.  Needs to be overridden in the
+        derived class.
+        """        
+        raise NotImplementedError
+
+    def __setitem__(self, index, value):
+        """
+        Set an entry to a value.  Needs to be overridden in the derived class.
+        """           
+        raise NotImplementedError
+
+    def __len__(self):
+        """
+        Returns the number of entries.  Needs to be overridden in the derived
+        class.
+        """         
+        raise NotImplementedError
+
+    def commit(self, txn, msg):
+        """
+        Commit the transaction to the undo/redo database.  "txn" should be
+        an instance of Gramps transaction class
+        """
+        txn.set_description(msg)
+        txn.timestamp = time.time()
+        self.undoq.append(txn)
+
+    def undo(self, update_history=True):
+        """
+        Undo a previously committed transaction
+        """
+        if self.db.readonly or self.undo_count == 0:
+            return False
+        return self.__undo(update_history)
+
+    def redo(self, update_history=True):
+        """
+        Redo a previously committed, then undone, transaction
+        """
+        if self.db.readonly or self.redo_count == 0:
+            return False
+        return self.__redo(update_history)
+
+    def undoredo(func):
+        """
+        Decorator function to wrap undo and redo operations within a bsddb
+        transaction.  It also catches bsddb errors and raises an exception
+        as appropriate
+        """
+        def try_(self, *args, **kwargs):
+            try:
+                with BSDDBTxn(self.db.env) as txn:
+                    self.txn = self.db.txn = txn.txn
+                    status = func(self, *args, **kwargs)
+                    if not status:
+                        txn.abort()
+                    self.db.txn = None
+                    return status
+
+            except DBERRS as msg:
+                self.db._log_error()
+                raise DbError(msg)
+
+        return try_        
+
+    @undoredo
+    def __undo(self, update_history=True):
+        """
+        Access the last committed transaction, and revert the data to the 
+        state before the transaction was committed.
+        """
+        txn = self.undoq.pop()
+        self.redoq.append(txn)
+        transaction = txn
+        db = self.db
+        subitems = transaction.get_recnos(reverse=True)
+
+        # Process all records in the transaction
+        for record_id in subitems:
+            (key, trans_type, handle, old_data, new_data) = \
+                    pickle.loads(self.undodb[record_id])
+
+            if key == REFERENCE_KEY:
+                self.undo_reference(old_data, handle, self.mapbase[key])
+            else:
+                self.undo_data(old_data, handle, self.mapbase[key],
+                                db.emit, _SIGBASE[key])
+        # Notify listeners
+        if db.undo_callback:
+            if self.undo_count > 0:
+                db.undo_callback(_("_Undo %s")
+                                   % self.undoq[-1].get_description())
+            else:
+                db.undo_callback(None)    
+
+        if db.redo_callback:
+            db.redo_callback(_("_Redo %s")
+                                   % transaction.get_description())
+
+        if update_history and db.undo_history_callback:
+            db.undo_history_callback()
+        return True
+
+    @undoredo
+    def __redo(self, db=None, update_history=True):
+        """
+        Access the last undone transaction, and revert the data to the state 
+        before the transaction was undone.
+        """
+        txn = self.redoq.pop()
+        self.undoq.append(txn)
+        transaction = txn
+        db = self.db
+        subitems = transaction.get_recnos()
+
+        # Process all records in the transaction
+        for record_id in subitems:
+            (key, trans_type, handle, old_data, new_data) = \
+                pickle.loads(self.undodb[record_id])
+
+            if key == REFERENCE_KEY:
+                self.undo_reference(new_data, handle, self.mapbase[key])
+            else:
+                self.undo_data(new_data, handle, self.mapbase[key],
+                                    db.emit, _SIGBASE[key])
+        # Notify listeners
+        if db.undo_callback:
+            db.undo_callback(_("_Undo %s")
+                                   % transaction.get_description())
+
+        if db.redo_callback:
+            if self.redo_count > 1:
+                new_transaction = self.redoq[-2]
+                db.redo_callback(_("_Redo %s")
+                                   % new_transaction.get_description())
+            else:
+                db.redo_callback(None)       
+
+        if update_history and db.undo_history_callback:
+            db.undo_history_callback()
+        return True        
+
+    def undo_reference(self, data, handle, db_map):
+        """
+        Helper method to undo a reference map entry
+        """
+        try:
+            if data is None:
+                db_map.delete(handle, txn=self.txn)
+            else:
+                db_map.put(handle, data, txn=self.txn)
+
+        except DBERRS as msg:
+            self.db._log_error()
+            raise DbError(msg)
+
+    def undo_data(self, data, handle, db_map, emit, signal_root):
+        """
+        Helper method to undo/redo the changes made
+        """
+        try:
+            if data is None:
+                emit(signal_root + '-delete', ([handle2internal(handle)],))
+                db_map.delete(handle, txn=self.txn)
+            else:
+                ex_data = db_map.get(handle, txn=self.txn)
+                if ex_data:
+                    signal = signal_root + '-update'
+                else:
+                    signal = signal_root + '-add'
+                db_map.put(handle, data, txn=self.txn)
+                emit(signal, ([handle2internal(handle)],))
+
+        except DBERRS as msg:
+            self.db._log_error()
+            raise DbError(msg)
+
+    undo_count = property(lambda self:len(self.undoq))
+    redo_count = property(lambda self:len(self.redoq))
+
+class DbUndoList(DbUndo):
+    """
+    Implementation of the Gramps undo database using a Python list
+    """
+    def __init__(self, grampsdb):
+        """
+        Class constructor
+        """
+        super(DbUndoList, self).__init__(grampsdb)
+        self.undodb = []
+
+    def open(self):
+        """
+        A list does not need to be opened
+        """
+        pass
+
+    def close(self):
+        """
+        Close the list by resetting it to empty
+        """
+        self.undodb = []
+        self.clear()
+
+    def append(self, value):
+        """
+        Add an entry on the end of the list
+        """
+        self.undodb.append(value)
+        return len(self.undodb)-1
+
+    def __getitem__(self, index):
+        """
+        Return an item at the specified index
+        """
+        return self.undodb[index]
+
+    def __setitem__(self, index, value):
+        """
+        Set an item at the speficied index to the given value
+        """
+        self.undodb[index] = value
+
+    def __iter__(self):
+        """
+        Iterator
+        """
+        for item in self.undodb:
+            yield item
+
+    def __len__(self):
+        """
+        Return number of entries in the list
+        """
+        return len(self.undodb)
+
+class DbUndoBSDDB(DbUndo):
+    """
+    Class constructor for Gramps undo/redo database using a bsddb recno
+    database as the backing store.
+    """
+
+    def __init__(self, grampsdb, path):
+        """
+        Class constructor
+        """
+        super(DbUndoBSDDB, self).__init__(grampsdb)
+        self.undodb = db.DB()
+        self.path = path
+
+    def open(self):
+        """
+        Open the undo/redo database
+        """
+        path = self.path
+        self.undodb.open(path, db.DB_RECNO, db.DB_CREATE)
+        
+    def close(self):
+        """
+        Close the undo/redo database
+        """
+        self.undodb.close()
+        self.undodb = None
+        self.mapbase = None
+        self.db = None
+        
+        try:
+            os.remove(self.path)
+        except OSError:
+            pass
+        self.clear()      
+
+    def append(self, value):
+        """
+        Add an entry on the end of the database
+        """
+        return self.undodb.append(value)
+
+    def __len__(self):
+        """
+        Returns the number of entries in the database
+        """
+        x = self.undodb.stat()['nkeys']
+        y = len(self.undodb)
+        assert x == y
+        return x
+
+    def __getitem__(self, index):
+        """
+        Returns the entry stored at the specified index
+        """
+        return self.undodb.get(index)
+
+    def __setitem__(self, index, value):
+        """
+        Sets the entry stored at the specified index to the value given.
+        """
+        self.undodb.put(index, value)
+
+    def __iter__(self):
+        """
+        Iterator
+        """
+        cursor = self.undodb.cursor()
+        data = cursor.first()
+        while data:
+            yield data
+            data = next(cursor)
+
+def testundo():
+    class T:
+        def __init__(self):
+            self.msg = ''
+            self.timetstamp = 0
+        def set_description(self, msg):
+            self.msg = msg
+
+    class D:
+        def __init__(self):
+            self.person_map = {}
+            self.family_map = {}
+            self.source_map = {}
+            self.event_map  = {}
+            self.media_map  = {}
+            self.place_map  = {}
+            self.note_map   = {}
+            self.tag_map   = {}
+            self.repository_map = {}
+            self.reference_map  = {}
+
+    print("list tests")
+    undo = DbUndoList(D())
+    print(undo.append('foo'))
+    print(undo.append('bar'))
+    print(undo[0])
+    undo[0] = 'foobar'
+    print(undo[0])
+    print("len", len(undo))
+    print("iter")
+    for data in undo:
+        print(data)
+    print()
+    print("bsddb tests")
+    undo = DbUndoBSDDB(D(), '/tmp/testundo')
+    undo.open()
+    print(undo.append('foo'))
+    print(undo.append('fo2'))
+    print(undo.append('fo3'))
+    print(undo[1])
+    undo[1] = 'bar'
+    print(undo[1])
+    for data in undo:
+        print(data)
+    print("len", len(undo))
+
+    print("test commit")
+    undo.commit(T(), msg="test commit")
+    undo.close()
+
+if __name__ == '__main__':
+    testundo()
diff --git a/gramps/gen/db/upgrade.py b/gramps/plugins/database/bsddb_support/upgrade.py
similarity index 98%
rename from gramps/gen/db/upgrade.py
rename to gramps/plugins/database/bsddb_support/upgrade.py
index c720a9547..1f1cd6719 100644
--- a/gramps/gen/db/upgrade.py
+++ b/gramps/plugins/database/bsddb_support/upgrade.py
@@ -39,23 +39,24 @@ from bsddb3 import db
 # Gramps modules
 #
 #-------------------------------------------------------------------------
-from ..const import GRAMPS_LOCALE as glocale
+from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
-from ..constfunc import handle2internal
-from ..lib.markertype import MarkerType
-from ..lib.nameorigintype import NameOriginType
-from ..lib.place import Place
-from ..lib.placeref import PlaceRef
-from ..lib.placetype import PlaceType
-from ..lib.placename import PlaceName
-from ..lib.eventtype import EventType
-from ..lib.tag import Tag
-from ..utils.file import create_checksum
-from ..utils.id import create_id
+from gramps.gen.constfunc import handle2internal
+from gramps.gen.lib.markertype import MarkerType
+from gramps.gen.lib.nameorigintype import NameOriginType
+from gramps.gen.lib.place import Place
+from gramps.gen.lib.placeref import PlaceRef
+from gramps.gen.lib.placetype import PlaceType
+from gramps.gen.lib.placename import PlaceName
+from gramps.gen.lib.eventtype import EventType
+from gramps.gen.lib.tag import Tag
+from gramps.gen.utils.file import create_checksum
+from gramps.gen.utils.id import create_id
 from . import BSDDBTxn
 from .write import _mkname, SURNAMES
-from .dbconst import (PERSON_KEY, FAMILY_KEY, EVENT_KEY, 
-                      MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, SOURCE_KEY)
+from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, EVENT_KEY, 
+                                   MEDIA_KEY, PLACE_KEY, REPOSITORY_KEY, 
+                                   SOURCE_KEY)
 from gramps.gui.dialog import (InfoDialog)
 
 LOG = logging.getLogger(".upgrade")
@@ -359,7 +360,7 @@ def upgrade_datamap_17(datamap):
     """
     new_srcattr_list = []
     private = False
-    from ..lib.srcattrtype import SrcAttributeType
+    from gramps.gen.lib.srcattrtype import SrcAttributeType
     for (key, value) in datamap.items():
         the_type = SrcAttributeType(key).serialize()
         new_srcattr_list.append((private, the_type, value))
diff --git a/gramps/gen/db/write.py b/gramps/plugins/database/bsddb_support/write.py
similarity index 93%
rename from gramps/gen/db/write.py
rename to gramps/plugins/database/bsddb_support/write.py
index 607735ec8..2eb665ac5 100644
--- a/gramps/gen/db/write.py
+++ b/gramps/plugins/database/bsddb_support/write.py
@@ -40,49 +40,46 @@ from functools import wraps
 import logging
 from sys import maxsize, getfilesystemencoding, version_info
 
-try:
-    from bsddb3 import dbshelve, db
-except:
-    # FIXME: make this more abstract to deal with other backends
-    class db:
-        DB_HASH = 0
-        DBRunRecoveryError = 0
-        DBAccessError = 0
-        DBPageNotFoundError = 0
-        DBInvalidArgError = 0
+from bsddb3 import dbshelve, db
+from bsddb3.db import DB_CREATE, DB_AUTO_COMMIT, DB_DUP, DB_DUPSORT, DB_RDONLY
+
+DBFLAGS_O = DB_CREATE | DB_AUTO_COMMIT  # Default flags for database open
+DBFLAGS_R = DB_RDONLY                   # Flags to open a database read-only
+DBFLAGS_D = DB_DUP | DB_DUPSORT         # Default flags for duplicate keys
 
 #-------------------------------------------------------------------------
 #
 # Gramps modules
 #
 #-------------------------------------------------------------------------
-from ..lib.person import Person
-from ..lib.family import Family
-from ..lib.src import Source
-from ..lib.citation import Citation
-from ..lib.event import Event
-from ..lib.place import Place
-from ..lib.repo import Repository
-from ..lib.mediaobj import MediaObject
-from ..lib.note import Note
-from ..lib.tag import Tag
-from ..lib.genderstats import GenderStats
-from ..lib.researcher import Researcher 
+from gramps.gen.lib.person import Person
+from gramps.gen.lib.family import Family
+from gramps.gen.lib.src import Source
+from gramps.gen.lib.citation import Citation
+from gramps.gen.lib.event import Event
+from gramps.gen.lib.place import Place
+from gramps.gen.lib.repo import Repository
+from gramps.gen.lib.mediaobj import MediaObject
+from gramps.gen.lib.note import Note
+from gramps.gen.lib.tag import Tag
+from gramps.gen.lib.genderstats import GenderStats
+from gramps.gen.lib.researcher import Researcher 
 
 from . import (DbBsddbRead, DbWriteBase, BSDDBTxn, 
                     DbTxn, BsddbBaseCursor, BsddbDowngradeError, DbVersionError,
                     DbEnvironmentError, DbUpgradeRequiredError, find_surname,
-                    find_byte_surname, find_surname_name, DbUndoBSDDB as DbUndo,
-                    exceptions)
-from .dbconst import *
-from ..utils.callback import Callback
-from ..utils.cast import conv_dbstr_to_unicode
-from ..utils.id import create_id
-from ..updatecallback import UpdateCallback
-from ..errors import DbError
-from ..constfunc import (win, conv_to_unicode, handle2internal,
+                    find_byte_surname, find_surname_name, DbUndoBSDDB as DbUndo)
+
+from gramps.gen.db import exceptions
+from gramps.gen.db.dbconst import *
+from gramps.gen.utils.callback import Callback
+from gramps.gen.utils.cast import conv_dbstr_to_unicode
+from gramps.gen.utils.id import create_id
+from gramps.gen.updatecallback import UpdateCallback
+from gramps.gen.errors import DbError
+from gramps.gen.constfunc import (win, conv_to_unicode, handle2internal,
                          get_env_var)
-from ..const import HOME_DIR, GRAMPS_LOCALE as glocale
+from gramps.gen.const import HOME_DIR, GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
 
 _LOG = logging.getLogger(DBLOGNAME)
@@ -133,39 +130,6 @@ DBERRS      = (db.DBRunRecoveryError, db.DBAccessError,
 # these maps or modifying the values of the keys will break
 # existing databases.
 
-CLASS_TO_KEY_MAP = {Person.__name__: PERSON_KEY, 
-                    Family.__name__: FAMILY_KEY, 
-                    Source.__name__: SOURCE_KEY, 
-                    Citation.__name__: CITATION_KEY, 
-                    Event.__name__: EVENT_KEY, 
-                    MediaObject.__name__: MEDIA_KEY, 
-                    Place.__name__: PLACE_KEY, 
-                    Repository.__name__:REPOSITORY_KEY,
-                    Note.__name__: NOTE_KEY,
-                    Tag.__name__: TAG_KEY}
-
-KEY_TO_CLASS_MAP = {PERSON_KEY: Person.__name__, 
-                    FAMILY_KEY: Family.__name__, 
-                    SOURCE_KEY: Source.__name__, 
-                    CITATION_KEY: Citation.__name__, 
-                    EVENT_KEY: Event.__name__, 
-                    MEDIA_KEY: MediaObject.__name__, 
-                    PLACE_KEY: Place.__name__, 
-                    REPOSITORY_KEY: Repository.__name__,
-                    NOTE_KEY: Note.__name__,
-                    TAG_KEY: Tag.__name__}
-
-KEY_TO_NAME_MAP = {PERSON_KEY: 'person',
-                   FAMILY_KEY: 'family',
-                   EVENT_KEY: 'event',
-                   SOURCE_KEY: 'source',
-                   CITATION_KEY: 'citation',
-                   PLACE_KEY: 'place',
-                   MEDIA_KEY: 'media',
-                   REPOSITORY_KEY: 'repository',
-                   #REFERENCE_KEY: 'reference',
-                   NOTE_KEY: 'note',
-                   TAG_KEY: 'tag'}
 #-------------------------------------------------------------------------
 #
 # Helper functions
@@ -689,7 +653,7 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
         return False
 
     @catch_db_error
-    def load(self, name, callback, mode=DBMODE_W, force_schema_upgrade=False,
+    def load(self, name, callback=None, mode=DBMODE_W, force_schema_upgrade=False,
              force_bsddb_upgrade=False, force_bsddb_downgrade=False,
              force_python_upgrade=False):
 
@@ -2433,6 +2397,11 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
             version = str(_DBVERSION)
             version_file.write(version)
 
+        versionpath = os.path.join(name, str(DBBACKEND))
+        _LOG.debug("Write database backend file to 'bsddb'")
+        with open(versionpath, "w") as version_file:
+            version_file.write("bsddb")
+
         self.metadata.close()
         self.env.close()
   
@@ -2449,6 +2418,180 @@ class DbBsddb(DbBsddbRead, DbWriteBase, UpdateCallback):
         """
         return DbTxn
 
+    def backup(self):
+        """
+        Exports the database to a set of backup files. These files consist
+        of the pickled database tables, one file for each table.
+        
+        The heavy lifting is done by the private :py:func:`__do__export` function.
+        The purpose of this function is to catch any exceptions that occur.
+        
+        :param database: database instance to backup
+        :type database: DbDir
+        """
+        try:
+            do_export(self)
+        except (OSError, IOError) as msg:
+            raise DbException(str(msg))
+
+    def restore(self):
+        """
+        Restores the database to a set of backup files. These files consist
+        of the pickled database tables, one file for each table.
+        
+        The heavy lifting is done by the private :py:func:`__do__restore` function.
+        The purpose of this function is to catch any exceptions that occur.
+        
+        :param database: database instance to restore
+        :type database: DbDir
+        """
+        try:
+            do_restore(self)
+        except (OSError, IOError) as msg:
+            raise DbException(str(msg))
+
+    def get_summary(self):
+        """
+        Returns dictionary of summary item.
+        Should include, if possible:
+
+        _("Number of people")
+        _("Version")
+        _("Schema version")
+        """
+        schema_version = self.metadata.get(b'version', default=None)
+        bdbversion_file = os.path.join(self.path, BDBVERSFN)
+        if os.path.isfile(bdbversion_file):
+            vers_file = open(bdbversion_file)
+            bsddb_version = vers_file.readline().strip()
+        else:
+            bsddb_version = _("Unknown")
+        return {
+            _("Number of people"): self.get_number_of_people(),
+            _("Schema version"): schema_version,
+            _("Version"): bsddb_version,
+        }
+
+    def prepare_import(self):
+        """
+        Initialization before imports
+        """
+        pass
+
+    def commit_import(self):
+        """
+        Post process after imports
+        """
+        pass
+
+def mk_backup_name(database, base):
+    """
+    Return the backup name of the database table
+
+    :param database: database instance 
+    :type database: DbDir
+    :param base: base name of the table
+    :type base: str
+    """
+    return os.path.join(database.get_save_path(), base + ".gbkp")
+
+def mk_tmp_name(database, base):
+    """
+    Return the temporary backup name of the database table
+
+    :param database: database instance 
+    :type database: DbDir
+    :param base: base name of the table
+    :type base: str
+    """
+    return os.path.join(database.get_save_path(), base + ".gbkp.new")
+
+def do_export(database):
+    """
+    Loop through each table of the database, saving the pickled data
+    a file.
+
+    :param database: database instance to backup
+    :type database: DbDir
+    """
+    try:
+        for (base, tbl) in build_tbl_map(database):
+            backup_name = mk_tmp_name(database, base)
+            backup_table = open(backup_name, 'wb')
+    
+            cursor = tbl.cursor()
+            data = cursor.first()
+            while data:
+                pickle.dump(data, backup_table, 2)
+                data = cursor.next()
+            cursor.close()
+            backup_table.close()
+    except (IOError,OSError):
+        return
+
+    for (base, tbl) in build_tbl_map(database):
+        new_name = mk_backup_name(database, base)
+        old_name = mk_tmp_name(database, base)
+        if os.path.isfile(new_name):
+            os.unlink(new_name)
+        os.rename(old_name, new_name)
+
+def do_restore(database):
+    """
+    Loop through each table of the database, restoring the pickled data
+    to the appropriate database file.
+
+    :param database: database instance to backup
+    :type database: DbDir
+    """
+    for (base, tbl) in build_tbl_map(database):
+        backup_name = mk_backup_name(database, base)
+        backup_table = open(backup_name, 'rb')
+        load_tbl_txn(database, backup_table, tbl)
+
+    database.rebuild_secondary()
+
+def load_tbl_txn(database, backup_table, tbl):
+    """
+    Return the temporary backup name of the database table
+
+    :param database: database instance 
+    :type database: DbDir
+    :param backup_table: file containing the backup data
+    :type backup_table: file
+    :param tbl: Berkeley db database table
+    :type tbl: Berkeley db database table
+    """
+    try:
+        while True:
+            data = pickle.load(backup_table)
+            txn = database.env.txn_begin()
+            tbl.put(data[0], data[1], txn=txn)
+            txn.commit()
+    except EOFError:
+        backup_table.close()
+
+def build_tbl_map(database):
+    """
+    Builds a table map of names to database tables.
+
+    :param database: database instance to backup
+    :type database: DbDir
+    """
+    return [
+        ( PERSON_TBL,  database.person_map.db),
+        ( FAMILY_TBL,  database.family_map.db),
+        ( PLACES_TBL,  database.place_map.db),
+        ( SOURCES_TBL, database.source_map.db),
+        ( CITATIONS_TBL, database.citation_map.db),
+        ( REPO_TBL,    database.repository_map.db),
+        ( NOTE_TBL,    database.note_map.db),
+        ( MEDIA_TBL,   database.media_map.db),
+        ( EVENTS_TBL,  database.event_map.db),
+        ( TAG_TBL,     database.tag_map.db),
+        ( META,        database.metadata.db),
+        ]
+
 def _mkname(path, name):
     return os.path.join(path, name + DBEXT)
 
diff --git a/gramps/plugins/database/dictionarydb.gpr.py b/gramps/plugins/database/dictionarydb.gpr.py
new file mode 100644
index 000000000..d035de58a
--- /dev/null
+++ b/gramps/plugins/database/dictionarydb.gpr.py
@@ -0,0 +1,31 @@
+#
+# Gramps - a GTK+/GNOME based genealogy program
+#
+# Copyright (C) 2015 Douglas Blank <doug.blank@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+
+plg = newplugin()
+plg.id    = 'dictionarydb'
+plg.name  = _("Dictionary Database Backend")
+plg.name_accell  = _("Di_ctionary Database Backend")
+plg.description =  _("Dictionary (in-memory) Database Backend")
+plg.version = '1.0'
+plg.gramps_target_version = "5.0"
+plg.status = STABLE
+plg.fname = 'dictionarydb.py'
+plg.ptype = DATABASE
+plg.databaseclass = 'DictionaryDb'
diff --git a/gramps/gen/db/dictionary.py b/gramps/plugins/database/dictionarydb.py
similarity index 56%
rename from gramps/gen/db/dictionary.py
rename to gramps/plugins/database/dictionarydb.py
index 36a8cd5fa..3fe7e0da5 100644
--- a/gramps/gen/db/dictionary.py
+++ b/gramps/plugins/database/dictionarydb.py
@@ -21,68 +21,163 @@
 
 #------------------------------------------------------------------------
 #
-# Gramps Modules
+# Python Modules
 #
 #------------------------------------------------------------------------
 import pickle
 import base64
 import time
 import re
-from . import DbReadBase, DbWriteBase, DbTxn
-from . import (PERSON_KEY,
-               FAMILY_KEY,
-               CITATION_KEY,
-               SOURCE_KEY,
-               EVENT_KEY,
-               MEDIA_KEY,
-               PLACE_KEY,
-               REPOSITORY_KEY,
-               NOTE_KEY,
-               TAG_KEY)
+import os
+import logging
 
-from ..const import GRAMPS_LOCALE as glocale
+#------------------------------------------------------------------------
+#
+# Gramps Modules
+#
+#------------------------------------------------------------------------
+from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
-from ..errors import DbError
-from ..utils.id import create_id
-from ..lib.researcher import Researcher
-from ..lib.mediaobj import MediaObject
-from ..lib.person import Person
-from ..lib.family import Family
-from ..lib.src import Source
-from ..lib.citation import Citation
-from ..lib.event import Event
-from ..lib.place import Place
-from ..lib.repo import Repository
-from ..lib.note import Note
-from ..lib.tag import Tag
+from gramps.gen.db import (DbReadBase, DbWriteBase, DbTxn, 
+                           KEY_TO_NAME_MAP, KEY_TO_CLASS_MAP)
+from gramps.gen.db.undoredo import DbUndo
+from gramps.gen.db.dbconst import *
+from gramps.gen.utils.callback import Callback
+from gramps.gen.updatecallback import UpdateCallback
+from gramps.gen.db import (PERSON_KEY,
+                           FAMILY_KEY,
+                           CITATION_KEY,
+                           SOURCE_KEY,
+                           EVENT_KEY,
+                           MEDIA_KEY,
+                           PLACE_KEY,
+                           REPOSITORY_KEY,
+                           NOTE_KEY,
+                           TAG_KEY)
 
-class Cursor(object):
+from gramps.gen.utils.id import create_id
+from gramps.gen.lib.researcher import Researcher
+from gramps.gen.lib.mediaobj import MediaObject
+from gramps.gen.lib.person import Person
+from gramps.gen.lib.family import Family
+from gramps.gen.lib.src import Source
+from gramps.gen.lib.citation import Citation
+from gramps.gen.lib.event import Event
+from gramps.gen.lib.place import Place
+from gramps.gen.lib.repo import Repository
+from gramps.gen.lib.note import Note
+from gramps.gen.lib.tag import Tag
+from gramps.gen.lib.genderstats import GenderStats
+
+_LOG = logging.getLogger(DBLOGNAME)
+
+def touch(fname, mode=0o666, dir_fd=None, **kwargs):
+    ## After http://stackoverflow.com/questions/1158076/implement-touch-using-python
+    flags = os.O_CREAT | os.O_APPEND
+    with os.fdopen(os.open(fname, flags=flags, mode=mode, dir_fd=dir_fd)) as f:
+        os.utime(f.fileno() if os.utime in os.supports_fd else fname,
+                 dir_fd=None if os.supports_fd else dir_fd, **kwargs)
+
+class Environment(object):
     """
-    Iterates through model returning (handle, raw_data)...
+    Implements the Environment API.
     """
-    def __init__(self, model, func):
-        self.model = model
-        self.func = func
+    def __init__(self, db):
+        self.db = db
+
+    def txn_begin(self):
+        return DictionaryTxn("DictionaryDb Transaction", self.db)
+
+class Table(object):
+    """
+    Implements Table interface.
+    """
+    def __init__(self, funcs):
+        self.funcs = funcs
+
+    def cursor(self):
+        """
+        Returns a Cursor for this Table.
+        """
+        return self.funcs["cursor_func"]()
+
+    def put(self, key, data, txn=None):
+        self.funcs["add_func"](data, txn)
+
+class Map(dict):
+    """
+    Implements the map API for person_map, etc.
+    
+    Takes a Table() as argument.
+    """
+    def __init__(self, tbl, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.db = tbl
+
+class MetaCursor(object):
+    def __init__(self):
+        pass
     def __enter__(self):
         return self
     def __iter__(self):
-        return self
+        return self.__next__()
     def __next__(self):
-        for handle in self.model.keys():
-            return (handle, self.func(handle))
-    def next(self):
-        for handle in self.model.keys():
-            return (handle, self.func(handle))
+        yield None
     def __exit__(self, *args, **kwargs):
         pass
+    def iter(self):
+        yield None
+    def first(self):
+        self._iter = self.__iter__()
+        return self.next()
+    def next(self):
+        try:
+            return next(self._iter)
+        except:
+            return None
     def close(self):
         pass
 
-class Bookmarks:
-    def get(self):
-        return [] # handles
-    def append(self, handle):
+class Cursor(object):
+    def __init__(self, map):
+        self.map = map
+        self._iter = self.__iter__()
+    def __enter__(self):
+        return self
+    def __iter__(self):
+        for item in self.map.keys():
+            yield (bytes(item, "utf-8"), self.map[item])
+    def __next__(self):
+        try:
+            return self._iter.__next__()
+        except StopIteration:
+            return None
+    def __exit__(self, *args, **kwargs):
         pass
+    def iter(self):
+        for item in self.map.keys():
+            yield (bytes(item, "utf-8"), self.map[item])
+    def first(self):
+        self._iter = self.__iter__()
+        try:
+            return next(self._iter)
+        except:
+            return
+    def next(self):
+        try:
+            return next(self._iter)
+        except:
+            return
+    def close(self):
+        pass
+
+class Bookmarks(object):
+    def __init__(self):
+        self.handles = []
+    def get(self):
+        return self.handles
+    def append(self, handle):
+        self.handles.append(handle)
 
 class DictionaryTxn(DbTxn):
     def __init__(self, message, db, batch=False):
@@ -102,13 +197,38 @@ class DictionaryTxn(DbTxn):
         """
         txn[handle] = new_data
 
-class DictionaryDb(DbWriteBase, DbReadBase):
+class DictionaryDb(DbWriteBase, DbReadBase, UpdateCallback, Callback):
     """
     A Gramps Database Backend. This replicates the grampsdb functions.
     """
-    def __init__(self, *args, **kwargs):
+    __signals__ = dict((obj+'-'+op, signal)
+                       for obj in
+                       ['person', 'family', 'event', 'place',
+                        'source', 'citation', 'media', 'note', 'repository', 'tag']
+                       for op, signal in zip(
+                               ['add',   'update', 'delete', 'rebuild'],
+                               [(list,), (list,),  (list,),   None]
+                       )
+                   )
+    
+    # 2. Signals for long operations
+    __signals__.update(('long-op-'+op, signal) for op, signal in zip(
+        ['start',  'heartbeat', 'end'],
+        [(object,), None,       None]
+        ))
+
+    # 3. Special signal for change in home person
+    __signals__['home-person-changed'] = None
+
+    # 4. Signal for change in person group name, parameters are
+    __signals__['person-groupname-rebuild'] = (str, str)
+
+    __callback_map = {}
+
+    def __init__(self, directory=None):
         DbReadBase.__init__(self)
         DbWriteBase.__init__(self)
+        Callback.__init__(self)
         self._tables['Person'].update(
             {
                 "handle_func": self.get_person_from_handle, 
@@ -118,6 +238,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_person_handles,
                 "add_func": self.add_person,
                 "commit_func": self.commit_person,
+                "iter_func": self.iter_people,
             })
         self._tables['Family'].update(
             {
@@ -128,6 +249,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_family_handles,
                 "add_func": self.add_family,
                 "commit_func": self.commit_family,
+                "iter_func": self.iter_families,
             })
         self._tables['Source'].update(
             {
@@ -138,6 +260,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_source_handles,
                 "add_func": self.add_source,
                 "commit_func": self.commit_source,
+                "iter_func": self.iter_sources,
                 })
         self._tables['Citation'].update(
             {
@@ -148,6 +271,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_citation_handles,
                 "add_func": self.add_citation,
                 "commit_func": self.commit_citation,
+                "iter_func": self.iter_citations,
             })
         self._tables['Event'].update(
             {
@@ -158,6 +282,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_event_handles,
                 "add_func": self.add_event,
                 "commit_func": self.commit_event,
+                "iter_func": self.iter_events,
             })
         self._tables['Media'].update(
             {
@@ -168,6 +293,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_media_object_handles,
                 "add_func": self.add_object,
                 "commit_func": self.commit_media_object,
+                "iter_func": self.iter_media_objects,
             })
         self._tables['Place'].update(
             {
@@ -178,6 +304,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_place_handles,
                 "add_func": self.add_place,
                 "commit_func": self.commit_place,
+                "iter_func": self.iter_places,
             })
         self._tables['Repository'].update(
             {
@@ -188,6 +315,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_repository_handles,
                 "add_func": self.add_repository,
                 "commit_func": self.commit_repository,
+                "iter_func": self.iter_repositories,
             })
         self._tables['Note'].update(
             {
@@ -198,6 +326,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_note_handles,
                 "add_func": self.add_note,
                 "commit_func": self.commit_note,
+                "iter_func": self.iter_notes,
             })
         self._tables['Tag'].update(
             {
@@ -208,6 +337,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 "handles_func": self.get_tag_handles,
                 "add_func": self.add_tag,
                 "commit_func": self.commit_tag,
+                "iter_func": self.iter_tags,
             })
         # skip GEDCOM cross-ref check for now:
         self.set_feature("skip-check-xref", True)
@@ -252,18 +382,27 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         self.omap_index = 0
         self.rmap_index = 0
         self.nmap_index = 0
-        self.env = None
-        self.person_map      = {}
-        self.family_map      = {}
-        self.place_map       = {}
-        self.citation_map    = {}
-        self.source_map      = {}
-        self.repository_map  = {}
-        self.note_map        = {}
-        self.media_map       = {}
-        self.event_map       = {}
-        self.tag_map         = {}
-        self.metadata   = {}
+        self.env = Environment(self)
+        self.person_map = Map(Table(self._tables["Person"]))
+        self.person_id_map = {}
+        self.family_map = Map(Table(self._tables["Family"]))
+        self.family_id_map = {}
+        self.place_map  = Map(Table(self._tables["Place"]))
+        self.place_id_map = {}
+        self.citation_map = Map(Table(self._tables["Citation"]))
+        self.citation_id_map = {}
+        self.source_map = Map(Table(self._tables["Source"]))
+        self.source_id_map = {}
+        self.repository_map  = Map(Table(self._tables["Repository"]))
+        self.repository_id_map = {}
+        self.note_map = Map(Table(self._tables["Note"]))
+        self.note_id_map = {}
+        self.media_map  = Map(Table(self._tables["Media"]))
+        self.media_id_map = {}
+        self.event_map  = Map(Table(self._tables["Event"]))
+        self.event_id_map = {}
+        self.tag_map  = Map(Table(self._tables["Tag"]))
+        self.metadata   = Map(Table({"cursor_func": lambda: MetaCursor()}))
         self.name_group = {}
         self.undo_callback = None
         self.redo_callback = None
@@ -271,6 +410,17 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         self.modified   = 0
         self.txn = DictionaryTxn("DbDictionary Transaction", self)
         self.transaction = None
+        self.undodb = DbUndo(self)
+        self.abort_possible = False
+        self._bm_changes = 0
+        self._directory = directory
+        self.full_name = None
+        self.path = None
+        self.brief_name = None
+        self.genderStats = GenderStats() # can pass in loaded stats as dict
+        self.owner = Researcher()
+        if directory:
+            self.load(directory)
 
     def version_supported(self):
         """Return True when the file has a supported version."""
@@ -287,15 +437,15 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         return None
 
     def transaction_commit(self, txn):
-        pass
-
-    def enable_signals(self):
+        ## FIXME
         pass
 
     def get_undodb(self):
+        ## FIXME
         return None
 
     def transaction_abort(self, txn):
+        ## FIXME
         pass
 
     @staticmethod
@@ -544,131 +694,196 @@ class DictionaryDb(DbWriteBase, DbReadBase):
     def get_name_group_mapping(self, key):
         return None
 
-    def get_researcher(self):
-        obj = Researcher()
-        return obj
-
     def get_person_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.person_map.keys()
+        ## Fixme: implement sort
+        return self.person_map.keys()
 
-    def get_family_handles(self):
+    def get_family_handles(self, sort_handles=False):
+        ## Fixme: implement sort
         return self.family_map.keys()
 
-    def get_event_handles(self):
+    def get_event_handles(self, sort_handles=False):
+        ## Fixme: implement sort
         return self.event_map.keys()
 
     def get_citation_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.citation_map.keys()
+        ## Fixme: implement sort
+        return self.citation_map.keys()
 
     def get_source_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.source_map.keys()
+        ## Fixme: implement sort
+        return self.source_map.keys()
 
     def get_place_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.place_map.keys()
+        ## Fixme: implement sort
+        return self.place_map.keys()
 
-    def get_repository_handles(self):
+    def get_repository_handles(self, sort_handles=False):
+        ## Fixme: implement sort
         return self.repository_map.keys()
 
     def get_media_object_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.media_map.keys()
+        ## Fixme: implement sort
+        return self.media_map.keys()
 
-    def get_note_handles(self):
+    def get_note_handles(self, sort_handles=False):
+        ## Fixme: implement sort
         return self.note_map.keys()
 
     def get_tag_handles(self, sort_handles=False):
-        if sort_handles:
-            raise Exception("Implement!")
-        else:
-            return self.tag_map.keys()
+        # FIXME: implement sort
+        return self.tag_map.keys()
 
     def get_event_from_handle(self, handle):
-        return self.event_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        event = None
+        if handle in self.event_map:
+            event = Event.create(self.event_map[handle])
+        return event
 
     def get_family_from_handle(self, handle): 
-        return self.family_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        family = None
+        if handle in self.family_map:
+            family = Family.create(self.family_map[handle])
+        return family
 
     def get_repository_from_handle(self, handle):
-        return self.repository_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        repository = None
+        if handle in self.repository_map:
+            repository = Repository.create(self.repository_map[handle])
+        return repository
 
     def get_person_from_handle(self, handle):
-        return self.person_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        person = None
+        if handle in self.person_map:
+            person = Person.create(self.person_map[handle])
+        return person
 
     def get_place_from_handle(self, handle):
-        place = self.place_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        place = None
+        if handle in self.place_map:
+            place = Place.create(self.place_map[handle])
         return place
 
     def get_citation_from_handle(self, handle):
-        citation = self.citation_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        citation = None
+        if handle in self.citation_map:
+            citation = Citation.create(self.citation_map[handle])
         return citation
 
     def get_source_from_handle(self, handle):
-        source = self.source_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        source = None
+        if handle in self.source_map:
+            source = Source.create(self.source_map[handle])
         return source
 
     def get_note_from_handle(self, handle):
-        note = self.note_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        note = None
+        if handle in self.note_map:
+            note = Note.create(self.note_map[handle])
         return note
 
     def get_object_from_handle(self, handle):
-        media = self.media_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        media = None
+        if handle in self.media_map:
+            media = MediaObject.create(self.media_map[handle])
         return media
 
     def get_tag_from_handle(self, handle):
-        tag = self.tag_map[handle]
+        if isinstance(handle, bytes):
+            handle = str(handle, "utf-8")
+        tag = None
+        if handle in self.tag_map:
+            tag = Tag.create(self.tag_map[handle])
         return tag
 
     def get_default_person(self):
-        return None
+        handle = self.get_default_handle()
+        if handle:
+            return self.get_person_from_handle(handle)
+        else:
+            return None
 
     def iter_people(self):
-        return (person for person in self.person_map.values())
+        return (Person.create(person) for person in self.person_map.values())
 
     def iter_person_handles(self):
         return (handle for handle in self.person_map.keys())
 
     def iter_families(self):
-        return (family for family in self.family_map.values())
+        return (Family.create(family) for family in self.family_map.values())
 
     def iter_family_handles(self):
         return (handle for handle in self.family_map.keys())
 
     def get_tag_from_name(self, name):
-        for tag in self.tag_map.values():
+        ## Slow, but typically not too many tags:
+        for data in self.tag_map.values():
+            tag = Tag.create(data)
             if tag.name == name:
                 return tag
         return None
 
-    def get_family_from_gramps_id(self, gramps_id):
-        for family in self.family_map.values():
-            if family.gramps_id == gramps_id:
-                return family
+    def get_person_from_gramps_id(self, gramps_id):
+        if gramps_id in self.person_id_map:
+            return Person.create(self.person_id_map[gramps_id])
         return None
 
-    def get_person_from_gramps_id(self, gramps_id):
-        for person in self.person_map.values():
-            if person.gramps_id == gramps_id:
-                return person
+    def get_family_from_gramps_id(self, gramps_id):
+        if gramps_id in self.family_id_map:
+            return Family.create(self.family_id_map[gramps_id])
+        return None
+
+    def get_citation_from_gramps_id(self, gramps_id):
+        if gramps_id in self.citation_id_map:
+            return Citation.create(self.citation_id_map[gramps_id])
+        return None
+
+    def get_source_from_gramps_id(self, gramps_id):
+        if gramps_id in self.source_id_map:
+            return Source.create(self.source_id_map[gramps_id])
+        return None
+
+    def get_event_from_gramps_id(self, gramps_id):
+        if gramps_id in self.event_id_map:
+            return Event.create(self.event_id_map[gramps_id])
+        return None
+
+    def get_media_from_gramps_id(self, gramps_id):
+        if gramps_id in self.media_id_map:
+            return MediaObject.create(self.media_id_map[gramps_id])
         return None
 
     def get_place_from_gramps_id(self, gramps_id):
-        for place in self.place_map.values():
-            if place.gramps_id == gramps_id:
-                return place
+        if gramps_id in self.place_id_map:
+            return Place.create(self.place_id_map[gramps_id])
+        return None
+
+    def get_repository_from_gramps_id(self, gramps_id):
+        if gramps_id in self.repository_id_map:
+            return Repository.create(self.repository_id_map[gramps_id])
+        return None
+
+    def get_note_from_gramps_id(self, gramps_id):
+        if gramps_id in self.note_id_map:
+            return Note.create(self.note_id_map[gramps_id])
         return None
 
     def get_number_of_people(self):
@@ -681,7 +896,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         return len(self.place_map)
 
     def get_number_of_tags(self):
-        return 0 # FIXME
+        return len(self.tag_map)
 
     def get_number_of_families(self):
         return len(self.family_map)
@@ -702,52 +917,48 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         return len(self.repository_map)
 
     def get_place_cursor(self):
-        return Cursor(self.place_map, self.get_raw_place_data)
+        return Cursor(self.place_map)
 
     def get_person_cursor(self):
-        return Cursor(self.person_map, self.get_raw_person_data)
+        return Cursor(self.person_map)
 
     def get_family_cursor(self):
-        return Cursor(self.family_map, self.get_raw_family_data)
+        return Cursor(self.family_map)
 
     def get_event_cursor(self):
-        return Cursor(self.event_map, self.get_raw_event_data)
+        return Cursor(self.event_map)
 
     def get_note_cursor(self):
-        return Cursor(self.note_map, self.get_raw_note_data)
+        return Cursor(self.note_map)
 
     def get_tag_cursor(self):
-        return Cursor(self.tag_map, self.get_raw_tag_data)
+        return Cursor(self.tag_map)
 
     def get_repository_cursor(self):
-        return Cursor(self.repository_map, self.get_raw_repository_data)
+        return Cursor(self.repository_map)
 
     def get_media_cursor(self):
-        return Cursor(self.media_map, self.get_raw_object_data)
+        return Cursor(self.media_map)
 
     def get_citation_cursor(self):
-        return Cursor(self.citation_map, self.get_raw_citation_data)
+        return Cursor(self.citation_map)
 
     def get_source_cursor(self):
-        return Cursor(self.source_map, self.get_raw_source_data)
+        return Cursor(self.source_map)
 
     def has_gramps_id(self, obj_key, gramps_id):
         key2table = {
-            PERSON_KEY:     self.person_map, 
-            FAMILY_KEY:     self.family_map, 
-            SOURCE_KEY:     self.source_map, 
-            CITATION_KEY:   self.citation_map, 
-            EVENT_KEY:      self.event_map, 
-            MEDIA_KEY:      self.media_map, 
-            PLACE_KEY:      self.place_map, 
-            REPOSITORY_KEY: self.repository_map, 
-            NOTE_KEY:       self.note_map, 
+            PERSON_KEY:     self.person_id_map, 
+            FAMILY_KEY:     self.family_id_map, 
+            SOURCE_KEY:     self.source_id_map, 
+            CITATION_KEY:   self.citation_id_map, 
+            EVENT_KEY:      self.event_id_map, 
+            MEDIA_KEY:      self.media_id_map, 
+            PLACE_KEY:      self.place_id_map, 
+            REPOSITORY_KEY: self.repository_id_map, 
+            NOTE_KEY:       self.note_id_map, 
             }
-        map = key2table[obj_key]
-        for item in map.values():
-            if item.gramps_id == gramps_id:
-                return True
-        return False
+        return gramps_id in key2table[obj_key]
 
     def has_person_handle(self, handle):
         return handle in self.person_map
@@ -788,54 +999,61 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         pass
 
     def set_default_person_handle(self, handle):
+        ## FIXME
         pass
 
     def set_mediapath(self, mediapath):
+        ## FIXME
         pass
 
     def get_raw_person_data(self, handle):
         if handle in self.person_map:
-            return self.person_map[handle].serialize()
+            return self.person_map[handle]
         return None
 
     def get_raw_family_data(self, handle):
         if handle in self.family_map:
-            return self.family_map[handle].serialize()
+            return self.family_map[handle]
         return None
 
     def get_raw_citation_data(self, handle):
         if handle in self.citation_map:
-            return self.citation_map[handle].serialize()
+            return self.citation_map[handle]
         return None
 
     def get_raw_source_data(self, handle):
         if handle in self.source_map:
-            return self.source_map[handle].serialize()
+            return self.source_map[handle]
         return None
 
     def get_raw_repository_data(self, handle):
         if handle in self.repository_map:
-            return self.repository_map[handle].serialize()
+            return self.repository_map[handle]
         return None
 
     def get_raw_note_data(self, handle):
         if handle in self.note_map:
-            return self.note_map[handle].serialize()
+            return self.note_map[handle]
         return None
 
     def get_raw_place_data(self, handle):
         if handle in self.place_map:
-            return self.place_map[handle].serialize()
+            return self.place_map[handle]
         return None
 
     def get_raw_object_data(self, handle):
         if handle in self.media_map:
-            return self.media_map[handle].serialize()
+            return self.media_map[handle]
         return None
 
     def get_raw_tag_data(self, handle):
         if handle in self.tag_map:
-            return self.tag_map[handle].serialize()
+            return self.tag_map[handle]
+        return None
+
+    def get_raw_event_data(self, handle):
+        if handle in self.event_map:
+            return self.event_map[handle]
         return None
 
     def add_person(self, person, trans, set_gid=True):
@@ -923,61 +1141,169 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         return obj.handle
 
     def commit_person(self, person, trans, change_time=None):
-        self.person_map[person.handle] = person
+        emit = None
+        if not trans.batch:
+            if person.handle in self.person_map:
+                emit = "person-update"
+            else:
+                emit = "person-add"
+        self.person_map[person.handle] = person.serialize()
+        self.person_id_map[person.gramps_id] = self.person_map[person.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([person.handle],))
 
     def commit_family(self, family, trans, change_time=None):
-        self.family_map[family.handle] = family
+        emit = None
+        if not trans.batch:
+            if family.handle in self.family_map:
+                emit = "family-update"
+            else:
+                emit = "family-add"
+        self.family_map[family.handle] = family.serialize()
+        self.family_id_map[family.gramps_id] = self.family_map[family.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([family.handle],))
 
     def commit_citation(self, citation, trans, change_time=None):
-        self.citation_map[citation.handle] = citation
+        emit = None
+        if not trans.batch:
+            if citation.handle in self.citation_map:
+                emit = "citation-update"
+            else:
+                emit = "citation-add"
+        self.citation_map[citation.handle] = citation.serialize()
+        self.citation_id_map[citation.gramps_id] = self.citation_map[citation.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([citation.handle],))
 
     def commit_source(self, source, trans, change_time=None):
-        self.source_map[source.handle] = source
+        emit = None
+        if not trans.batch:
+            if source.handle in self.source_map:
+                emit = "source-update"
+            else:
+                emit = "source-add"
+        self.source_map[source.handle] = source.serialize()
+        self.source_id_map[source.gramps_id] = self.source_map[source.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([source.handle],))
 
     def commit_repository(self, repository, trans, change_time=None):
-        self.repository_map[repository.handle] = repository
+        emit = None
+        if not trans.batch:
+            if repository.handle in self.repository_map:
+                emit = "repository-update"
+            else:
+                emit = "repository-add"
+        self.repository_map[repository.handle] = repository.serialize()
+        self.repository_id_map[repository.gramps_id] = self.repository_map[repository.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([repository.handle],))
 
     def commit_note(self, note, trans, change_time=None):
-        self.note_map[note.handle] = note
+        emit = None
+        if not trans.batch:
+            if note.handle in self.note_map:
+                emit = "note-update"
+            else:
+                emit = "note-add"
+        self.note_map[note.handle] = note.serialize()
+        self.note_id_map[note.gramps_id] = self.note_map[note.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([note.handle],))
 
     def commit_place(self, place, trans, change_time=None):
-        self.place_map[place.handle] = place
+        emit = None
+        if not trans.batch:
+            if place.handle in self.place_map:
+                emit = "place-update"
+            else:
+                emit = "place-add"
+        self.place_map[place.handle] = place.serialize()
+        self.place_id_map[place.gramps_id] = self.place_map[place.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([place.handle],))
 
     def commit_event(self, event, trans, change_time=None):
-        self.event_map[event.handle] = event
+        emit = None
+        if not trans.batch:
+            if event.handle in self.event_map:
+                emit = "event-update"
+            else:
+                emit = "event-add"
+        self.event_map[event.handle] = event.serialize()
+        self.event_id_map[event.gramps_id] = self.event_map[event.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([event.handle],))
 
     def commit_tag(self, tag, trans, change_time=None):
-        self.tag_map[tag.handle] = tag
+        emit = None
+        if not trans.batch:
+            if tag.handle in self.tag_map:
+                emit = "tag-update"
+            else:
+                emit = "tag-add"
+        self.tag_map[tag.handle] = tag.serialize()
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([tag.handle],))
 
-    def commit_media_object(self, obj, transaction, change_time=None):
-        self.media_map[obj.handle] = obj
+    def commit_media_object(self, media, trans, change_time=None):
+        emit = None
+        if not trans.batch:
+            if media.handle in self.media_map:
+                emit = "media-update"
+            else:
+                emit = "media-add"
+        self.media_map[media.handle] = media.serialize()
+        self.media_id_map[media.gramps_id] = self.media_map[media.handle]
+        # Emit after added:
+        if emit:
+            self.emit(emit, ([media.handle],))
 
     def get_gramps_ids(self, obj_key):
         key2table = {
-            PERSON_KEY:     self.person_map, 
-            FAMILY_KEY:     self.family_map, 
-            CITATION_KEY:   self.citation_map, 
-            SOURCE_KEY:     self.source_map, 
-            EVENT_KEY:      self.event_map, 
-            MEDIA_KEY:      self.media_map, 
-            PLACE_KEY:      self.place_map, 
-            REPOSITORY_KEY: self.repository_map, 
-            NOTE_KEY:       self.note_map, 
+            PERSON_KEY:     self.person_id_map, 
+            FAMILY_KEY:     self.family_id_map, 
+            CITATION_KEY:   self.citation_id_map, 
+            SOURCE_KEY:     self.source_id_map, 
+            EVENT_KEY:      self.event_id_map, 
+            MEDIA_KEY:      self.media_id_map, 
+            PLACE_KEY:      self.place_id_map, 
+            REPOSITORY_KEY: self.repository_id_map, 
+            NOTE_KEY:       self.note_id_map, 
             }
-        table = key2table[obj_key]
-        return [item.gramps_id for item in table.values()]
+        return list(key2table[obj_key].keys())
 
     def transaction_begin(self, transaction):
+        ## FIXME
         return 
 
-    def disable_signals(self):
-        pass
-
     def set_researcher(self, owner):
-        pass
+        self.owner.set_from(owner)
+
+    def get_researcher(self):
+        return self.owner
 
     def request_rebuild(self):
-        pass
+        self.emit('person-rebuild')
+        self.emit('family-rebuild')
+        self.emit('place-rebuild')
+        self.emit('source-rebuild')
+        self.emit('citation-rebuild')
+        self.emit('media-rebuild')
+        self.emit('event-rebuild')
+        self.emit('repository-rebuild')
+        self.emit('note-rebuild')
+        self.emit('tag-rebuild')
 
     def copy_from_db(self, db):
         """
@@ -1034,21 +1360,11 @@ class DictionaryDb(DbWriteBase, DbReadBase):
 
         if self.readonly or not handle:
             return
-        person = self.get_person_from_handle(handle)
-        #self.genderStats.uncount_person (person)
-        #self.remove_from_surname_list(person)
-        if isinstance(handle, str):
-            handle = handle.encode('utf-8')
-        if transaction.batch:
-            with BSDDBTxn(self.env, self.person_map) as txn:            
-                self.delete_primary_from_reference_map(handle, transaction,
-                                                       txn=txn.txn)
-                txn.delete(handle)
-        else:
-            self.delete_primary_from_reference_map(handle, transaction,
-                                                   txn=self.txn)
-            self.person_map.delete(handle, txn=self.txn)
-            transaction.add(PERSON_KEY, TXNDEL, handle, person.serialize(), None)
+        if handle in self.person_map:
+            person = Person.create(self.person_map[handle])
+            del self.person_map[handle]
+            del self.person_id_map[person.gramps_id]
+            self.emit("person-delete", ([handle],))
 
     def remove_source(self, handle, transaction):
         """
@@ -1056,7 +1372,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.source_map, 
-                              SOURCE_KEY)
+                         self.source_id_map, SOURCE_KEY)
 
     def remove_citation(self, handle, transaction):
         """
@@ -1064,7 +1380,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.citation_map, 
-                              CITATION_KEY)
+                         self.citation_id_map, CITATION_KEY)
 
     def remove_event(self, handle, transaction):
         """
@@ -1072,7 +1388,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.event_map, 
-                              EVENT_KEY)
+                         self.event_id_map, EVENT_KEY)
 
     def remove_object(self, handle, transaction):
         """
@@ -1080,7 +1396,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.media_map, 
-                              MEDIA_KEY)
+                         self.media_id_map, MEDIA_KEY)
 
     def remove_place(self, handle, transaction):
         """
@@ -1088,7 +1404,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.place_map, 
-                              PLACE_KEY)
+                         self.place_id_map, PLACE_KEY)
 
     def remove_family(self, handle, transaction):
         """
@@ -1096,7 +1412,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.family_map, 
-                              FAMILY_KEY)
+                         self.family_id_map, FAMILY_KEY)
 
     def remove_repository(self, handle, transaction):
         """
@@ -1104,7 +1420,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.repository_map, 
-                              REPOSITORY_KEY)
+                         self.repository_id_map, REPOSITORY_KEY)
 
     def remove_note(self, handle, transaction):
         """
@@ -1112,7 +1428,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.note_map, 
-                              NOTE_KEY)
+                         self.note_id_map, NOTE_KEY)
 
     def remove_tag(self, handle, transaction):
         """
@@ -1120,7 +1436,7 @@ class DictionaryDb(DbWriteBase, DbReadBase):
         database, preserving the change in the passed transaction. 
         """
         self.__do_remove(handle, transaction, self.tag_map, 
-                              TAG_KEY)
+                         None, TAG_KEY)
 
     def is_empty(self):
         """
@@ -1131,23 +1447,15 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 return False
         return True
 
-    def __do_remove(self, handle, transaction, data_map, key):
+    def __do_remove(self, handle, transaction, data_map, data_id_map, key):
         if self.readonly or not handle:
             return
-
-        if isinstance(handle, str):
-            handle = handle.encode('utf-8')
-        if transaction.batch:
-            with BSDDBTxn(self.env, data_map) as txn:
-                self.delete_primary_from_reference_map(handle, transaction,
-                                                        txn=txn.txn)
-                txn.delete(handle)
-        else:
-            self.delete_primary_from_reference_map(handle, transaction,
-                                                   txn=self.txn)
-            old_data = data_map.get(handle, txn=self.txn)
-            data_map.delete(handle, txn=self.txn)
-            transaction.add(key, TXNDEL, handle, old_data, None)
+        if handle in data_map:
+            obj = self._tables[KEY_TO_CLASS_MAP[key]]["class_func"].create(data_map[handle])
+            del data_map[handle]
+            if data_id_map:
+                del data_id_map[obj.gramps_id]
+            self.emit(KEY_TO_NAME_MAP[key] + "-delete", ([handle],))
 
     def delete_primary_from_reference_map(self, handle, transaction, txn=None):
         """
@@ -1206,3 +1514,318 @@ class DictionaryDb(DbWriteBase, DbReadBase):
                 transaction.add(REFERENCE_KEY, TXNDEL, key, old_data, None)
                 #transaction.reference_del.append(str(key))
             self.reference_map.delete(key, txn=txn)
+
+    ## Missing:
+
+    def backup(self):
+        ## FIXME
+        pass
+
+    def close(self):
+        if self._directory:
+            from gramps.plugins.export.exportxml import XmlWriter
+            from gramps.cli.user import User 
+            writer = XmlWriter(self, User(), strip_photos=0, compress=1)
+            filename = os.path.join(self._directory, "data.gramps")
+            writer.write(filename)
+            filename = os.path.join(self._directory, "meta_data.db")
+            touch(filename)
+
+    def find_backlink_handles(self, handle, include_classes=None):
+        ## FIXME
+        return []
+
+    def find_initial_person(self):
+        items = self.person_map.keys()
+        if len(items) > 0:
+            return self.get_person_from_handle(list(items)[0])
+        return None
+
+    def find_place_child_handles(self, handle):
+        ## FIXME
+        return []
+
+    def get_bookmarks(self):
+        return self.bookmarks
+
+    def get_child_reference_types(self):
+        ## FIXME
+        return []
+
+    def get_citation_bookmarks(self):
+        return self.citation_bookmarks
+
+    def get_cursor(self, table, txn=None, update=False, commit=False):
+        ## FIXME
+        ## called from a complete find_back_ref
+        pass
+
+    # cursors for lookups in the reference_map for back reference
+    # lookups. The reference_map has three indexes:
+    # the main index: a tuple of (primary_handle, referenced_handle)
+    # the primary_handle index: the primary_handle
+    # the referenced_handle index: the referenced_handle
+    # the main index is unique, the others allow duplicate entries.
+
+    def get_default_handle(self):
+        items = self.person_map.keys()
+        if len(items) > 0:
+            return list(items)[0]
+        return None
+
+    def get_event_attribute_types(self):
+        ## FIXME
+        return []
+
+    def get_event_bookmarks(self):
+        return self.event_bookmarks
+
+    def get_event_roles(self):
+        ## FIXME
+        return []
+
+    def get_event_types(self):
+        ## FIXME
+        return []
+
+    def get_family_attribute_types(self):
+        ## FIXME
+        return []
+
+    def get_family_bookmarks(self):
+        return self.family_bookmarks
+
+    def get_family_event_types(self):
+        ## FIXME
+        return []
+
+    def get_family_relation_types(self):
+        ## FIXME
+        return []
+
+    def get_media_attribute_types(self):
+        ## FIXME
+        return []
+
+    def get_media_bookmarks(self):
+        return self.media_bookmarks
+
+    def get_name_types(self):
+        ## FIXME
+        return []
+
+    def get_note_bookmarks(self):
+        return self.note_bookmarks
+
+    def get_note_types(self):
+        ## FIXME
+        return []
+
+    def get_origin_types(self):
+        ## FIXME
+        return []
+
+    def get_person_attribute_types(self):
+        ## FIXME
+        return []
+
+    def get_person_event_types(self):
+        ## FIXME
+        return []
+
+    def get_place_bookmarks(self):
+        return self.place_bookmarks
+
+    def get_place_tree_cursor(self):
+        ## FIXME
+        return []
+
+    def get_place_types(self):
+        ## FIXME
+        return []
+
+    def get_repo_bookmarks(self):
+        return self.repo_bookmarks
+
+    def get_repository_types(self):
+        ## FIXME
+        return []
+
+    def get_save_path(self):
+        return self._directory
+
+    def get_source_attribute_types(self):
+        ## FIXME
+        return []
+
+    def get_source_bookmarks(self):
+        return self.source_bookmarks
+
+    def get_source_media_types(self):
+        ## FIXME
+        return []
+
+    def get_surname_list(self):
+        ## FIXME
+        return []
+
+    def get_url_types(self):
+        ## FIXME
+        return []
+
+    def has_changed(self):
+        ## FIXME
+        return True
+
+    def is_open(self):
+        return self._directory is not None
+
+    def iter_citation_handles(self):
+        return (key for key in self.citation_map.keys())
+
+    def iter_citations(self):
+        return (Citation.create(key) for key in self.citation_map.values())
+
+    def iter_event_handles(self):
+        return (key for key in self.event_map.keys())
+
+    def iter_events(self):
+        return (Event.create(key) for key in self.event_map.values())
+
+    def iter_media_objects(self):
+        return (MediaObject.create(key) for key in self.media_map.values())
+
+    def iter_note_handles(self):
+        return (key for key in self.note_map.keys())
+
+    def iter_notes(self):
+        return (Note.create(key) for key in self.note_map.values())
+
+    def iter_place_handles(self):
+        return (key for key in self.place_map.keys())
+
+    def iter_places(self):
+        return (Place.create(key) for key in self.place_map.values())
+
+    def iter_repositories(self):
+        return (Repository.create(key) for key in self.repositories_map.values())
+
+    def iter_repository_handles(self):
+        return (key for key in self.repositories_map.keys())
+
+    def iter_source_handles(self):
+        return (key for key in self.source_map.keys())
+
+    def iter_sources(self):
+        return (Source.create(key) for key in self.source_map.values())
+
+    def iter_tag_handles(self):
+        return (key for key in self.tag_map.keys())
+
+    def iter_tags(self):
+        return (Tag.create(key) for key in self.tag_map.values())
+
+    def load(self, directory, callback=None, mode=None, 
+             force_schema_upgrade=False, 
+             force_bsddb_upgrade=False, 
+             force_bsddb_downgrade=False, 
+             force_python_upgrade=False):
+        from gramps.plugins.importer.importxml import importData
+        from gramps.cli.user import User 
+        self._directory = directory
+        self.full_name = os.path.abspath(self._directory)
+        self.path = self.full_name
+        self.brief_name = os.path.basename(self._directory)
+        filename = os.path.join(directory, "data.gramps")
+        if os.path.isfile(filename):
+            importData(self, filename, User())
+
+    def redo(self, update_history=True):
+        ## FIXME
+        pass
+
+    def restore(self):
+        ## FIXME
+        pass
+
+    def set_prefixes(self, person, media, family, source, citation, 
+                     place, event, repository, note):
+        ## FIXME
+        pass
+
+    def set_save_path(self, directory):
+        self._directory = directory
+        self.full_name = os.path.abspath(self._directory)
+        self.path = self.full_name
+        self.brief_name = os.path.basename(self._directory)
+
+    def undo(self, update_history=True):
+        ## FIXME
+        pass
+
+    def write_version(self, directory):
+        """Write files for a newly created DB."""
+        versionpath = os.path.join(directory, str(DBBACKEND))
+        _LOG.debug("Write database backend file to 'dictionarydb'")
+        with open(versionpath, "w") as version_file:
+            version_file.write("dictionarydb")
+
+    def report_bm_change(self):
+        """
+        Add 1 to the number of bookmark changes during this session.
+        """
+        self._bm_changes += 1
+
+    def db_has_bm_changes(self):
+        """
+        Return whethere there were bookmark changes during the session.
+        """
+        return self._bm_changes > 0
+
+    def get_summary(self):
+        """
+        Returns dictionary of summary item.
+        Should include, if possible:
+
+        _("Number of people")
+        _("Version")
+        _("Schema version")
+        """
+        return {
+            _("Number of people"): self.get_number_of_people(),
+        }
+
+    def get_dbname(self):
+        """
+        In DictionaryDb, the database is in a text file at the path
+        """
+        filepath = os.path.join(self._directory, "name.txt")
+        try:
+            name_file = open(filepath, "r")
+            name = name_file.readline().strip()
+            name_file.close()
+        except (OSError, IOError) as msg:
+            _LOG.error(str(msg))
+            name = None
+        return name
+
+    def reindex_reference_map(self):
+        ## FIXME
+        pass
+
+    def rebuild_secondary(self, update):
+        ## FIXME
+        pass
+
+    def prepare_import(self):
+        """
+        Initialization before imports
+        """
+        pass
+
+    def commit_import(self):
+        """
+        Post process after imports
+        """
+        pass
+
diff --git a/gramps/plugins/gramplet/leak.py b/gramps/plugins/gramplet/leak.py
index 2e30e7582..3c10a1681 100644
--- a/gramps/plugins/gramplet/leak.py
+++ b/gramps/plugins/gramplet/leak.py
@@ -31,7 +31,6 @@ Show uncollected objects in a window.
 #------------------------------------------------------------------------
 from gramps.gen.const import GRAMPS_LOCALE as glocale
 _ = glocale.translation.gettext
-from bsddb3.db import DBError
 
 #------------------------------------------------------------------------
 #
@@ -155,6 +154,13 @@ class Leak(Gramplet):
                         parent=self.uistate.window)
 
     def display(self):
+        try:
+            from bsddb3.db import DBError
+        except:
+            class DBError(Exception):
+                """
+                Dummy.
+                """
         gc.collect(2)
         self.model.clear()
         count = 0
diff --git a/gramps/plugins/importer/importcsv.py b/gramps/plugins/importer/importcsv.py
index 4c1e61764..44f7d29ed 100644
--- a/gramps/plugins/importer/importcsv.py
+++ b/gramps/plugins/importer/importcsv.py
@@ -103,8 +103,10 @@ def importData(dbase, filename, user):
         parser = CSVParser(dbase, user, (config.get('preferences.tag-on-import-format') if 
                                          config.get('preferences.tag-on-import') else None))
     try:
+        dbase.prepare_import()
         with open(filename, 'r') as filehandle:
             parser.parse(filehandle)
+        dbase.commit_import()
     except EnvironmentError as err:
         user.notify_error(_("%s could not be opened\n") % filename, str(err))
         return
diff --git a/gramps/plugins/importer/importgedcom.py b/gramps/plugins/importer/importgedcom.py
index 5f095674a..34a1d4876 100644
--- a/gramps/plugins/importer/importgedcom.py
+++ b/gramps/plugins/importer/importgedcom.py
@@ -131,7 +131,9 @@ def importData(database, filename, user):
     try:
         read_only = database.readonly
         database.readonly = False
+        database.prepare_import()
         gedparse.parse_gedcom_file(False)
+        database.commit_import()
         database.readonly = read_only
         ifile.close()
     except IOError as msg:
diff --git a/gramps/plugins/importer/importgeneweb.py b/gramps/plugins/importer/importgeneweb.py
index 334cfac7c..f3c23e074 100644
--- a/gramps/plugins/importer/importgeneweb.py
+++ b/gramps/plugins/importer/importgeneweb.py
@@ -154,7 +154,9 @@ def importData(database, filename, user):
         return
 
     try:
+        database.prepare_import()
         status = g.parse_geneweb_file()
+        database.commit_import()
     except IOError as msg:
         errmsg = _("%s could not be opened\n") % filename
         user.notify_error(errmsg,str(msg))
diff --git a/gramps/plugins/importer/importgpkg.py b/gramps/plugins/importer/importgpkg.py
index 3c7f1b4c7..2a164b09d 100644
--- a/gramps/plugins/importer/importgpkg.py
+++ b/gramps/plugins/importer/importgpkg.py
@@ -93,7 +93,9 @@ def impData(database, name, user):
     imp_db_name = os.path.join(tmpdir_path, XMLFILE)  
 
     importer = importData
+    database.prepare_import()
     info = importer(database, imp_db_name, user)
+    database.commit_import()
 
     newmediapath = database.get_mediapath()
     #import of gpkg should not change media path as all media has new paths!
diff --git a/gramps/plugins/importer/importprogen.py b/gramps/plugins/importer/importprogen.py
index 16b72eeea..3ad119660 100644
--- a/gramps/plugins/importer/importprogen.py
+++ b/gramps/plugins/importer/importprogen.py
@@ -75,7 +75,9 @@ def _importData(database, filename, user):
         return
 
     try:
+        database.prepare_import()
         status = g.parse_progen_file()
+        database.commit_import()
     except ProgenError as msg:
         user.notify_error(_("Pro-Gen data error"), str(msg))
         return
diff --git a/gramps/plugins/importer/importvcard.py b/gramps/plugins/importer/importvcard.py
index 9495afb5f..e9f42d44b 100644
--- a/gramps/plugins/importer/importvcard.py
+++ b/gramps/plugins/importer/importvcard.py
@@ -63,8 +63,10 @@ def importData(database, filename, user):
     """Function called by Gramps to import data on persons in VCard format."""
     parser = VCardParser(database)
     try:
+        database.prepare_import()
         with OpenFileOrStdin(filename) as filehandle:
             parser.parse(filehandle)
+        database.commit_import()
     except EnvironmentError as msg:
         user.notify_error(_("%s could not be opened\n") % filename, str(msg))
         return
diff --git a/gramps/plugins/importer/importxml.py b/gramps/plugins/importer/importxml.py
index 00aa28420..acee74f69 100644
--- a/gramps/plugins/importer/importxml.py
+++ b/gramps/plugins/importer/importxml.py
@@ -57,7 +57,7 @@ from gramps.gen.lib import (Address, Attribute, AttributeType, ChildRef,
                             SrcAttribute, SrcAttributeType, StyledText,
                             StyledTextTag, StyledTextTagType, Surname, Tag, Url)
 from gramps.gen.db import DbTxn
-from gramps.gen.db.write import CLASS_TO_KEY_MAP
+#from gramps.gen.db.write import CLASS_TO_KEY_MAP
 from gramps.gen.errors import GrampsImportError
 from gramps.gen.utils.id import create_id
 from gramps.gen.utils.db import family_name
@@ -68,7 +68,7 @@ from gramps.gen.display.name import displayer as name_displayer
 from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, 
                                    EVENT_KEY, MEDIA_KEY, PLACE_KEY, 
                                    REPOSITORY_KEY, NOTE_KEY, TAG_KEY, 
-                                   CITATION_KEY)
+                                   CITATION_KEY, CLASS_TO_KEY_MAP)
 from gramps.gen.updatecallback import UpdateCallback
 from gramps.version import VERSION
 from gramps.gen.config import config
@@ -122,6 +122,7 @@ def importData(database, filename, user):
     line_cnt = 0
     person_cnt = 0
     
+    database.prepare_import()
     with ImportOpenFileContextManager(filename, user) as xml_file:
         if xml_file is None:
             return
@@ -162,6 +163,7 @@ def importData(database, filename, user):
                           "valid Gramps database."))
             return
 
+    database.commit_import()
     database.readonly = read_only
     return info
 
diff --git a/gramps/webapp/reports.py b/gramps/webapp/reports.py
index c0c7f480e..35cc66356 100644
--- a/gramps/webapp/reports.py
+++ b/gramps/webapp/reports.py
@@ -85,9 +85,9 @@ def import_file(db, filename, user):
                     print("ERROR:", name, exception)
                 return False
             import_function = getattr(mod, pdata.import_function)
-            db.prepare_import()
+            #db.prepare_import()
             retval = import_function(db, filename, user)
-            db.commit_import()
+            #db.commit_import()
             return retval
     return False
 
diff --git a/gramps/webapp/utils.py b/gramps/webapp/utils.py
index 07eddd341..10e0aaf38 100644
--- a/gramps/webapp/utils.py
+++ b/gramps/webapp/utils.py
@@ -143,8 +143,12 @@ def get_person_from_handle(db, handle):
         return None
 
 def probably_alive(handle):
+    ## FIXME: need to call after save?
     person = db.get_person_from_handle(handle)
-    return alive(person, db)
+    if person:
+        return alive(person, db)
+    else:
+        return True
 
 def format_number(number, with_grouping=True):
     if number != "":