Environment variables in mediapath

The mediapath could now be a relative path from database location,
or use variables ($GRAMPSHOME, $GRAMPS_RESOURCES, etc.)
This commit is contained in:
belissent 2015-08-19 16:26:59 +02:00
parent 65ff7340af
commit 5d654d402a
6 changed files with 187 additions and 83 deletions

View File

@ -7,7 +7,7 @@
<researcher> <researcher>
<resname>Alex Roitman,,,</resname> <resname>Alex Roitman,,,</resname>
</researcher> </researcher>
<mediapath>/home/pierre/Gramps/master/example/gramps</mediapath> <mediapath>$GRAMPS_RESOURCES/example/gramps</mediapath>
</header> </header>
<name-formats> <name-formats>
<format number="-1" name="SURNAME, Given (Common)" fmt_str="SURNAME, given (common)" active="1"/> <format number="-1" name="SURNAME, Given (Common)" fmt_str="SURNAME, given (common)" active="1"/>

View File

@ -11,7 +11,7 @@
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
@ -85,16 +85,16 @@ APP_VCARD = ["text/x-vcard", "text/x-vcalendar"]
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
if 'GRAMPSHOME' in os.environ: if 'GRAMPSHOME' in os.environ:
USER_HOME = get_env_var('GRAMPSHOME') USER_HOME = get_env_var('GRAMPSHOME')
HOME_DIR = os.path.join(USER_HOME, 'gramps') HOME_DIR = os.path.join(USER_HOME, 'gramps')
elif 'USERPROFILE' in os.environ: elif 'USERPROFILE' in os.environ:
USER_HOME = get_env_var('USERPROFILE') USER_HOME = get_env_var('USERPROFILE')
if 'APPDATA' in os.environ: if 'APPDATA' in os.environ:
HOME_DIR = os.path.join(get_env_var('APPDATA'), 'gramps') HOME_DIR = os.path.join(get_env_var('APPDATA'), 'gramps')
else: else:
HOME_DIR = os.path.join(USER_HOME, 'gramps') HOME_DIR = os.path.join(USER_HOME, 'gramps')
else: else:
USER_HOME = get_env_var('HOME') USER_HOME = get_env_var('HOME')
HOME_DIR = os.path.join(USER_HOME, '.gramps') HOME_DIR = os.path.join(USER_HOME, '.gramps')
@ -115,9 +115,26 @@ USER_PLUGINS = os.path.join(VERSION_DIR, "plugins")
USER_DIRLIST = (USER_HOME, HOME_DIR, VERSION_DIR, ENV_DIR, TEMP_DIR, THUMB_DIR, USER_DIRLIST = (USER_HOME, HOME_DIR, VERSION_DIR, ENV_DIR, TEMP_DIR, THUMB_DIR,
THUMB_NORMAL, THUMB_LARGE, USER_PLUGINS) THUMB_NORMAL, THUMB_LARGE, USER_PLUGINS)
# Update environment
# This could be of general use, for example when using os.path.expandvars
os.environ['GRAMPS_VERSION'] = VERSION
os.environ['GRAMPS_VERSION_MAJOR'] = major_version
os.environ['GRAMPS_VERSION_DIR'] = VERSION_DIR
os.environ['GRAMPS_ENV_DIR'] = ENV_DIR
os.environ['GRAMPS_TEMP_DIR'] = TEMP_DIR
os.environ['GRAMPS_THUMB_DIR'] = THUMB_DIR
os.environ['GRAMPS_THUMB_NORMAL'] = THUMB_NORMAL
os.environ['GRAMPS_THUMB_LARGE'] = THUMB_LARGE
os.environ['GRAMPS_USER_PLUGINS'] = USER_PLUGINS
# Attention: $GRAMPSHOME should NOT be set, because it redefines the HOME_DIR behavior
# This leads to bugs, especially when a test calls GRAMPS again:
# the HOME_DIR is then re-computed with a wrong $GRAMPSHOME
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
# Paths to python modules - assumes that the root directory is one level # Paths to python modules - assumes that the root directory is one level
# above this one, and that the plugins directory is below the root directory. # above this one, and that the plugins directory is below the root directory.
# #
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -192,21 +209,21 @@ COMMENTS = _("Gramps\n (Genealogical Research and Analysis "
"is a personal genealogy program.") "is a personal genealogy program.")
AUTHORS = [ AUTHORS = [
"Alexander Roitman", "Alexander Roitman",
"Benny Malengier", "Benny Malengier",
"Brian Matherly", "Brian Matherly",
"Donald A. Peterson", "Donald A. Peterson",
"Donald N. Allingham", "Donald N. Allingham",
"David Hampton", "David Hampton",
"Martin Hawlisch", "Martin Hawlisch",
"Richard Taylor", "Richard Taylor",
"Tim Waugh", "Tim Waugh",
"John Ralls" "John Ralls"
] ]
AUTHORS_FILE = os.path.join(DATA_DIR, "authors.xml") AUTHORS_FILE = os.path.join(DATA_DIR, "authors.xml")
DOCUMENTERS = [ DOCUMENTERS = [
'Alexander Roitman', 'Alexander Roitman',
] ]
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
@ -232,14 +249,14 @@ ARABIC_SEMICOLON = "؛"
# (longName, shortName, type , default, flags, descrip , argDescrip) # (longName, shortName, type , default, flags, descrip , argDescrip)
POPT_TABLE = [ POPT_TABLE = [
("config", 'c', str, None, 0, "Set config setting(s) and start Gramps", ""), ("config", 'c', str, None, 0, "Set config setting(s) and start Gramps", ""),
("open", 'O', str, None, 0, "Open family tree", "FAMILY_TREE"), ("open", 'O', str, None, 0, "Open family tree", "FAMILY_TREE"),
("create", 'C', str, None, 0, "Create or Open family tree", "FAMILY_TREE"), ("create", 'C', str, None, 0, "Create or Open family tree", "FAMILY_TREE"),
("import", 'i', str, None, 0, "Import file", "FILENAME"), ("import", 'i', str, None, 0, "Import file", "FILENAME"),
("export", 'e', str, None, 0, "Export file", "FILENAME"), ("export", 'e', str, None, 0, "Export file", "FILENAME"),
("format", 'f', str, None, 0, 'Specify format', "FORMAT"), ("format", 'f', str, None, 0, 'Specify format', "FORMAT"),
("action", 'a', str, None, 0, 'Specify action', "ACTION"), ("action", 'a', str, None, 0, 'Specify action', "ACTION"),
("options", 'p', str, None, 0, 'Specify options', "OPTIONS_STRING"), ("options", 'p', str, None, 0, 'Specify options', "OPTIONS_STRING"),
("debug", 'd', str, None, 0, 'Enable debug logs', "LOGGER_NAME"), ("debug", 'd', str, None, 0, 'Enable debug logs', "LOGGER_NAME"),
("", 'l', None, None, 0, 'List Family Trees', ""), ("", 'l', None, None, 0, 'List Family Trees', ""),
("", 'L', None, None, 0, 'List Family Tree Details', ""), ("", 'L', None, None, 0, 'List Family Tree Details', ""),
("show", 's', None, None, 0, "Show config settings", ""), ("show", 's', None, None, 0, "Show config settings", ""),
@ -248,42 +265,42 @@ POPT_TABLE = [
] ]
LONGOPTS = [ LONGOPTS = [
"action=", "action=",
"class=", "class=",
"config=", "config=",
"debug=", "debug=",
"display=", "display=",
"disable-sound", "disable-sound",
"disable-crash-dialog", "disable-crash-dialog",
"enable-sound", "enable-sound",
"espeaker=", "espeaker=",
"export=", "export=",
"force-unlock", "force-unlock",
"format=", "format=",
"gdk-debug=", "gdk-debug=",
"gdk-no-debug=", "gdk-no-debug=",
"gtk-debug=", "gtk-debug=",
"gtk-no-debug=", "gtk-no-debug=",
"gtk-module=", "gtk-module=",
"g-fatal-warnings", "g-fatal-warnings",
"help", "help",
"import=", "import=",
"load-modules=", "load-modules=",
"list" "list"
"name=", "name=",
"oaf-activate-iid=", "oaf-activate-iid=",
"oaf-ior-fd=", "oaf-ior-fd=",
"oaf-private", "oaf-private",
"open=", "open=",
"create=", "create=",
"options=", "options=",
"screen=", "screen=",
"show", "show",
"sm-client-id=", "sm-client-id=",
"sm-config-prefix=", "sm-config-prefix=",
"sm-disable", "sm-disable",
"sync", "sync",
"usage", "usage",
"version", "version",
"qml", "qml",
"yes", "yes",

View File

@ -10,7 +10,7 @@
# the Free Software Foundation; either version 2 of the License, or # the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version. # (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU General Public License for more details.
@ -91,7 +91,7 @@ def get_empty_tempdir(dirname):
""" Return path to TEMP_DIR/dirname, a guaranteed empty directory """ Return path to TEMP_DIR/dirname, a guaranteed empty directory
makes intervening directories if required makes intervening directories if required
fails if _file_ by that name already exists, fails if _file_ by that name already exists,
or for inadequate permissions to delete dir/files or create dir(s) or for inadequate permissions to delete dir/files or create dir(s)
""" """
@ -121,10 +121,10 @@ def relative_path(original, base):
return original return original
original = os.path.normpath(original) original = os.path.normpath(original)
base = os.path.normpath(base) base = os.path.normpath(base)
# If the db_dir and obj_dir are on different drives (win only) # If the db_dir and obj_dir are on different drives (win only)
# then there cannot be a relative path. Return original obj_path # then there cannot be a relative path. Return original obj_path
(base_drive, base) = os.path.splitdrive(base) (base_drive, base) = os.path.splitdrive(base)
(orig_drive, orig_name) = os.path.splitdrive(original) (orig_drive, orig_name) = os.path.splitdrive(original)
if base_drive.upper() != orig_drive.upper(): if base_drive.upper() != orig_drive.upper():
return original return original
@ -133,7 +133,7 @@ def relative_path(original, base):
# shared by base and target. # shared by base and target.
base_list = (base).split(os.sep) base_list = (base).split(os.sep)
target_list = (orig_name).split(os.sep) target_list = (orig_name).split(os.sep)
# make sure '/home/person' and 'c:/home/person' both give # make sure '/home/person' and 'c:/home/person' both give
# list ['home', 'person'] # list ['home', 'person']
base_list = [_f for _f in base_list if _f] base_list = [_f for _f in base_list if _f]
target_list = [_f for _f in target_list if _f] target_list = [_f for _f in target_list if _f]
@ -146,14 +146,48 @@ def relative_path(original, base):
rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:] rel_list = [os.pardir] * (len(base_list)-i) + target_list[i:]
return os.path.join(*rel_list) return os.path.join(*rel_list)
def expanded_vars_path(path):
"""
Expand environment variables in a path
$GRAMPSHOME is set and restored afterwards,
because undefined $GRAMPSHOME has a special meaning (see const.py).
"""
if not 'GRAMPSHOME' in os.environ:
os.environ['GRAMPSHOME'] = USER_HOME
grampshome_added = True
path = os.path.expandvars(path)
if (grampshome_added):
del os.environ['GRAMPSHOME']
return path
def media_path(db): def media_path(db):
""" """
Given a database, return the mediapath to use as basedir for media Given a database, return the mediapath to use as basedir for media
""" """
mpath = db.get_mediapath() mpath = db.get_mediapath()
return norm_media_path(mpath, db)
def norm_media_path(mpath, db):
"""
Normalize a mediapath:
- Relative mediapath are considered as relative to the database
- Expand variables ($GRAMPSHOME, $GRAMPS_RESOURCES, etc.)
- Convert to absolute path
- Convert slashes and case (on Windows)
"""
# Use home dir if no media_path specified
if mpath is None: if mpath is None:
#use home dir mpath = os.path.abspath(USER_HOME)
mpath = USER_HOME # Expand environment variables
mpath = expanded_vars_path(mpath)
# Relative mediapath are considered as relative to the database
if not os.path.isabs(mpath):
basepath = db.get_save_path()
if not basepath:
basepath = USER_HOME
mpath = os.path.join(os.path.abspath(basepath), mpath)
# Normalize path
mpath = os.path.normcase(os.path.normpath(os.path.abspath(mpath)))
return mpath return mpath
def media_path_full(db, filename): def media_path_full(db, filename):
@ -178,7 +212,7 @@ def search_for(name):
return 1 return 1
if os.access(name, os.X_OK) and not os.path.isdir(name): if os.access(name, os.X_OK) and not os.path.isdir(name):
return 1 return 1
else: else:
for i in os.environ['PATH'].split(':'): #not win() for i in os.environ['PATH'].split(':'): #not win()
fname = os.path.join(i, name) fname = os.path.join(i, name)
if os.access(fname, os.X_OK) and not os.path.isdir(fname): if os.access(fname, os.X_OK) and not os.path.isdir(fname):

View File

@ -0,0 +1,84 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2007-2009 B. Malengier
# Copyright (C) 2009 Swoon on bug tracker
#
# 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.
#
#-------------------------------------------------------------------------
#
# Standard python modules
#
#-------------------------------------------------------------------------
import os
import shutil
import unittest
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import TEMP_DIR, USER_HOME, USER_PLUGINS
from gramps.gen.constfunc import get_env_var
from gramps.gen.utils.file import media_path, get_empty_tempdir
from gramps.gen.dbstate import DbState
from gramps.version import VERSION
#-------------------------------------------------------------------------
#
# FileTest class
#
#-------------------------------------------------------------------------
class FileTest(unittest.TestCase):
def test_mediapath(self):
# Create database
dbstate = DbState()
db = dbstate.make_database("bsddb")
path = get_empty_tempdir("utils_file_test")
db.write_version(path)
db.load(path)
dbstate.change_database(db)
# Test without db.mediapath set
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME))))
self.assertTrue(os.path.exists(media_path(db)))
# Test with absolute db.mediapath
db.set_mediapath(os.path.abspath(USER_HOME) + "/test_abs")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME + "/test_abs"))))
# Test with relative db.mediapath
db.set_mediapath("test_rel")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(TEMP_DIR + "/utils_file_test/test_rel"))))
# Test with environment variable
db.set_mediapath("$GRAMPSHOME/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_HOME + "/test_var"))))
db.set_mediapath("/test/$GRAMPS_VERSION/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath("/test/" + VERSION + "/test_var"))))
db.set_mediapath("${GRAMPS_USER_PLUGINS}/test_var")
self.assertEqual(media_path(db), os.path.normcase(os.path.normpath(os.path.abspath(USER_PLUGINS + "/test_var"))))
#-------------------------------------------------------------------------
#
# main
#
#-------------------------------------------------------------------------
if __name__ == "__main__":
unittest.main()

View File

@ -53,6 +53,7 @@ from gramps.gen.datehandler import get_date_formats
from gramps.gen.display.name import displayer as _nd from gramps.gen.display.name import displayer as _nd
from gramps.gen.display.name import NameDisplayError from gramps.gen.display.name import NameDisplayError
from gramps.gen.utils.alive import update_constants from gramps.gen.utils.alive import update_constants
from gramps.gen.utils.file import media_path
from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword, from gramps.gen.utils.keyword import (get_keywords, get_translation_from_keyword,
get_translations, get_keyword_from_translation) get_translations, get_keyword_from_translation)
from gramps.gen.lib import Date, FamilyRelType from gramps.gen.lib import Date, FamilyRelType
@ -1460,9 +1461,7 @@ class GrampsPreferences(ConfigureDialog):
_('_Apply'), _('_Apply'),
Gtk.ResponseType.OK) Gtk.ResponseType.OK)
) )
mpath = self.dbstate.db.get_mediapath() mpath = media_path(self.dbstate.db)
if not mpath:
mpath = HOME_DIR
f.set_current_folder(os.path.dirname(mpath)) f.set_current_folder(os.path.dirname(mpath))
status = f.run() status = f.run()

