[MERGE] from Trunk
bzr revid: stw@openerp.com-20140228161338-8yjlkwacuube7vir
This commit is contained in:
commit
5918ad323e
|
@ -64,19 +64,37 @@ class ir_attachment(osv.osv):
|
|||
data[attachment.id] = False
|
||||
return data
|
||||
|
||||
def _storage(self, cr, uid, context=None):
|
||||
return self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'ir_attachment.location', 'file')
|
||||
|
||||
@tools.ormcache()
|
||||
def _filestore(self, cr, uid, context=None):
|
||||
return os.path.join(tools.config['data_dir'], 'filestore', cr.dbname)
|
||||
|
||||
# 'data' field implementation
|
||||
def _full_path(self, cr, uid, location, path):
|
||||
# location = 'file:filestore'
|
||||
assert location.startswith('file:'), "Unhandled filestore location %s" % location
|
||||
location = location[5:]
|
||||
|
||||
# sanitize location name and path
|
||||
location = re.sub('[.]','',location)
|
||||
location = location.strip('/\\')
|
||||
|
||||
path = re.sub('[.]','',path)
|
||||
# sanitize ath
|
||||
path = re.sub('[.]', '', path)
|
||||
path = path.strip('/\\')
|
||||
return os.path.join(tools.config['root_path'], location, cr.dbname, path)
|
||||
return os.path.join(self._filestore(cr, uid), path)
|
||||
|
||||
def _get_path(self, cr, uid, location, bin_data):
|
||||
sha = hashlib.sha1(bin_data).hexdigest()
|
||||
|
||||
# retro compatibility
|
||||
fname = sha[:3] + '/' + sha
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
if os.path.isfile(full_path):
|
||||
return fname, full_path # keep existing path
|
||||
|
||||
# scatter files across 256 dirs
|
||||
# we use '/' in the db (even on windows)
|
||||
fname = sha[:2] + '/' + sha
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
dirname = os.path.dirname(full_path)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
return fname, full_path
|
||||
|
||||
def _file_read(self, cr, uid, location, fname, bin_size=False):
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
|
@ -92,18 +110,13 @@ class ir_attachment(osv.osv):
|
|||
|
||||
def _file_write(self, cr, uid, location, value):
|
||||
bin_value = value.decode('base64')
|
||||
fname = hashlib.sha1(bin_value).hexdigest()
|
||||
# scatter files across 1024 dirs
|
||||
# we use '/' in the db (even on windows)
|
||||
fname = fname[:3] + '/' + fname
|
||||
full_path = self._full_path(cr, uid, location, fname)
|
||||
try:
|
||||
dirname = os.path.dirname(full_path)
|
||||
if not os.path.isdir(dirname):
|
||||
os.makedirs(dirname)
|
||||
open(full_path,'wb').write(bin_value)
|
||||
except IOError:
|
||||
_logger.error("_file_write writing %s",full_path)
|
||||
fname, full_path = self._get_path(cr, uid, location, bin_value)
|
||||
if not os.path.exists(full_path):
|
||||
try:
|
||||
with open(full_path, 'wb') as fp:
|
||||
fp.write(bin_value)
|
||||
except IOError:
|
||||
_logger.error("_file_write writing %s", full_path)
|
||||
return fname
|
||||
|
||||
def _file_delete(self, cr, uid, location, fname):
|
||||
|
@ -122,10 +135,10 @@ class ir_attachment(osv.osv):
|
|||
if context is None:
|
||||
context = {}
|
||||
result = {}
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
location = self._storage(cr, uid, context)
|
||||
bin_size = context.get('bin_size')
|
||||
for attach in self.browse(cr, uid, ids, context=context):
|
||||
if location and attach.store_fname:
|
||||
if location != 'db' and attach.store_fname:
|
||||
result[attach.id] = self._file_read(cr, uid, location, attach.store_fname, bin_size)
|
||||
else:
|
||||
result[attach.id] = attach.db_datas
|
||||
|
@ -137,9 +150,9 @@ class ir_attachment(osv.osv):
|
|||
return True
|
||||
if context is None:
|
||||
context = {}
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
location = self._storage(cr, uid, context)
|
||||
file_size = len(value.decode('base64'))
|
||||
if location:
|
||||
if location != 'db':
|
||||
attach = self.browse(cr, uid, id, context=context)
|
||||
if attach.store_fname:
|
||||
self._file_delete(cr, uid, location, attach.store_fname)
|
||||
|
@ -285,8 +298,8 @@ class ir_attachment(osv.osv):
|
|||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
self.check(cr, uid, ids, 'unlink', context=context)
|
||||
location = self.pool.get('ir.config_parameter').get_param(cr, uid, 'ir_attachment.location')
|
||||
if location:
|
||||
location = self._storage(cr, uid, context)
|
||||
if location != 'db':
|
||||
for attach in self.browse(cr, uid, ids, context=context):
|
||||
if attach.store_fname:
|
||||
self._file_delete(cr, uid, location, attach.store_fname)
|
||||
|
@ -303,4 +316,3 @@ class ir_attachment(osv.osv):
|
|||
cr, uid, 'base', 'action_attachment', context=context)
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ from openerp import tools
|
|||
from openerp.osv import osv, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
ADDONS_PATH = tools.config['addons_path'].split(",")[-1]
|
||||
|
||||
class base_module_import(osv.osv_memory):
|
||||
""" Import Module """
|
||||
|
||||
|
|
|
@ -1,89 +1,92 @@
|
|||
import hashlib
|
||||
import os
|
||||
|
||||
import unittest2
|
||||
|
||||
import openerp
|
||||
import openerp.tests.common
|
||||
|
||||
class test_ir_attachment(openerp.tests.common.TransactionCase):
|
||||
HASH_SPLIT = 2 # FIXME: testing implementations detail is not a good idea
|
||||
|
||||
def test_00_attachment_flow(self):
|
||||
class test_ir_attachment(openerp.tests.common.TransactionCase):
|
||||
def setUp(self):
|
||||
super(test_ir_attachment, self).setUp()
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
root_path = openerp.tools.config['root_path']
|
||||
ira = registry('ir.attachment')
|
||||
self.ira = registry('ir.attachment')
|
||||
self.filestore = self.ira._filestore(cr, uid)
|
||||
|
||||
# Blob1
|
||||
blob1 = 'blob1'
|
||||
blob1_b64 = blob1.encode('base64')
|
||||
blob1_hash = hashlib.sha1(blob1).hexdigest()
|
||||
blob1_fname = blob1_hash[:3] + '/' + blob1_hash
|
||||
self.blob1 = 'blob1'
|
||||
self.blob1_b64 = self.blob1.encode('base64')
|
||||
blob1_hash = hashlib.sha1(self.blob1).hexdigest()
|
||||
self.blob1_fname = blob1_hash[:HASH_SPLIT] + '/' + blob1_hash
|
||||
|
||||
# Blob2
|
||||
blob2 = 'blob2'
|
||||
blob2_b64 = blob2.encode('base64')
|
||||
blob2_hash = hashlib.sha1(blob2).hexdigest()
|
||||
blob2_fname = blob2_hash[:3] + '/' + blob2_hash
|
||||
self.blob2_b64 = blob2.encode('base64')
|
||||
|
||||
def test_01_store_in_db(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
||||
# force storing in database
|
||||
registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'db')
|
||||
|
||||
# 'ir_attachment.location' is undefined test database storage
|
||||
a1 = ira.create(cr, uid, {'name': 'a1', 'datas': blob1_b64})
|
||||
a1_read = ira.read(cr, uid, [a1], ['datas'])
|
||||
self.assertEqual(a1_read[0]['datas'], blob1_b64)
|
||||
a1 = self.ira.create(cr, uid, {'name': 'a1', 'datas': self.blob1_b64})
|
||||
a1_read = self.ira.read(cr, uid, [a1], ['datas'])
|
||||
self.assertEqual(a1_read[0]['datas'], self.blob1_b64)
|
||||
|
||||
cr.execute("select id,db_datas from ir_attachment where id = %s", (a1,) )
|
||||
a1_db_datas = str(cr.fetchall()[0][1])
|
||||
self.assertEqual(a1_db_datas, blob1_b64)
|
||||
a1_db_datas = self.ira.browse(cr, uid, a1).db_datas
|
||||
self.assertEqual(a1_db_datas, self.blob1_b64)
|
||||
|
||||
# define a location for filestore
|
||||
registry('ir.config_parameter').set_param(cr, uid, 'ir_attachment.location', 'file:///filestore')
|
||||
def test_02_store_on_disk(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
||||
# Test file storage
|
||||
a2 = ira.create(cr, uid, {'name': 'a2', 'datas': blob1_b64})
|
||||
a2_read = ira.read(cr, uid, [a2], ['datas'])
|
||||
self.assertEqual(a2_read[0]['datas'], blob1_b64)
|
||||
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
|
||||
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
|
||||
|
||||
cr.execute("select id,store_fname from ir_attachment where id = %s", (a2,) )
|
||||
a2_store_fname = cr.fetchall()[0][1]
|
||||
self.assertEqual(a2_store_fname, blob1_fname)
|
||||
self.assertEqual(a2_store_fname, self.blob1_fname)
|
||||
self.assertTrue(os.path.isfile(os.path.join(self.filestore, a2_store_fname)))
|
||||
|
||||
a2_fn = os.path.join(root_path, 'filestore', cr.dbname, blob1_hash[:3], blob1_hash)
|
||||
fc = file(a2_fn).read()
|
||||
self.assertEqual(fc, blob1)
|
||||
def test_03_no_duplication(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
||||
# create a3 with same blob
|
||||
a3 = ira.create(cr, uid, {'name': 'a3', 'datas': blob1_b64})
|
||||
a3_read = ira.read(cr, uid, [a3], ['datas'])
|
||||
self.assertEqual(a3_read[0]['datas'], blob1_b64)
|
||||
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
|
||||
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
|
||||
|
||||
a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64})
|
||||
a3_store_fname = self.ira.browse(cr, uid, a3).store_fname
|
||||
|
||||
cr.execute("select id,store_fname from ir_attachment where id = %s", (a3,) )
|
||||
a3_store_fname = cr.fetchall()[0][1]
|
||||
self.assertEqual(a3_store_fname, a2_store_fname)
|
||||
|
||||
# create a4 blob2
|
||||
a4 = ira.create(cr, uid, {'name': 'a4', 'datas': blob2_b64})
|
||||
a4_read = ira.read(cr, uid, [a4], ['datas'])
|
||||
self.assertEqual(a4_read[0]['datas'], blob2_b64)
|
||||
def test_04_keep_file(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
||||
a4_fn = os.path.join(root_path, 'filestore', cr.dbname, blob2_hash[:3], blob2_hash)
|
||||
self.assertTrue(os.path.isfile(a4_fn))
|
||||
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
|
||||
a3 = self.ira.create(cr, uid, {'name': 'a3', 'datas': self.blob1_b64})
|
||||
|
||||
# delete a3 but file stays
|
||||
ira.unlink(cr, uid, [a3])
|
||||
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
|
||||
a2_fn = os.path.join(self.filestore, a2_store_fname)
|
||||
|
||||
self.ira.unlink(cr, uid, [a3])
|
||||
self.assertTrue(os.path.isfile(a2_fn))
|
||||
|
||||
# delete a2 it is unlinked
|
||||
ira.unlink(cr, uid, [a2])
|
||||
self.ira.unlink(cr, uid, [a2])
|
||||
self.assertFalse(os.path.isfile(a2_fn))
|
||||
|
||||
# update a4 blob2 by blob1
|
||||
ira.write(cr, uid, [a4], {'datas': blob1_b64})
|
||||
a4_read = ira.read(cr, uid, [a4], ['datas'])
|
||||
self.assertEqual(a4_read[0]['datas'], blob1_b64)
|
||||
def test_05_change_data_change_file(self):
|
||||
registry, cr, uid = self.registry, self.cr, self.uid
|
||||
|
||||
a2 = self.ira.create(cr, uid, {'name': 'a2', 'datas': self.blob1_b64})
|
||||
a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
|
||||
a2_fn = os.path.join(self.filestore, a2_store_fname)
|
||||
|
||||
# file of a4 disapear and a2 reappear
|
||||
self.assertFalse(os.path.isfile(a4_fn))
|
||||
self.assertTrue(os.path.isfile(a2_fn))
|
||||
|
||||
# everybody applause
|
||||
self.ira.write(cr, uid, [a2], {'datas': self.blob2_b64})
|
||||
self.assertFalse(os.path.isfile(a2_fn))
|
||||
|
||||
new_a2_store_fname = self.ira.browse(cr, uid, a2).store_fname
|
||||
self.assertNotEqual(a2_store_fname, new_a2_store_fname)
|
||||
|
||||
new_a2_fn = os.path.join(self.filestore, new_a2_store_fname)
|
||||
self.assertTrue(os.path.isfile(new_a2_fn))
|
||||
|
|
|
@ -72,7 +72,7 @@ def report_configuration():
|
|||
"""
|
||||
config = openerp.tools.config
|
||||
_logger.info("OpenERP version %s", __version__)
|
||||
for name, value in [('addons paths', config['addons_path']),
|
||||
for name, value in [('addons paths', openerp.modules.module.ad_paths),
|
||||
('database hostname', config['db_host'] or 'localhost'),
|
||||
('database port', config['db_port'] or '5432'),
|
||||
('database user', config['db_user'])]:
|
||||
|
|
|
@ -1069,33 +1069,12 @@ class DisableCacheMiddleware(object):
|
|||
start_response(status, new_headers)
|
||||
return self.app(environ, start_wrapped)
|
||||
|
||||
def session_path():
|
||||
try:
|
||||
import pwd
|
||||
username = pwd.getpwuid(os.geteuid()).pw_name
|
||||
except ImportError:
|
||||
try:
|
||||
username = getpass.getuser()
|
||||
except Exception:
|
||||
username = "unknown"
|
||||
path = os.path.join(tempfile.gettempdir(), "oe-sessions-" + username)
|
||||
try:
|
||||
os.mkdir(path, 0700)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.EEXIST:
|
||||
# directory exists: ensure it has the correct permissions
|
||||
# this will fail if the directory is not owned by the current user
|
||||
os.chmod(path, 0700)
|
||||
else:
|
||||
raise
|
||||
return path
|
||||
|
||||
class Root(object):
|
||||
"""Root WSGI application for the OpenERP Web Client.
|
||||
"""
|
||||
def __init__(self):
|
||||
# Setup http sessions
|
||||
path = session_path()
|
||||
path = openerp.tools.config.session_dir
|
||||
_logger.debug('HTTP sessions stored in: %s', path)
|
||||
self.session_store = werkzeug.contrib.sessions.FilesystemSessionStore(path, session_class=OpenERPSession)
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -40,9 +40,6 @@ from openerp.tools.safe_eval import safe_eval as eval
|
|||
_logger = logging.getLogger(__name__)
|
||||
_test_logger = logging.getLogger('openerp.tests')
|
||||
|
||||
# addons path ','.joined
|
||||
_ad = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons') # default addons path (base)
|
||||
|
||||
# addons path as a list
|
||||
ad_paths = []
|
||||
|
||||
|
@ -90,8 +87,13 @@ def initialize_sys_path():
|
|||
if ad_paths:
|
||||
return
|
||||
|
||||
ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
ad_paths.append(os.path.abspath(_ad)) # for get_module_path
|
||||
ad_paths = [tools.config.addons_data_dir]
|
||||
ad_paths += map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
|
||||
# add base module path
|
||||
base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons'))
|
||||
ad_paths += [base_path]
|
||||
|
||||
sys.meta_path.append(AddonsImportHook())
|
||||
|
||||
def get_module_path(module, downloaded=False, display_warning=True):
|
||||
|
@ -108,7 +110,7 @@ def get_module_path(module, downloaded=False, display_warning=True):
|
|||
return opj(adp, module)
|
||||
|
||||
if downloaded:
|
||||
return opj(_ad, module)
|
||||
return opj(tools.config.addons_data_dir, module)
|
||||
if display_warning:
|
||||
_logger.warning('module %s: module not found', module)
|
||||
return False
|
||||
|
|
|
@ -32,7 +32,7 @@ RELEASE_LEVELS_DISPLAY = {ALPHA: ALPHA,
|
|||
# (6,1,0,'candidate',2) < (6,1,0,'final',0) < (6,1,2,'final',0)
|
||||
version_info = (8, 0, 0, ALPHA, 1)
|
||||
version = '.'.join(map(str, version_info[:2])) + RELEASE_LEVELS_DISPLAY[version_info[3]] + str(version_info[4] or '')
|
||||
serie = major_version = '.'.join(map(str, version_info[:2]))
|
||||
series = serie = major_version = '.'.join(map(str, version_info[:2]))
|
||||
|
||||
description = 'OpenERP Server'
|
||||
long_desc = '''OpenERP is a complete ERP and CRM. The main features are accounting (analytic
|
||||
|
@ -50,6 +50,6 @@ author = 'OpenERP S.A.'
|
|||
author_email = 'info@openerp.com'
|
||||
license = 'AGPL-3'
|
||||
|
||||
nt_service_name = "openerp-server-" + serie
|
||||
nt_service_name = "openerp-server-" + series
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
|
|
@ -276,15 +276,10 @@ def exp_rename(old_name, new_name):
|
|||
cr.autocommit(True) # avoid transaction block
|
||||
try:
|
||||
cr.execute('ALTER DATABASE "%s" RENAME TO "%s"' % (old_name, new_name))
|
||||
_logger.info('RENAME DB: %s -> %s', old_name, new_name)
|
||||
except Exception, e:
|
||||
_logger.error('RENAME DB: %s -> %s failed:\n%s', old_name, new_name, e)
|
||||
raise Exception("Couldn't rename database %s to %s: %s" % (old_name, new_name, e))
|
||||
else:
|
||||
fs = os.path.join(openerp.tools.config['root_path'], 'filestore')
|
||||
if os.path.exists(os.path.join(fs, old_name)):
|
||||
os.rename(os.path.join(fs, old_name), os.path.join(fs, new_name))
|
||||
|
||||
_logger.info('RENAME DB: %s -> %s', old_name, new_name)
|
||||
return True
|
||||
|
||||
def exp_db_exist(db_name):
|
||||
|
|
|
@ -108,15 +108,14 @@ class AutoReload(object):
|
|||
self.handler = EventHandler(self)
|
||||
self.notifier = pyinotify.Notifier(self.wm, self.handler, timeout=0)
|
||||
mask = pyinotify.IN_MODIFY | pyinotify.IN_CREATE # IN_MOVED_FROM, IN_MOVED_TO ?
|
||||
for path in openerp.tools.config.options["addons_path"].split(','):
|
||||
for path in openerp.modules.modules.ad_paths:
|
||||
_logger.info('Watching addons folder %s', path)
|
||||
self.wm.add_watch(path, mask, rec=True)
|
||||
|
||||
def process_data(self, files):
|
||||
xml_files = [i for i in files if i.endswith('.xml')]
|
||||
addons_path = openerp.tools.config.options["addons_path"].split(',')
|
||||
for i in xml_files:
|
||||
for path in addons_path:
|
||||
for path in openerp.modules.modules.ad_paths:
|
||||
if i.startswith(path):
|
||||
# find out wich addons path the file belongs to
|
||||
# and extract it's module name
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
|
||||
import copy
|
||||
import win32
|
||||
import appdirs
|
||||
from config import config
|
||||
from misc import *
|
||||
from convert import *
|
||||
|
|
|
@ -0,0 +1,477 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2005-2010 ActiveState Software Inc.
|
||||
# Copyright (c) 2013 Eddy Petrișor
|
||||
|
||||
"""Utilities for determining application-specific dirs.
|
||||
|
||||
See <http://github.com/ActiveState/appdirs> for details and usage.
|
||||
"""
|
||||
# Dev Notes:
|
||||
# - MSDN on where to store app data files:
|
||||
# http://support.microsoft.com/default.aspx?scid=kb;en-us;310294#XSLTH3194121123120121120120
|
||||
# - Mac OS X: http://developer.apple.com/documentation/MacOSX/Conceptual/BPFileSystem/index.html
|
||||
# - XDG spec for Un*x: http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
|
||||
__version_info__ = (1, 3, 0)
|
||||
__version__ = '.'.join(map(str, __version_info__))
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
unicode = str
|
||||
|
||||
|
||||
|
||||
def user_data_dir(appname=None, appauthor=None, version=None, roaming=False):
|
||||
r"""Return full path to the user-specific data dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"roaming" (boolean, default False) can be set True to use the Windows
|
||||
roaming appdata directory. That means that for users on a Windows
|
||||
network setup for roaming profiles, this user data will be
|
||||
sync'd on login. See
|
||||
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
|
||||
for a discussion of issues.
|
||||
|
||||
Typical user data directories are:
|
||||
Mac OS X: ~/Library/Application Support/<AppName>
|
||||
Unix: ~/.local/share/<AppName> # or in $XDG_DATA_HOME, if defined
|
||||
Win XP (not roaming): C:\Documents and Settings\<username>\Application Data\<AppAuthor>\<AppName>
|
||||
Win XP (roaming): C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>
|
||||
Win 7 (not roaming): C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>
|
||||
Win 7 (roaming): C:\Users\<username>\AppData\Roaming\<AppAuthor>\<AppName>
|
||||
|
||||
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
|
||||
That means, by deafult "~/.local/share/<AppName>".
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
if appauthor is None:
|
||||
appauthor = appname
|
||||
const = roaming and "CSIDL_APPDATA" or "CSIDL_LOCAL_APPDATA"
|
||||
path = os.path.normpath(_get_win_folder(const))
|
||||
if appname:
|
||||
path = os.path.join(path, appauthor, appname)
|
||||
elif sys.platform == 'darwin':
|
||||
path = os.path.expanduser('~/Library/Application Support/')
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
else:
|
||||
path = os.getenv('XDG_DATA_HOME', os.path.expanduser("~/.local/share"))
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
return path
|
||||
|
||||
|
||||
def site_data_dir(appname=None, appauthor=None, version=None, multipath=False):
|
||||
"""Return full path to the user-shared data dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"multipath" is an optional parameter only applicable to *nix
|
||||
which indicates that the entire list of data dirs should be
|
||||
returned. By default, the first item from XDG_DATA_DIRS is
|
||||
returned, or '/usr/local/share/<AppName>',
|
||||
if XDG_DATA_DIRS is not set
|
||||
|
||||
Typical user data directories are:
|
||||
Mac OS X: /Library/Application Support/<AppName>
|
||||
Unix: /usr/local/share/<AppName> or /usr/share/<AppName>
|
||||
Win XP: C:\Documents and Settings\All Users\Application Data\<AppAuthor>\<AppName>
|
||||
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
|
||||
Win 7: C:\ProgramData\<AppAuthor>\<AppName> # Hidden, but writeable on Win 7.
|
||||
|
||||
For Unix, this is using the $XDG_DATA_DIRS[0] default.
|
||||
|
||||
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
if appauthor is None:
|
||||
appauthor = appname
|
||||
path = os.path.normpath(_get_win_folder("CSIDL_COMMON_APPDATA"))
|
||||
if appname:
|
||||
path = os.path.join(path, appauthor, appname)
|
||||
elif sys.platform == 'darwin':
|
||||
path = os.path.expanduser('/Library/Application Support')
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
else:
|
||||
# XDG default for $XDG_DATA_DIRS
|
||||
# only first, if multipath is False
|
||||
path = os.getenv('XDG_DATA_DIRS',
|
||||
os.pathsep.join(['/usr/local/share', '/usr/share']))
|
||||
pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ]
|
||||
if appname:
|
||||
if version:
|
||||
appname = os.path.join(appname, version)
|
||||
pathlist = [ os.sep.join([x, appname]) for x in pathlist ]
|
||||
|
||||
if multipath:
|
||||
path = os.pathsep.join(pathlist)
|
||||
else:
|
||||
path = pathlist[0]
|
||||
return path
|
||||
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
return path
|
||||
|
||||
|
||||
def user_config_dir(appname=None, appauthor=None, version=None, roaming=False):
|
||||
r"""Return full path to the user-specific config dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"roaming" (boolean, default False) can be set True to use the Windows
|
||||
roaming appdata directory. That means that for users on a Windows
|
||||
network setup for roaming profiles, this user data will be
|
||||
sync'd on login. See
|
||||
<http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx>
|
||||
for a discussion of issues.
|
||||
|
||||
Typical user data directories are:
|
||||
Mac OS X: same as user_data_dir
|
||||
Unix: ~/.config/<AppName> # or in $XDG_CONFIG_HOME, if defined
|
||||
Win *: same as user_data_dir
|
||||
|
||||
For Unix, we follow the XDG spec and support $XDG_DATA_HOME.
|
||||
That means, by deafult "~/.local/share/<AppName>".
|
||||
"""
|
||||
if sys.platform in [ "win32", "darwin" ]:
|
||||
path = user_data_dir(appname, appauthor, None, roaming)
|
||||
else:
|
||||
path = os.getenv('XDG_CONFIG_HOME', os.path.expanduser("~/.config"))
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
return path
|
||||
|
||||
|
||||
def site_config_dir(appname=None, appauthor=None, version=None, multipath=False):
|
||||
"""Return full path to the user-shared data dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"multipath" is an optional parameter only applicable to *nix
|
||||
which indicates that the entire list of config dirs should be
|
||||
returned. By default, the first item from XDG_CONFIG_DIRS is
|
||||
returned, or '/etc/xdg/<AppName>', if XDG_CONFIG_DIRS is not set
|
||||
|
||||
Typical user data directories are:
|
||||
Mac OS X: same as site_data_dir
|
||||
Unix: /etc/xdg/<AppName> or $XDG_CONFIG_DIRS[i]/<AppName> for each value in
|
||||
$XDG_CONFIG_DIRS
|
||||
Win *: same as site_data_dir
|
||||
Vista: (Fail! "C:\ProgramData" is a hidden *system* directory on Vista.)
|
||||
|
||||
For Unix, this is using the $XDG_CONFIG_DIRS[0] default, if multipath=False
|
||||
|
||||
WARNING: Do not use this on Windows. See the Vista-Fail note above for why.
|
||||
"""
|
||||
if sys.platform in [ "win32", "darwin" ]:
|
||||
path = site_data_dir(appname, appauthor)
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
else:
|
||||
# XDG default for $XDG_CONFIG_DIRS
|
||||
# only first, if multipath is False
|
||||
path = os.getenv('XDG_CONFIG_DIRS', '/etc/xdg')
|
||||
pathlist = [ os.path.expanduser(x.rstrip(os.sep)) for x in path.split(os.pathsep) ]
|
||||
if appname:
|
||||
if version:
|
||||
appname = os.path.join(appname, version)
|
||||
pathlist = [ os.sep.join([x, appname]) for x in pathlist ]
|
||||
|
||||
if multipath:
|
||||
path = os.pathsep.join(pathlist)
|
||||
else:
|
||||
path = pathlist[0]
|
||||
return path
|
||||
|
||||
def user_cache_dir(appname=None, appauthor=None, version=None, opinion=True):
|
||||
r"""Return full path to the user-specific cache dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"opinion" (boolean) can be False to disable the appending of
|
||||
"Cache" to the base app data dir for Windows. See
|
||||
discussion below.
|
||||
|
||||
Typical user cache directories are:
|
||||
Mac OS X: ~/Library/Caches/<AppName>
|
||||
Unix: ~/.cache/<AppName> (XDG default)
|
||||
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Cache
|
||||
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Cache
|
||||
|
||||
On Windows the only suggestion in the MSDN docs is that local settings go in
|
||||
the `CSIDL_LOCAL_APPDATA` directory. This is identical to the non-roaming
|
||||
app data dir (the default returned by `user_data_dir` above). Apps typically
|
||||
put cache data somewhere *under* the given dir here. Some examples:
|
||||
...\Mozilla\Firefox\Profiles\<ProfileName>\Cache
|
||||
...\Acme\SuperApp\Cache\1.0
|
||||
OPINION: This function appends "Cache" to the `CSIDL_LOCAL_APPDATA` value.
|
||||
This can be disabled with the `opinion=False` option.
|
||||
"""
|
||||
if sys.platform == "win32":
|
||||
if appauthor is None:
|
||||
appauthor = appname
|
||||
path = os.path.normpath(_get_win_folder("CSIDL_LOCAL_APPDATA"))
|
||||
if appname:
|
||||
path = os.path.join(path, appauthor, appname)
|
||||
if opinion:
|
||||
path = os.path.join(path, "Cache")
|
||||
elif sys.platform == 'darwin':
|
||||
path = os.path.expanduser('~/Library/Caches')
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
else:
|
||||
path = os.getenv('XDG_CACHE_HOME', os.path.expanduser('~/.cache'))
|
||||
if appname:
|
||||
path = os.path.join(path, appname)
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
return path
|
||||
|
||||
def user_log_dir(appname=None, appauthor=None, version=None, opinion=True):
|
||||
r"""Return full path to the user-specific log dir for this application.
|
||||
|
||||
"appname" is the name of application.
|
||||
If None, just the system directory is returned.
|
||||
"appauthor" (only required and used on Windows) is the name of the
|
||||
appauthor or distributing body for this application. Typically
|
||||
it is the owning company name. This falls back to appname.
|
||||
"version" is an optional version path element to append to the
|
||||
path. You might want to use this if you want multiple versions
|
||||
of your app to be able to run independently. If used, this
|
||||
would typically be "<major>.<minor>".
|
||||
Only applied when appname is present.
|
||||
"opinion" (boolean) can be False to disable the appending of
|
||||
"Logs" to the base app data dir for Windows, and "log" to the
|
||||
base cache dir for Unix. See discussion below.
|
||||
|
||||
Typical user cache directories are:
|
||||
Mac OS X: ~/Library/Logs/<AppName>
|
||||
Unix: ~/.cache/<AppName>/log # or under $XDG_CACHE_HOME if defined
|
||||
Win XP: C:\Documents and Settings\<username>\Local Settings\Application Data\<AppAuthor>\<AppName>\Logs
|
||||
Vista: C:\Users\<username>\AppData\Local\<AppAuthor>\<AppName>\Logs
|
||||
|
||||
On Windows the only suggestion in the MSDN docs is that local settings
|
||||
go in the `CSIDL_LOCAL_APPDATA` directory. (Note: I'm interested in
|
||||
examples of what some windows apps use for a logs dir.)
|
||||
|
||||
OPINION: This function appends "Logs" to the `CSIDL_LOCAL_APPDATA`
|
||||
value for Windows and appends "log" to the user cache dir for Unix.
|
||||
This can be disabled with the `opinion=False` option.
|
||||
"""
|
||||
if sys.platform == "darwin":
|
||||
path = os.path.join(
|
||||
os.path.expanduser('~/Library/Logs'),
|
||||
appname)
|
||||
elif sys.platform == "win32":
|
||||
path = user_data_dir(appname, appauthor, version); version=False
|
||||
if opinion:
|
||||
path = os.path.join(path, "Logs")
|
||||
else:
|
||||
path = user_cache_dir(appname, appauthor, version); version=False
|
||||
if opinion:
|
||||
path = os.path.join(path, "log")
|
||||
if appname and version:
|
||||
path = os.path.join(path, version)
|
||||
return path
|
||||
|
||||
|
||||
class AppDirs(object):
|
||||
"""Convenience wrapper for getting application dirs."""
|
||||
def __init__(self, appname, appauthor=None, version=None,
|
||||
roaming=False, multipath=False):
|
||||
self.appname = appname
|
||||
self.appauthor = appauthor
|
||||
self.version = version
|
||||
self.roaming = roaming
|
||||
self.multipath = multipath
|
||||
@property
|
||||
def user_data_dir(self):
|
||||
return user_data_dir(self.appname, self.appauthor,
|
||||
version=self.version, roaming=self.roaming)
|
||||
@property
|
||||
def site_data_dir(self):
|
||||
return site_data_dir(self.appname, self.appauthor,
|
||||
version=self.version, multipath=self.multipath)
|
||||
@property
|
||||
def user_config_dir(self):
|
||||
return user_config_dir(self.appname, self.appauthor,
|
||||
version=self.version, roaming=self.roaming)
|
||||
@property
|
||||
def site_config_dir(self):
|
||||
return site_data_dir(self.appname, self.appauthor,
|
||||
version=self.version, multipath=self.multipath)
|
||||
@property
|
||||
def user_cache_dir(self):
|
||||
return user_cache_dir(self.appname, self.appauthor,
|
||||
version=self.version)
|
||||
@property
|
||||
def user_log_dir(self):
|
||||
return user_log_dir(self.appname, self.appauthor,
|
||||
version=self.version)
|
||||
|
||||
|
||||
|
||||
|
||||
#---- internal support stuff
|
||||
|
||||
def _get_win_folder_from_registry(csidl_name):
|
||||
"""This is a fallback technique at best. I'm not sure if using the
|
||||
registry for this guarantees us the correct answer for all CSIDL_*
|
||||
names.
|
||||
"""
|
||||
import _winreg
|
||||
|
||||
shell_folder_name = {
|
||||
"CSIDL_APPDATA": "AppData",
|
||||
"CSIDL_COMMON_APPDATA": "Common AppData",
|
||||
"CSIDL_LOCAL_APPDATA": "Local AppData",
|
||||
}[csidl_name]
|
||||
|
||||
key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
|
||||
r"Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders")
|
||||
dir, type = _winreg.QueryValueEx(key, shell_folder_name)
|
||||
return dir
|
||||
|
||||
def _get_win_folder_with_pywin32(csidl_name):
|
||||
from win32com.shell import shellcon, shell
|
||||
dir = shell.SHGetFolderPath(0, getattr(shellcon, csidl_name), 0, 0)
|
||||
# Try to make this a unicode path because SHGetFolderPath does
|
||||
# not return unicode strings when there is unicode data in the
|
||||
# path.
|
||||
try:
|
||||
dir = unicode(dir)
|
||||
|
||||
# Downgrade to short path name if have highbit chars. See
|
||||
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
|
||||
has_high_char = False
|
||||
for c in dir:
|
||||
if ord(c) > 255:
|
||||
has_high_char = True
|
||||
break
|
||||
if has_high_char:
|
||||
try:
|
||||
import win32api
|
||||
dir = win32api.GetShortPathName(dir)
|
||||
except ImportError:
|
||||
pass
|
||||
except UnicodeError:
|
||||
pass
|
||||
return dir
|
||||
|
||||
def _get_win_folder_with_ctypes(csidl_name):
|
||||
import ctypes
|
||||
|
||||
csidl_const = {
|
||||
"CSIDL_APPDATA": 26,
|
||||
"CSIDL_COMMON_APPDATA": 35,
|
||||
"CSIDL_LOCAL_APPDATA": 28,
|
||||
}[csidl_name]
|
||||
|
||||
buf = ctypes.create_unicode_buffer(1024)
|
||||
ctypes.windll.shell32.SHGetFolderPathW(None, csidl_const, None, 0, buf)
|
||||
|
||||
# Downgrade to short path name if have highbit chars. See
|
||||
# <http://bugs.activestate.com/show_bug.cgi?id=85099>.
|
||||
has_high_char = False
|
||||
for c in buf:
|
||||
if ord(c) > 255:
|
||||
has_high_char = True
|
||||
break
|
||||
if has_high_char:
|
||||
buf2 = ctypes.create_unicode_buffer(1024)
|
||||
if ctypes.windll.kernel32.GetShortPathNameW(buf.value, buf2, 1024):
|
||||
buf = buf2
|
||||
|
||||
return buf.value
|
||||
|
||||
if sys.platform == "win32":
|
||||
try:
|
||||
import win32com.shell
|
||||
_get_win_folder = _get_win_folder_with_pywin32
|
||||
except ImportError:
|
||||
try:
|
||||
import ctypes
|
||||
_get_win_folder = _get_win_folder_with_ctypes
|
||||
except ImportError:
|
||||
_get_win_folder = _get_win_folder_from_registry
|
||||
|
||||
|
||||
|
||||
#---- self test code
|
||||
|
||||
if __name__ == "__main__":
|
||||
appname = "MyApp"
|
||||
appauthor = "MyCompany"
|
||||
|
||||
props = ("user_data_dir", "site_data_dir",
|
||||
"user_config_dir", "site_config_dir",
|
||||
"user_cache_dir", "user_log_dir")
|
||||
|
||||
print("-- app dirs (with optional 'version')")
|
||||
dirs = AppDirs(appname, appauthor, version="1.0")
|
||||
for prop in props:
|
||||
print("%s: %s" % (prop, getattr(dirs, prop)))
|
||||
|
||||
print("\n-- app dirs (without optional 'version')")
|
||||
dirs = AppDirs(appname, appauthor)
|
||||
for prop in props:
|
||||
print("%s: %s" % (prop, getattr(dirs, prop)))
|
||||
|
||||
print("\n-- app dirs (without optional 'appauthor')")
|
||||
dirs = AppDirs(appname)
|
||||
for prop in props:
|
||||
print("%s: %s" % (prop, getattr(dirs, prop)))
|
||||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2012 OpenERP s.a. (<http://openerp.com>).
|
||||
# Copyright (C) 2010-2014 OpenERP s.a. (<http://openerp.com>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -29,6 +29,7 @@ import openerp.conf
|
|||
import openerp.loglevels as loglevels
|
||||
import logging
|
||||
import openerp.release as release
|
||||
import appdirs
|
||||
|
||||
class MyOption (optparse.Option, object):
|
||||
""" optparse Option with two additional attributes.
|
||||
|
@ -59,6 +60,9 @@ def check_ssl():
|
|||
|
||||
DEFAULT_LOG_HANDLER = [':INFO']
|
||||
|
||||
def _get_default_datadir():
|
||||
return appdirs.user_data_dir(appname='OpenERP', appauthor=release.author)
|
||||
|
||||
class configmanager(object):
|
||||
def __init__(self, fname=None):
|
||||
# Options not exposed on the command line. Command line options will be added
|
||||
|
@ -110,6 +114,9 @@ class configmanager(object):
|
|||
help="specify additional addons paths (separated by commas).",
|
||||
action="callback", callback=self._check_addons_path, nargs=1, type="string")
|
||||
group.add_option("--load", dest="server_wide_modules", help="Comma-separated list of server-wide modules default=web")
|
||||
|
||||
group.add_option("-D", "--data-dir", dest="data_dir", my_default=_get_default_datadir(),
|
||||
help="Directory where to store OpenERP data")
|
||||
parser.add_option_group(group)
|
||||
|
||||
# XML-RPC / HTTP
|
||||
|
@ -356,6 +363,7 @@ class configmanager(object):
|
|||
# (../etc from the server)
|
||||
# if the server is run by an unprivileged user, he has to specify location of a config file where he has the rights to write,
|
||||
# else he won't be able to save the configurations, or even to start the server...
|
||||
# TODO use appdirs
|
||||
if os.name == 'nt':
|
||||
rcfilepath = os.path.join(os.path.abspath(os.path.dirname(sys.argv[0])), 'openerp-server.conf')
|
||||
else:
|
||||
|
@ -366,7 +374,6 @@ class configmanager(object):
|
|||
or os.environ.get('OPENERP_SERVER') or rcfilepath)
|
||||
self.load()
|
||||
|
||||
|
||||
# Verify that we want to log or not, if not the output will go to stdout
|
||||
if self.options['logfile'] in ('None', 'False'):
|
||||
self.options['logfile'] = False
|
||||
|
@ -395,7 +402,6 @@ class configmanager(object):
|
|||
elif isinstance(self.options[arg], basestring) and self.casts[arg].type in optparse.Option.TYPE_CHECKER:
|
||||
self.options[arg] = optparse.Option.TYPE_CHECKER[self.casts[arg].type](self.casts[arg], arg, self.options[arg])
|
||||
|
||||
|
||||
if isinstance(self.options['log_handler'], basestring):
|
||||
self.options['log_handler'] = self.options['log_handler'].split(',')
|
||||
|
||||
|
@ -407,7 +413,8 @@ class configmanager(object):
|
|||
'list_db', 'xmlrpcs', 'proxy_mode',
|
||||
'test_file', 'test_enable', 'test_commit', 'test_report_directory',
|
||||
'osv_memory_count_limit', 'osv_memory_age_limit', 'max_cron_threads', 'unaccent',
|
||||
'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request', 'auto_reload'
|
||||
'workers', 'limit_memory_hard', 'limit_memory_soft', 'limit_time_cpu', 'limit_time_real', 'limit_request',
|
||||
'auto_reload', 'data_dir',
|
||||
]
|
||||
|
||||
for arg in keys:
|
||||
|
@ -625,6 +632,24 @@ class configmanager(object):
|
|||
def __getitem__(self, key):
|
||||
return self.options[key]
|
||||
|
||||
@property
|
||||
def addons_data_dir(self):
|
||||
d = os.path.join(self['data_dir'], 'addons', release.series)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d, 0700)
|
||||
else:
|
||||
os.chmod(d, 0700)
|
||||
return d
|
||||
|
||||
@property
|
||||
def session_dir(self):
|
||||
d = os.path.join(self['data_dir'], 'sessions', release.series)
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d, 0700)
|
||||
else:
|
||||
os.chmod(d, 0700)
|
||||
return d
|
||||
|
||||
config = configmanager()
|
||||
|
||||
|
||||
|
|
|
@ -778,49 +778,32 @@ def trans_generate(lang, modules, cr):
|
|||
if model_obj._sql_constraints:
|
||||
push_local_constraints(module, model_obj, 'sql_constraints')
|
||||
|
||||
def get_module_from_path(path, mod_paths=None):
|
||||
if not mod_paths:
|
||||
# First, construct a list of possible paths
|
||||
def_path = os.path.abspath(os.path.join(config.config['root_path'], 'addons')) # default addons path (base)
|
||||
ad_paths= map(lambda m: os.path.abspath(m.strip()),config.config['addons_path'].split(','))
|
||||
mod_paths=[def_path]
|
||||
for adp in ad_paths:
|
||||
mod_paths.append(adp)
|
||||
if not os.path.isabs(adp):
|
||||
mod_paths.append(adp)
|
||||
elif adp.startswith(def_path):
|
||||
mod_paths.append(adp[len(def_path)+1:])
|
||||
for mp in mod_paths:
|
||||
if path.startswith(mp) and (os.path.dirname(path) != mp):
|
||||
path = path[len(mp)+1:]
|
||||
return path.split(os.path.sep)[0]
|
||||
return 'base' # files that are not in a module are considered as being in 'base' module
|
||||
|
||||
modobj = registry['ir.module.module']
|
||||
installed_modids = modobj.search(cr, uid, [('state', '=', 'installed')])
|
||||
installed_modules = map(lambda m: m['name'], modobj.read(cr, uid, installed_modids, ['name']))
|
||||
|
||||
root_path = os.path.join(config.config['root_path'], 'addons')
|
||||
|
||||
apaths = map(os.path.abspath, map(str.strip, config.config['addons_path'].split(',')))
|
||||
if root_path in apaths:
|
||||
path_list = apaths
|
||||
else :
|
||||
path_list = [root_path,] + apaths
|
||||
|
||||
path_list = list(openerp.modules.module.ad_paths)
|
||||
# Also scan these non-addon paths
|
||||
for bin_path in ['osv', 'report' ]:
|
||||
path_list.append(os.path.join(config.config['root_path'], bin_path))
|
||||
|
||||
_logger.debug("Scanning modules at paths: ", path_list)
|
||||
|
||||
mod_paths = []
|
||||
mod_paths = list(path_list)
|
||||
|
||||
def get_module_from_path(path):
|
||||
for mp in mod_paths:
|
||||
if path.startswith(mp) and (os.path.dirname(path) != mp):
|
||||
path = path[len(mp)+1:]
|
||||
return path.split(os.path.sep)[0]
|
||||
return 'base' # files that are not in a module are considered as being in 'base' module
|
||||
|
||||
def verified_module_filepaths(fname, path, root):
|
||||
fabsolutepath = join(root, fname)
|
||||
frelativepath = fabsolutepath[len(path):]
|
||||
display_path = "addons%s" % frelativepath
|
||||
module = get_module_from_path(fabsolutepath, mod_paths=mod_paths)
|
||||
module = get_module_from_path(fabsolutepath)
|
||||
if ('all' in modules or module in modules) and module in installed_modules:
|
||||
return module, fabsolutepath, frelativepath, display_path
|
||||
return None, None, None, None
|
||||
|
|
Loading…
Reference in New Issue