View File

@ -62,7 +62,7 @@ from gramps.gen.errors import GrampsImportError
from gramps.gen.utils.id import create_id from gramps.gen.utils.id import create_id
from gramps.gen.utils.db import family_name from gramps.gen.utils.db import family_name
from gramps.gen.utils.unknown import make_unknown, create_explanation_note from gramps.gen.utils.unknown import make_unknown, create_explanation_note
from gramps.gen.utils.file import create_checksum from gramps.gen.utils.file import create_checksum, media_path, norm_media_path
from gramps.gen.datehandler import parser, set_date from gramps.gen.datehandler import parser, set_date
from gramps.gen.display.name import displayer as name_displayer from gramps.gen.display.name import displayer as name_displayer
from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY, from gramps.gen.db.dbconst import (PERSON_KEY, FAMILY_KEY, SOURCE_KEY,
@ -165,35 +165,6 @@ def importData(database, filename, user):
database.readonly = read_only database.readonly = read_only
return info return info
## TODO - WITH MEDIA PATH, IS THIS STILL NEEDED?
## BETTER LEAVE ALL RELATIVE TO NEW RELATIVE PATH
## save_path is in .gramps/dbbase, no good place !
## # copy all local images into <database>.images directory
## db_dir = os.path.abspath(os.path.dirname(database.get_save_path()))
## db_base = os.path.basename(database.get_save_path())
## img_dir = os.path.join(db_dir, db_base)
## first = not os.path.exists(img_dir)
##
## for m_id in database.get_media_object_handles():
## mobject = database.get_object_from_handle(m_id)
## oldfile = mobject.get_path()
## if oldfile and not os.path.isabs(oldfile):
## if first:
## os.mkdir(img_dir)
## first = 0
## newfile = os.path.join(img_dir, oldfile)
##
## try:
## oldfilename = os.path.join(basefile, oldfile)
## shutil.copyfile(oldfilename, newfile)
## try:
## shutil.copystat(oldfilename, newfile)
## except:
## pass
## mobject.set_path(newfile)
## database.commit_media_object(mobject, None, change)
## except (IOError, OSError), msg:
## ErrorDialog(_('Could not copy file'), str(msg))
#------------------------------------------------------------------------- #-------------------------------------------------------------------------
# #
@ -965,14 +936,13 @@ class GrampsParser(UpdateCallback):
person = self.db.get_person_from_handle(self.home) person = self.db.get_person_from_handle(self.home)
self.db.set_default_person_handle(person.handle) self.db.set_default_person_handle(person.handle)
#set media path, this should really do some parsing to convert eg # Set media path
# windows path to unix ? # The paths are normalized before being compared.
if self.mediapath: if self.mediapath:
oldpath = self.db.get_mediapath() if not self.db.get_mediapath():
if not oldpath:
self.db.set_mediapath(self.mediapath) self.db.set_mediapath(self.mediapath)
elif not oldpath == self.mediapath: elif not media_path(self.db) == norm_media_path(self.mediapath, self.db):
self.user.notify_error(_("Could not change media path"), self.user.notify_error(_("Could not change media path"),
_("The opened file has media path %s, which conflicts with" _("The opened file has media path %s, which conflicts with"
" the media path of the Family Tree you import into. " " the media path of the Family Tree you import into. "
"The original media path has been retained. Copy the " "The original media path has been retained. Copy the "