[MERGE] merged with trunk development branch
bzr revid: hmo@tinyerp.com-20110613051813-g11nroav1l93er22
This commit is contained in:
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Vendored
+5
-5
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-server\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2009-08-24 22:41+0300\n"
|
||||
"PO-Revision-Date: 2010-01-29 05:27+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"PO-Revision-Date: 2011-05-17 20:53+0000\n"
|
||||
"Last-Translator: Ayhan KIZILTAN <Unknown>\n"
|
||||
"Language-Team: Turkish <tr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-04-19 04:50+0000\n"
|
||||
"X-Generator: Launchpad (build 12758)\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-18 06:22+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. Type: string
|
||||
#. Description
|
||||
@@ -38,4 +38,4 @@ msgstr ""
|
||||
#. Description
|
||||
#: ../openerp-server.templates:1001
|
||||
msgid "Please choose that account's username."
|
||||
msgstr "Lütfen bu hesabın kullanıcı adını seçiniz."
|
||||
msgstr "Lütfen o hesabın kullanıcı adını seçiniz."
|
||||
|
||||
+62
-83
@@ -39,10 +39,11 @@ import signal
|
||||
import sys
|
||||
import threading
|
||||
import traceback
|
||||
import time
|
||||
|
||||
import openerp.release as release
|
||||
__author__ = release.author
|
||||
__version__ = release.version
|
||||
import openerp
|
||||
__author__ = openerp.release.author
|
||||
__version__ = openerp.release.version
|
||||
|
||||
if os.name == 'posix':
|
||||
import pwd
|
||||
@@ -53,80 +54,58 @@ if os.name == 'posix':
|
||||
sys.exit(1)
|
||||
|
||||
#-----------------------------------------------------------------------
|
||||
# import the tools module so that the commandline parameters are parsed
|
||||
# parse the command line
|
||||
#-----------------------------------------------------------------------
|
||||
import openerp.tools as tools
|
||||
tools.config.parse_config(sys.argv[1:])
|
||||
openerp.tools.config.parse_config(sys.argv[1:])
|
||||
config = openerp.tools.config
|
||||
|
||||
#----------------------------------------------------------
|
||||
# get logger
|
||||
#----------------------------------------------------------
|
||||
import openerp.netsvc as netsvc
|
||||
netsvc.init_logger()
|
||||
openerp.netsvc.init_logger()
|
||||
logger = logging.getLogger('server')
|
||||
|
||||
logger.info("OpenERP version - %s", release.version)
|
||||
for name, value in [('addons_path', tools.config['addons_path']),
|
||||
('database hostname', tools.config['db_host'] or 'localhost'),
|
||||
('database port', tools.config['db_port'] or '5432'),
|
||||
('database user', tools.config['db_user'])]:
|
||||
logger.info("OpenERP version - %s", __version__)
|
||||
for name, value in [('addons_path', config['addons_path']),
|
||||
('database hostname', config['db_host'] or 'localhost'),
|
||||
('database port', config['db_port'] or '5432'),
|
||||
('database user', config['db_user'])]:
|
||||
logger.info("%s - %s", name, value)
|
||||
|
||||
# Don't allow if the connection to PostgreSQL done by postgres user
|
||||
if tools.config['db_user'] == 'postgres':
|
||||
if config['db_user'] == 'postgres':
|
||||
logger.error("Connecting to the database as 'postgres' user is forbidden, as it present major security issues. Shutting down.")
|
||||
sys.exit(1)
|
||||
|
||||
import time
|
||||
|
||||
#----------------------------------------------------------
|
||||
# init net service
|
||||
#----------------------------------------------------------
|
||||
logger.info('initialising distributed objects services')
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# connect to the database and initialize it with base if needed
|
||||
#---------------------------------------------------------------
|
||||
import openerp.pooler as pooler
|
||||
|
||||
#----------------------------------------------------------
|
||||
# import basic modules
|
||||
#----------------------------------------------------------
|
||||
import openerp.osv as osv
|
||||
import openerp.workflow as workflow
|
||||
import openerp.report as report
|
||||
import openerp.service as service
|
||||
|
||||
#----------------------------------------------------------
|
||||
# import addons
|
||||
#----------------------------------------------------------
|
||||
|
||||
import openerp.addons as addons
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Load and update databases if requested
|
||||
#----------------------------------------------------------
|
||||
|
||||
import openerp.service.http_server as service_http_server
|
||||
if not ( config["stop_after_init"] or \
|
||||
config["translate_in"] or \
|
||||
config["translate_out"] ):
|
||||
openerp.osv.osv.start_object_proxy()
|
||||
openerp.service.web_services.start_web_services()
|
||||
http_server = openerp.service.http_server
|
||||
netrpc_server = openerp.service.netrpc_server
|
||||
http_server.init_servers()
|
||||
http_server.init_xmlrpc()
|
||||
http_server.init_static_http()
|
||||
netrpc_server.init_servers()
|
||||
|
||||
if not ( tools.config["stop_after_init"] or \
|
||||
tools.config["translate_in"] or \
|
||||
tools.config["translate_out"] ):
|
||||
service_http_server.init_servers()
|
||||
service_http_server.init_xmlrpc()
|
||||
service_http_server.init_static_http()
|
||||
|
||||
import openerp.service.netrpc_server as service_netrpc_server
|
||||
service_netrpc_server.init_servers()
|
||||
|
||||
if tools.config['db_name']:
|
||||
for dbname in tools.config['db_name'].split(','):
|
||||
db,pool = pooler.get_db_and_pool(dbname, update_module=tools.config['init'] or tools.config['update'], pooljobs=False)
|
||||
if config['db_name']:
|
||||
for dbname in config['db_name'].split(','):
|
||||
db, pool = openerp.pooler.get_db_and_pool(dbname, update_module=config['init'] or config['update'], pooljobs=False)
|
||||
cr = db.cursor()
|
||||
|
||||
if tools.config["test_file"]:
|
||||
logger.info('loading test file %s', tools.config["test_file"])
|
||||
tools.convert_yaml_import(cr, 'base', file(tools.config["test_file"]), {}, 'test', True)
|
||||
if config["test_file"]:
|
||||
logger.info('loading test file %s', config["test_file"])
|
||||
openerp.tools.convert_yaml_import(cr, 'base', file(config["test_file"]), {}, 'test', True)
|
||||
cr.rollback()
|
||||
|
||||
pool.get('ir.cron')._poolJobs(db.dbname)
|
||||
@@ -136,35 +115,33 @@ if tools.config['db_name']:
|
||||
#----------------------------------------------------------
|
||||
# translation stuff
|
||||
#----------------------------------------------------------
|
||||
if tools.config["translate_out"]:
|
||||
import csv
|
||||
|
||||
if tools.config["language"]:
|
||||
msg = "language %s" % (tools.config["language"],)
|
||||
if config["translate_out"]:
|
||||
if config["language"]:
|
||||
msg = "language %s" % (config["language"],)
|
||||
else:
|
||||
msg = "new language"
|
||||
logger.info('writing translation file for %s to %s', msg, tools.config["translate_out"])
|
||||
logger.info('writing translation file for %s to %s', msg, config["translate_out"])
|
||||
|
||||
fileformat = os.path.splitext(tools.config["translate_out"])[-1][1:].lower()
|
||||
buf = file(tools.config["translate_out"], "w")
|
||||
dbname = tools.config['db_name']
|
||||
cr = pooler.get_db(dbname).cursor()
|
||||
tools.trans_export(tools.config["language"], tools.config["translate_modules"] or ["all"], buf, fileformat, cr)
|
||||
fileformat = os.path.splitext(config["translate_out"])[-1][1:].lower()
|
||||
buf = file(config["translate_out"], "w")
|
||||
dbname = config['db_name']
|
||||
cr = openerp.pooler.get_db(dbname).cursor()
|
||||
openerp.tools.trans_export(config["language"], config["translate_modules"] or ["all"], buf, fileformat, cr)
|
||||
cr.close()
|
||||
buf.close()
|
||||
|
||||
logger.info('translation file written successfully')
|
||||
sys.exit(0)
|
||||
|
||||
if tools.config["translate_in"]:
|
||||
context = {'overwrite': tools.config["overwrite_existing_translations"]}
|
||||
dbname = tools.config['db_name']
|
||||
cr = pooler.get_db(dbname).cursor()
|
||||
tools.trans_load(cr,
|
||||
tools.config["translate_in"],
|
||||
tools.config["language"],
|
||||
if config["translate_in"]:
|
||||
context = {'overwrite': config["overwrite_existing_translations"]}
|
||||
dbname = config['db_name']
|
||||
cr = openerp.pooler.get_db(dbname).cursor()
|
||||
openerp.tools.trans_load(cr,
|
||||
config["translate_in"],
|
||||
config["language"],
|
||||
context=context)
|
||||
tools.trans_update_res_ids(cr)
|
||||
openerp.tools.trans_update_res_ids(cr)
|
||||
cr.commit()
|
||||
cr.close()
|
||||
sys.exit(0)
|
||||
@@ -172,9 +149,10 @@ if tools.config["translate_in"]:
|
||||
#----------------------------------------------------------------------------------
|
||||
# if we don't want the server to continue to run after initialization, we quit here
|
||||
#----------------------------------------------------------------------------------
|
||||
if tools.config["stop_after_init"]:
|
||||
if config["stop_after_init"]:
|
||||
sys.exit(0)
|
||||
|
||||
openerp.netsvc.start_agent()
|
||||
|
||||
#----------------------------------------------------------
|
||||
# Launch Servers
|
||||
@@ -186,15 +164,16 @@ SIGNALS = dict(
|
||||
[(getattr(signal, sign), sign) for sign in LST_SIGNALS]
|
||||
)
|
||||
|
||||
netsvc.quit_signals_received = 0
|
||||
quit_signals_received = 0
|
||||
|
||||
def handler(signum, frame):
|
||||
"""
|
||||
:param signum: the signal number
|
||||
:param frame: the interrupted stack frame or None
|
||||
"""
|
||||
netsvc.quit_signals_received += 1
|
||||
if netsvc.quit_signals_received > 1:
|
||||
global quit_signals_received
|
||||
quit_signals_received += 1
|
||||
if quit_signals_received > 1:
|
||||
sys.stderr.write("Forced shutdown.\n")
|
||||
os._exit(0)
|
||||
|
||||
@@ -219,10 +198,10 @@ if os.name == 'posix':
|
||||
signal.signal(signal.SIGQUIT, dumpstacks)
|
||||
|
||||
def quit():
|
||||
netsvc.Agent.quit()
|
||||
netsvc.Server.quitAll()
|
||||
if tools.config['pidfile']:
|
||||
os.unlink(tools.config['pidfile'])
|
||||
openerp.netsvc.Agent.quit()
|
||||
openerp.netsvc.Server.quitAll()
|
||||
if config['pidfile']:
|
||||
os.unlink(config['pidfile'])
|
||||
logger = logging.getLogger('shutdown')
|
||||
logger.info("Initiating OpenERP Server shutdown")
|
||||
logger.info("Hit CTRL-C again or send a second signal to immediately terminate the server...")
|
||||
@@ -240,17 +219,17 @@ def quit():
|
||||
time.sleep(0.05)
|
||||
sys.exit(0)
|
||||
|
||||
if tools.config['pidfile']:
|
||||
fd = open(tools.config['pidfile'], 'w')
|
||||
if config['pidfile']:
|
||||
fd = open(config['pidfile'], 'w')
|
||||
pidtext = "%d" % (os.getpid())
|
||||
fd.write(pidtext)
|
||||
fd.close()
|
||||
|
||||
netsvc.Server.startAll()
|
||||
openerp.netsvc.Server.startAll()
|
||||
|
||||
logger.info('OpenERP server is running, waiting for connections...')
|
||||
|
||||
while netsvc.quit_signals_received == 0:
|
||||
while quit_signals_received == 0:
|
||||
time.sleep(60)
|
||||
|
||||
quit()
|
||||
|
||||
+6
-1
@@ -19,9 +19,14 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" OpenERP core library.
|
||||
|
||||
"""
|
||||
|
||||
import addons
|
||||
import ir
|
||||
import conf
|
||||
import loglevels
|
||||
import modules
|
||||
import netsvc
|
||||
import osv
|
||||
import pooler
|
||||
|
||||
+8
-952
@@ -20,960 +20,16 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import itertools
|
||||
import zipimport
|
||||
""" Addons module.
|
||||
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
import openerp.pooler as pooler
|
||||
from openerp.tools.translate import _
|
||||
This module only serves to contain OpenERP addons. For the code to
|
||||
manage those addons, see openerp.modules. This module conveniently
|
||||
reexports some symbols from openerp.modules. Importing them from here
|
||||
is deprecated.
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
|
||||
import zipfile
|
||||
import openerp.release as release
|
||||
|
||||
import re
|
||||
import base64
|
||||
from zipfile import PyZipFile, ZIP_DEFLATED
|
||||
from cStringIO import StringIO
|
||||
|
||||
import logging
|
||||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
_ad = os.path.dirname(__file__) # default addons path (base)
|
||||
ad_paths = []
|
||||
|
||||
# Modules already loaded
|
||||
loaded = []
|
||||
|
||||
def initialize_sys_path():
|
||||
global ad_paths
|
||||
|
||||
if ad_paths:
|
||||
return
|
||||
|
||||
ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
|
||||
sys.path.insert(1, _ad)
|
||||
|
||||
ad_cnt=1
|
||||
for adp in ad_paths:
|
||||
if adp != _ad:
|
||||
sys.path.insert(ad_cnt, adp)
|
||||
ad_cnt+=1
|
||||
|
||||
ad_paths.append(_ad) # for get_module_path
|
||||
|
||||
class Graph(dict):
|
||||
|
||||
def addNode(self, name, deps):
|
||||
max_depth, father = 0, None
|
||||
for n in [Node(x, self) for x in deps]:
|
||||
if n.depth >= max_depth:
|
||||
father = n
|
||||
max_depth = n.depth
|
||||
if father:
|
||||
father.addChild(name)
|
||||
else:
|
||||
Node(name, self)
|
||||
|
||||
def update_from_db(self, cr):
|
||||
if not len(self):
|
||||
return
|
||||
# update the graph with values from the database (if exist)
|
||||
## First, we set the default values for each package in graph
|
||||
additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
|
||||
## Then we get the values from the database
|
||||
cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
|
||||
' FROM ir_module_module'
|
||||
' WHERE name IN %s',(tuple(additional_data),)
|
||||
)
|
||||
|
||||
## and we update the default values with values from the database
|
||||
additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
|
||||
|
||||
for package in self.values():
|
||||
for k, v in additional_data[package.name].items():
|
||||
setattr(package, k, v)
|
||||
|
||||
def __iter__(self):
|
||||
level = 0
|
||||
done = set(self.keys())
|
||||
while done:
|
||||
level_modules = [(name, module) for name, module in self.items() if module.depth==level]
|
||||
for name, module in level_modules:
|
||||
done.remove(name)
|
||||
yield module
|
||||
level += 1
|
||||
|
||||
class Singleton(object):
|
||||
def __new__(cls, name, graph):
|
||||
if name in graph:
|
||||
inst = graph[name]
|
||||
else:
|
||||
inst = object.__new__(cls)
|
||||
inst.name = name
|
||||
graph[name] = inst
|
||||
return inst
|
||||
|
||||
|
||||
class Node(Singleton):
|
||||
|
||||
def __init__(self, name, graph):
|
||||
self.graph = graph
|
||||
if not hasattr(self, 'children'):
|
||||
self.children = []
|
||||
if not hasattr(self, 'depth'):
|
||||
self.depth = 0
|
||||
|
||||
def addChild(self, name):
|
||||
node = Node(name, self.graph)
|
||||
node.depth = self.depth + 1
|
||||
if node not in self.children:
|
||||
self.children.append(node)
|
||||
for attr in ('init', 'update', 'demo'):
|
||||
if hasattr(self, attr):
|
||||
setattr(node, attr, True)
|
||||
self.children.sort(lambda x, y: cmp(x.name, y.name))
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
super(Singleton, self).__setattr__(name, value)
|
||||
if name in ('init', 'update', 'demo'):
|
||||
tools.config[name][self.name] = 1
|
||||
for child in self.children:
|
||||
setattr(child, name, value)
|
||||
if name == 'depth':
|
||||
for child in self.children:
|
||||
setattr(child, name, value + 1)
|
||||
|
||||
def __iter__(self):
|
||||
return itertools.chain(iter(self.children), *map(iter, self.children))
|
||||
|
||||
def __str__(self):
|
||||
return self._pprint()
|
||||
|
||||
def _pprint(self, depth=0):
|
||||
s = '%s\n' % self.name
|
||||
for c in self.children:
|
||||
s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
|
||||
return s
|
||||
|
||||
|
||||
def get_module_path(module, downloaded=False):
|
||||
"""Return the path of the given module."""
|
||||
initialize_sys_path()
|
||||
for adp in ad_paths:
|
||||
if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
|
||||
return opj(adp, module)
|
||||
|
||||
if downloaded:
|
||||
return opj(_ad, module)
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
|
||||
return False
|
||||
|
||||
|
||||
def get_module_filetree(module, dir='.'):
|
||||
path = get_module_path(module)
|
||||
if not path:
|
||||
return False
|
||||
|
||||
dir = os.path.normpath(dir)
|
||||
if dir == '.':
|
||||
dir = ''
|
||||
if dir.startswith('..') or (dir and dir[0] == '/'):
|
||||
raise Exception('Cannot access file outside the module')
|
||||
|
||||
if not os.path.isdir(path):
|
||||
# zipmodule
|
||||
zip = zipfile.ZipFile(path + ".zip")
|
||||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
else:
|
||||
files = osutil.listdir(path, True)
|
||||
|
||||
tree = {}
|
||||
for f in files:
|
||||
if not f.startswith(dir):
|
||||
continue
|
||||
|
||||
if dir:
|
||||
f = f[len(dir)+int(not dir.endswith('/')):]
|
||||
lst = f.split(os.sep)
|
||||
current = tree
|
||||
while len(lst) != 1:
|
||||
current = current.setdefault(lst.pop(0), {})
|
||||
current[lst.pop(0)] = None
|
||||
|
||||
return tree
|
||||
|
||||
def zip_directory(directory, b64enc=True, src=True):
|
||||
"""Compress a directory
|
||||
|
||||
@param directory: The directory to compress
|
||||
@param base64enc: if True the function will encode the zip file with base64
|
||||
@param src: Integrate the source files
|
||||
|
||||
@return: a string containing the zip file
|
||||
"""
|
||||
|
||||
RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
|
||||
|
||||
def _zippy(archive, path, src=True):
|
||||
path = os.path.abspath(path)
|
||||
base = os.path.basename(path)
|
||||
for f in osutil.listdir(path, True):
|
||||
bf = os.path.basename(f)
|
||||
if not RE_exclude.search(bf) and (src or bf in ('__openerp__.py', '__terp__.py') or not bf.endswith('.py')):
|
||||
archive.write(os.path.join(path, f), os.path.join(base, f))
|
||||
|
||||
archname = StringIO()
|
||||
archive = PyZipFile(archname, "w", ZIP_DEFLATED)
|
||||
|
||||
# for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
|
||||
directory = tools.ustr(directory).encode('utf-8')
|
||||
|
||||
archive.writepy(directory)
|
||||
_zippy(archive, directory, src=src)
|
||||
archive.close()
|
||||
archive_data = archname.getvalue()
|
||||
archname.close()
|
||||
|
||||
if b64enc:
|
||||
return base64.encodestring(archive_data)
|
||||
|
||||
return archive_data
|
||||
|
||||
def get_module_as_zip(modulename, b64enc=True, src=True):
|
||||
"""Generate a module as zip file with the source or not and can do a base64 encoding
|
||||
|
||||
@param modulename: The module name
|
||||
@param b64enc: if True the function will encode the zip file with base64
|
||||
@param src: Integrate the source files
|
||||
|
||||
@return: a stream to store in a file-like object
|
||||
"""
|
||||
|
||||
ap = get_module_path(str(modulename))
|
||||
if not ap:
|
||||
raise Exception('Unable to find path for module %s' % modulename)
|
||||
|
||||
ap = ap.encode('utf8')
|
||||
if os.path.isfile(ap + '.zip'):
|
||||
val = file(ap + '.zip', 'rb').read()
|
||||
if b64enc:
|
||||
val = base64.encodestring(val)
|
||||
else:
|
||||
val = zip_directory(ap, b64enc, src)
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def get_module_resource(module, *args):
|
||||
"""Return the full path of a resource of the given module.
|
||||
|
||||
@param module: the module
|
||||
@param args: the resource path components
|
||||
|
||||
@return: absolute path to the resource
|
||||
"""
|
||||
a = get_module_path(module)
|
||||
if not a: return False
|
||||
resource_path = opj(a, *args)
|
||||
if zipfile.is_zipfile( a +'.zip') :
|
||||
zip = zipfile.ZipFile( a + ".zip")
|
||||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
resource_path = '/'.join(args)
|
||||
if resource_path in files:
|
||||
return opj(a, resource_path)
|
||||
elif os.path.exists(resource_path):
|
||||
return resource_path
|
||||
return False
|
||||
|
||||
def get_modules():
|
||||
"""Returns the list of module names
|
||||
"""
|
||||
def listdir(dir):
|
||||
def clean(name):
|
||||
name = os.path.basename(name)
|
||||
if name[-4:] == '.zip':
|
||||
name = name[:-4]
|
||||
return name
|
||||
|
||||
def is_really_module(name):
|
||||
name = opj(dir, name)
|
||||
return os.path.isdir(name) or zipfile.is_zipfile(name)
|
||||
return map(clean, filter(is_really_module, os.listdir(dir)))
|
||||
|
||||
plist = []
|
||||
initialize_sys_path()
|
||||
for ad in ad_paths:
|
||||
plist.extend(listdir(ad))
|
||||
return list(set(plist))
|
||||
|
||||
def load_information_from_description_file(module):
|
||||
"""
|
||||
:param module: The name of the module (sale, purchase, ...)
|
||||
"""
|
||||
|
||||
for filename in ['__openerp__.py', '__terp__.py']:
|
||||
description_file = get_module_resource(module, filename)
|
||||
if description_file :
|
||||
desc_f = tools.file_open(description_file)
|
||||
try:
|
||||
return eval(desc_f.read())
|
||||
finally:
|
||||
desc_f.close()
|
||||
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
# for 6.0
|
||||
logging.getLogger('addons').debug('The module %s does not contain a description file:'\
|
||||
'__openerp__.py or __terp__.py (deprecated)', module)
|
||||
return {}
|
||||
|
||||
def get_modules_with_version():
|
||||
modules = get_modules()
|
||||
res = {}
|
||||
for module in modules:
|
||||
try:
|
||||
info = load_information_from_description_file(module)
|
||||
res[module] = "%s.%s" % (release.major_version, info['version'])
|
||||
except Exception, e:
|
||||
continue
|
||||
return res
|
||||
|
||||
def create_graph(cr, module_list, force=None):
|
||||
graph = Graph()
|
||||
upgrade_graph(graph, cr, module_list, force)
|
||||
return graph
|
||||
|
||||
def upgrade_graph(graph, cr, module_list, force=None):
|
||||
if force is None:
|
||||
force = []
|
||||
packages = []
|
||||
len_graph = len(graph)
|
||||
for module in module_list:
|
||||
mod_path = get_module_path(module)
|
||||
terp_file = get_module_resource(module, '__openerp__.py')
|
||||
if not terp_file:
|
||||
terp_file = get_module_resource(module, '__terp__.py')
|
||||
if not mod_path or not terp_file:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not found, skipped' % (module))
|
||||
continue
|
||||
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
||||
terp_f = tools.file_open(terp_file)
|
||||
try:
|
||||
info = eval(terp_f.read())
|
||||
except Exception:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: eval file %s' % (module, terp_file))
|
||||
raise
|
||||
finally:
|
||||
terp_f.close()
|
||||
if info.get('installable', True):
|
||||
packages.append((module, info.get('depends', []), info))
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module))
|
||||
|
||||
dependencies = dict([(p, deps) for p, deps, data in packages])
|
||||
current, later = set([p for p, dep, data in packages]), set()
|
||||
|
||||
while packages and current > later:
|
||||
package, deps, data = packages[0]
|
||||
|
||||
# if all dependencies of 'package' are already in the graph, add 'package' in the graph
|
||||
if reduce(lambda x, y: x and y in graph, deps, True):
|
||||
if not package in current:
|
||||
packages.pop(0)
|
||||
continue
|
||||
later.clear()
|
||||
current.remove(package)
|
||||
graph.addNode(package, deps)
|
||||
node = Node(package, graph)
|
||||
node.data = data
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force:
|
||||
setattr(node, kind, True)
|
||||
else:
|
||||
later.add(package)
|
||||
packages.append((package, deps, data))
|
||||
packages.pop(0)
|
||||
|
||||
graph.update_from_db(cr)
|
||||
|
||||
for package in later:
|
||||
unmet_deps = filter(lambda p: p not in graph, dependencies[package])
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
|
||||
|
||||
result = len(graph) - len_graph
|
||||
if result != len(module_list):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.')
|
||||
return result
|
||||
|
||||
|
||||
def init_module_objects(cr, module_name, obj_list):
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: creating or updating database tables' % module_name)
|
||||
todo = []
|
||||
for obj in obj_list:
|
||||
try:
|
||||
result = obj._auto_init(cr, {'module': module_name})
|
||||
except Exception, e:
|
||||
raise
|
||||
if result:
|
||||
todo += result
|
||||
if hasattr(obj, 'init'):
|
||||
obj.init(cr)
|
||||
cr.commit()
|
||||
todo.sort()
|
||||
for t in todo:
|
||||
t[1](cr, *t[2])
|
||||
cr.commit()
|
||||
|
||||
|
||||
def register_class(m):
|
||||
"""
|
||||
Register module named m, if not already registered
|
||||
"""
|
||||
|
||||
def log(e):
|
||||
mt = isinstance(e, zipimport.ZipImportError) and 'zip ' or ''
|
||||
msg = "Couldn't load %smodule %s" % (mt, m)
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, msg)
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, e)
|
||||
|
||||
global loaded
|
||||
if m in loaded:
|
||||
return
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
|
||||
mod_path = get_module_path(m)
|
||||
|
||||
initialize_sys_path()
|
||||
try:
|
||||
zip_mod_path = mod_path + '.zip'
|
||||
if not os.path.isfile(zip_mod_path):
|
||||
fm = imp.find_module(m, ad_paths)
|
||||
try:
|
||||
imp.load_module(m, *fm)
|
||||
finally:
|
||||
if fm[0]:
|
||||
fm[0].close()
|
||||
else:
|
||||
zimp = zipimport.zipimporter(zip_mod_path)
|
||||
zimp.load_module(m)
|
||||
except Exception, e:
|
||||
log(e)
|
||||
raise
|
||||
else:
|
||||
loaded.append(m)
|
||||
|
||||
|
||||
class MigrationManager(object):
|
||||
"""
|
||||
This class manage the migration of modules
|
||||
Migrations files must be python files containing a "migrate(cr, installed_version)" function.
|
||||
Theses files must respect a directory tree structure: A 'migrations' folder which containt a
|
||||
folder by version. Version can be 'module' version or 'server.module' version (in this case,
|
||||
the files will only be processed by this version of the server). Python file names must start
|
||||
by 'pre' or 'post' and will be executed, respectively, before and after the module initialisation
|
||||
Example:
|
||||
|
||||
<moduledir>
|
||||
`-- migrations
|
||||
|-- 1.0
|
||||
| |-- pre-update_table_x.py
|
||||
| |-- pre-update_table_y.py
|
||||
| |-- post-clean-data.py
|
||||
| `-- README.txt # not processed
|
||||
|-- 5.0.1.1 # files in this folder will be executed only on a 5.0 server
|
||||
| |-- pre-delete_table_z.py
|
||||
| `-- post-clean-data.py
|
||||
`-- foo.py # not processed
|
||||
|
||||
This similar structure is generated by the maintenance module with the migrations files get by
|
||||
the maintenance contract
|
||||
|
||||
"""
|
||||
def __init__(self, cr, graph):
|
||||
self.cr = cr
|
||||
self.graph = graph
|
||||
self.migrations = {}
|
||||
self._get_files()
|
||||
|
||||
def _get_files(self):
|
||||
|
||||
"""
|
||||
import addons.base.maintenance.utils as maintenance_utils
|
||||
maintenance_utils.update_migrations_files(self.cr)
|
||||
#"""
|
||||
|
||||
for pkg in self.graph:
|
||||
self.migrations[pkg.name] = {}
|
||||
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
|
||||
continue
|
||||
|
||||
self.migrations[pkg.name]['module'] = get_module_filetree(pkg.name, 'migrations') or {}
|
||||
self.migrations[pkg.name]['maintenance'] = get_module_filetree('base', 'maintenance/migrations/' + pkg.name) or {}
|
||||
|
||||
def migrate_module(self, pkg, stage):
|
||||
assert stage in ('pre', 'post')
|
||||
stageformat = {'pre': '[>%s]',
|
||||
'post': '[%s>]',
|
||||
}
|
||||
|
||||
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
|
||||
return
|
||||
|
||||
def convert_version(version):
|
||||
if version.startswith(release.major_version) and version != release.major_version:
|
||||
return version # the version number already containt the server version
|
||||
return "%s.%s" % (release.major_version, version)
|
||||
|
||||
def _get_migration_versions(pkg):
|
||||
def __get_dir(tree):
|
||||
return [d for d in tree if tree[d] is not None]
|
||||
|
||||
versions = list(set(
|
||||
__get_dir(self.migrations[pkg.name]['module']) +
|
||||
__get_dir(self.migrations[pkg.name]['maintenance'])
|
||||
))
|
||||
versions.sort(key=lambda k: parse_version(convert_version(k)))
|
||||
return versions
|
||||
|
||||
def _get_migration_files(pkg, version, stage):
|
||||
""" return a list of tuple (module, file)
|
||||
"""
|
||||
m = self.migrations[pkg.name]
|
||||
lst = []
|
||||
|
||||
mapping = {'module': opj(pkg.name, 'migrations'),
|
||||
'maintenance': opj('base', 'maintenance', 'migrations', pkg.name),
|
||||
}
|
||||
|
||||
for x in mapping.keys():
|
||||
if version in m[x]:
|
||||
for f in m[x][version]:
|
||||
if m[x][version][f] is not None:
|
||||
continue
|
||||
if not f.startswith(stage + '-'):
|
||||
continue
|
||||
lst.append(opj(mapping[x], version, f))
|
||||
lst.sort()
|
||||
return lst
|
||||
|
||||
def mergedict(a, b):
|
||||
a = a.copy()
|
||||
a.update(b)
|
||||
return a
|
||||
|
||||
from openerp.tools.parse_version import parse_version
|
||||
|
||||
parsed_installed_version = parse_version(pkg.installed_version or '')
|
||||
current_version = parse_version(convert_version(pkg.data.get('version', '0')))
|
||||
|
||||
versions = _get_migration_versions(pkg)
|
||||
|
||||
for version in versions:
|
||||
if parsed_installed_version < parse_version(convert_version(version)) <= current_version:
|
||||
|
||||
strfmt = {'addon': pkg.name,
|
||||
'stage': stage,
|
||||
'version': stageformat[stage] % version,
|
||||
}
|
||||
|
||||
for pyfile in _get_migration_files(pkg, version, stage):
|
||||
name, ext = os.path.splitext(os.path.basename(pyfile))
|
||||
if ext.lower() != '.py':
|
||||
continue
|
||||
mod = fp = fp2 = None
|
||||
try:
|
||||
fp = tools.file_open(pyfile)
|
||||
|
||||
# imp.load_source need a real file object, so we create
|
||||
# one from the file-like object we get from file_open
|
||||
fp2 = os.tmpfile()
|
||||
fp2.write(fp.read())
|
||||
fp2.seek(0)
|
||||
try:
|
||||
mod = imp.load_source(name, pyfile, fp2)
|
||||
logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s' % mergedict({'name': mod.__name__}, strfmt))
|
||||
mod.migrate(self.cr, pkg.installed_version)
|
||||
except ImportError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % mergedict({'file': pyfile}, strfmt))
|
||||
raise
|
||||
except AttributeError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
if fp2:
|
||||
fp2.close()
|
||||
if mod:
|
||||
del mod
|
||||
|
||||
log = logging.getLogger('init')
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, **kwargs):
|
||||
"""Migrates+Updates or Installs all module nodes from ``graph``
|
||||
:param graph: graph of module nodes to load
|
||||
:param status: status dictionary for keeping track of progress
|
||||
:param perform_checks: whether module descriptors should be checked for validity (prints warnings
|
||||
for same cases, and even raise osv_except if certificate is invalid)
|
||||
:param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped
|
||||
:return: list of modules that were installed or updated
|
||||
"""
|
||||
def process_sql_file(cr, fp):
|
||||
queries = fp.read().split(';')
|
||||
for query in queries:
|
||||
new_query = ' '.join(query.split())
|
||||
if new_query:
|
||||
cr.execute(new_query)
|
||||
|
||||
def load_init_update_xml(cr, m, idref, mode, kind):
|
||||
for filename in package.data.get('%s_xml' % kind, []):
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, filename))
|
||||
_, ext = os.path.splitext(filename)
|
||||
fp = tools.file_open(opj(m, filename))
|
||||
try:
|
||||
if ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, m, os.path.basename(filename), fp.read(), idref, mode=mode, noupdate=noupdate)
|
||||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, **kwargs)
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
def load_demo_xml(cr, m, idref, mode):
|
||||
for xml in package.data.get('demo_xml', []):
|
||||
name, ext = os.path.splitext(xml)
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading %s' % (m, xml))
|
||||
fp = tools.file_open(opj(m, xml))
|
||||
try:
|
||||
if ext == '.csv':
|
||||
tools.convert_csv_import(cr, m, os.path.basename(xml), fp.read(), idref, mode=mode, noupdate=True)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
else:
|
||||
tools.convert_xml_import(cr, m, fp, idref, mode=mode, noupdate=True, **kwargs)
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
def load_data(cr, module_name, id_map, mode):
|
||||
_load_data(cr, module_name, id_map, mode, 'data')
|
||||
|
||||
def load_demo(cr, module_name, id_map, mode):
|
||||
_load_data(cr, module_name, id_map, mode, 'demo')
|
||||
|
||||
def load_test(cr, module_name, id_map, mode):
|
||||
cr.commit()
|
||||
if not tools.config.options['test_disable']:
|
||||
try:
|
||||
_load_data(cr, module_name, id_map, mode, 'test')
|
||||
except Exception, e:
|
||||
logging.getLogger('test').exception('Tests failed to execute in module %s', module_name)
|
||||
finally:
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, id_map, mode, kind):
|
||||
for filename in package.data.get(kind, []):
|
||||
noupdate = (kind == 'demo')
|
||||
_, ext = os.path.splitext(filename)
|
||||
log.info("module %s: loading %s", module_name, filename)
|
||||
pathname = os.path.join(module_name, filename)
|
||||
file = tools.file_open(pathname)
|
||||
try:
|
||||
if ext == '.sql':
|
||||
process_sql_file(cr, file)
|
||||
elif ext == '.csv':
|
||||
noupdate = (kind == 'init')
|
||||
tools.convert_csv_import(cr, module_name, pathname, file.read(), id_map, mode, noupdate)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, file, id_map, mode, noupdate)
|
||||
finally:
|
||||
file.close()
|
||||
|
||||
# **kwargs is passed directly to convert_xml_import
|
||||
if not status:
|
||||
status = {}
|
||||
|
||||
status = status.copy()
|
||||
processed_modules = []
|
||||
statusi = 0
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
migrations = MigrationManager(cr, graph)
|
||||
modobj = None
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph))
|
||||
|
||||
for package in graph:
|
||||
if skip_modules and package.name in skip_modules:
|
||||
continue
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
register_class(package.name)
|
||||
modules = pool.instanciate(package.name, cr)
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
init_module_objects(cr, package.name, modules)
|
||||
cr.commit()
|
||||
|
||||
for package in graph:
|
||||
status['progress'] = (float(statusi)+0.1) / len(graph)
|
||||
m = package.name
|
||||
mid = package.id
|
||||
|
||||
if skip_modules and m in skip_modules:
|
||||
continue
|
||||
|
||||
if modobj is None:
|
||||
modobj = pool.get('ir.module.module')
|
||||
|
||||
if modobj and perform_checks:
|
||||
modobj.check(cr, 1, [mid])
|
||||
|
||||
idref = {}
|
||||
status['progress'] = (float(statusi)+0.4) / len(graph)
|
||||
|
||||
mode = 'update'
|
||||
if hasattr(package, 'init') or package.state == 'to install':
|
||||
mode = 'init'
|
||||
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
for kind in ('init', 'update'):
|
||||
if package.state=='to upgrade':
|
||||
# upgrading the module information
|
||||
modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data))
|
||||
load_init_update_xml(cr, m, idref, mode, kind)
|
||||
load_data(cr, m, idref, mode)
|
||||
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
|
||||
status['progress'] = (float(statusi)+0.75) / len(graph)
|
||||
load_demo_xml(cr, m, idref, mode)
|
||||
load_demo(cr, m, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
|
||||
|
||||
# launch tests only in demo mode, as most tests will depend
|
||||
# on demo data. Other tests can be added into the regular
|
||||
# 'data' section, but should probably not alter the data,
|
||||
# as there is no rollback.
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
processed_modules.append(package.name)
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
if modobj:
|
||||
ver = release.major_version + '.' + package.data.get('version', '1.0')
|
||||
# Set new modules and dependencies
|
||||
modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver})
|
||||
cr.commit()
|
||||
# Update translations for all installed languages
|
||||
modobj.update_translations(cr, 1, [mid], None, {'overwrite': tools.config['overwrite_existing_translations']})
|
||||
cr.commit()
|
||||
|
||||
package.state = 'installed'
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
|
||||
statusi += 1
|
||||
|
||||
cr.commit()
|
||||
|
||||
return processed_modules
|
||||
|
||||
def _check_module_names(cr, module_names):
|
||||
mod_names = set(module_names)
|
||||
if 'base' in mod_names:
|
||||
# ignore dummy 'all' module
|
||||
if 'all' in mod_names:
|
||||
mod_names.remove('all')
|
||||
if mod_names:
|
||||
cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),))
|
||||
if cr.dictfetchone()['count'] != len(mod_names):
|
||||
# find out what module name(s) are incorrect:
|
||||
cr.execute("SELECT name FROM ir_module_module")
|
||||
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
|
||||
logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
|
||||
|
||||
def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
|
||||
initialize_sys_path()
|
||||
|
||||
# Backward compatibility: addons don't have to import openerp.xxx, they still can import xxx
|
||||
for k, v in list(sys.modules.items()):
|
||||
if k.startswith('openerp.') and sys.modules.get(k[8:]) is None:
|
||||
sys.modules[k[8:]] = v
|
||||
|
||||
if not status:
|
||||
status = {}
|
||||
cr = db.cursor()
|
||||
if cr:
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'")
|
||||
if len(cr.fetchall())==0:
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, "init db")
|
||||
tools.init_db(cr)
|
||||
tools.config["init"]["all"] = 1
|
||||
tools.config['update']['all'] = 1
|
||||
if not tools.config['without_demo']:
|
||||
tools.config["demo"]['all'] = 1
|
||||
force = []
|
||||
if force_demo:
|
||||
force.append('demo')
|
||||
|
||||
# This is a brand new pool, just created in pooler.get_db_and_pool()
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
try:
|
||||
processed_modules = []
|
||||
report = tools.assertion_report()
|
||||
# NOTE: Try to also load the modules that have been marked as uninstallable previously...
|
||||
STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable']
|
||||
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
|
||||
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
|
||||
|
||||
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
|
||||
graph = create_graph(cr, ['base'], force)
|
||||
if not graph:
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
|
||||
raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report))
|
||||
|
||||
if tools.config['load_language']:
|
||||
for lang in tools.config['load_language'].split(','):
|
||||
tools.load_language(cr, lang)
|
||||
|
||||
# STEP 2: Mark other modules to be loaded/updated
|
||||
if update_module:
|
||||
modobj = pool.get('ir.module.module')
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
|
||||
if ('base' in tools.config['init']) or ('base' in tools.config['update']):
|
||||
modobj.update_list(cr, 1)
|
||||
|
||||
_check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys()))
|
||||
|
||||
mods = [k for k in tools.config['init'] if tools.config['init'][k]]
|
||||
if mods:
|
||||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_install(cr, 1, ids)
|
||||
|
||||
mods = [k for k in tools.config['update'] if tools.config['update'][k]]
|
||||
if mods:
|
||||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_upgrade(cr, 1, ids)
|
||||
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
|
||||
STATES_TO_LOAD += ['to install']
|
||||
|
||||
|
||||
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
|
||||
loop_guardrail = 0
|
||||
while True:
|
||||
loop_guardrail += 1
|
||||
if loop_guardrail > 100:
|
||||
raise ValueError('Possible recursive module tree detected, aborting.')
|
||||
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
|
||||
|
||||
module_list = [name for (name,) in cr.fetchall() if name not in graph]
|
||||
if not module_list:
|
||||
break
|
||||
|
||||
new_modules_in_graph = upgrade_graph(graph, cr, module_list, force)
|
||||
if new_modules_in_graph == 0:
|
||||
# nothing to load
|
||||
break
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules))
|
||||
|
||||
# load custom models
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for model in cr.dictfetchall():
|
||||
pool.get('ir.model').instanciate(cr, 1, model['model'], {})
|
||||
|
||||
# STEP 4: Finish and cleanup
|
||||
if processed_modules:
|
||||
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
|
||||
for (model, name) in cr.fetchall():
|
||||
model_obj = pool.get(model)
|
||||
if model_obj and not isinstance(model_obj, osv.osv.osv_memory):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
|
||||
|
||||
# Temporary warning while we remove access rights on osv_memory objects, as they have
|
||||
# been replaced by owner-only access rights
|
||||
cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""")
|
||||
for (model, name) in cr.fetchall():
|
||||
model_obj = pool.get(model)
|
||||
if isinstance(model_obj, osv.osv.osv_memory):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name))
|
||||
|
||||
cr.execute("SELECT model from ir_model")
|
||||
for (model,) in cr.fetchall():
|
||||
obj = pool.get(model)
|
||||
if obj:
|
||||
obj._check_removed_columns(cr, log=True)
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model)
|
||||
|
||||
# Cleanup orphan records
|
||||
pool.get('ir.model.data')._process_end(cr, 1, processed_modules)
|
||||
|
||||
if report.get_report():
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, report)
|
||||
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
tools.config[kind] = {}
|
||||
|
||||
cr.commit()
|
||||
if update_module:
|
||||
cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
|
||||
for mod_id, mod_name in cr.fetchall():
|
||||
cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
|
||||
for rmod, rid in cr.fetchall():
|
||||
uid = 1
|
||||
rmod_module= pool.get(rmod)
|
||||
if rmod_module:
|
||||
rmod_module.unlink(cr, uid, [rid])
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid))
|
||||
cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,))
|
||||
cr.commit()
|
||||
#
|
||||
# TODO: remove menu without actions of children
|
||||
#
|
||||
while True:
|
||||
cr.execute('''delete from
|
||||
ir_ui_menu
|
||||
where
|
||||
(id not IN (select parent_id from ir_ui_menu where parent_id is not null))
|
||||
and
|
||||
(id not IN (select res_id from ir_values where model='ir.ui.menu'))
|
||||
and
|
||||
(id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''')
|
||||
cr.commit()
|
||||
if not cr.rowcount:
|
||||
break
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,))
|
||||
|
||||
cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
"""
|
||||
|
||||
# get_module_path is used only by base_module_quality
|
||||
from openerp.modules import get_module_resource, get_module_path
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -45,6 +45,7 @@ CREATE TABLE ir_model_fields (
|
||||
view_load boolean,
|
||||
relate boolean default False,
|
||||
relation_field varchar(128),
|
||||
translate boolean default False,
|
||||
primary key(id)
|
||||
);
|
||||
|
||||
|
||||
@@ -7,14 +7,14 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2010-06-06 04:19+0000\n"
|
||||
"Last-Translator: Vladimír Burian <vladimir.burian@email.cz>\n"
|
||||
"PO-Revision-Date: 2011-05-18 16:53+0000\n"
|
||||
"Last-Translator: Jan B. Krejčí <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-01-12 04:46+0000\n"
|
||||
"X-Generator: Launchpad (build Unknown)\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-19 06:22+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
@@ -23,22 +23,22 @@ msgstr ""
|
||||
#: field:ir.rule,domain_force:0
|
||||
#: field:res.partner.title,domain:0
|
||||
msgid "Domain"
|
||||
msgstr ""
|
||||
msgstr "Doména"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
msgid "Saint Helena"
|
||||
msgstr ""
|
||||
msgstr "Svatá Helena"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.report.xml:0
|
||||
msgid "Other Configuration"
|
||||
msgstr ""
|
||||
msgstr "Ostatní nastavení"
|
||||
|
||||
#. module: base
|
||||
#: selection:ir.property,type:0
|
||||
msgid "DateTime"
|
||||
msgstr ""
|
||||
msgstr "Datum a čas"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/fields.py:534
|
||||
@@ -58,12 +58,12 @@ msgstr "Metadata"
|
||||
#: field:ir.ui.view,arch:0
|
||||
#: field:ir.ui.view.custom,arch:0
|
||||
msgid "View Architecture"
|
||||
msgstr "Architektura náhledu(View Architecture)"
|
||||
msgstr "Architektura zobrazení"
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.import,code:0
|
||||
msgid "Code (eg:en__US)"
|
||||
msgstr "Kód (eg:en__US)"
|
||||
msgstr "Kód (např. en_US)"
|
||||
|
||||
#. module: base
|
||||
#: view:workflow:0
|
||||
@@ -73,12 +73,12 @@ msgstr "Kód (eg:en__US)"
|
||||
#: field:workflow.transition,wkf_id:0
|
||||
#: field:workflow.workitem,wkf_id:0
|
||||
msgid "Workflow"
|
||||
msgstr "Workflow(Workflow)"
|
||||
msgstr "Pracovní postup"
|
||||
|
||||
#. module: base
|
||||
#: view:partner.sms.send:0
|
||||
msgid "SMS - Gateway: clickatell"
|
||||
msgstr "Brána: clickatell(Gateway: clickatell)"
|
||||
msgstr "SMS brána Clickatell"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-11 04:20+0000\n"
|
||||
"Last-Translator: doingit.cl <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-06-06 17:53+0000\n"
|
||||
"Last-Translator: Hector Rojas (doingIT.cl) <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-12 04:34+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-06-07 04:35+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -58,7 +58,7 @@ msgstr "Metadatos"
|
||||
#: field:ir.ui.view,arch:0
|
||||
#: field:ir.ui.view.custom,arch:0
|
||||
msgid "View Architecture"
|
||||
msgstr "Código vista"
|
||||
msgstr "Estructura de la vista"
|
||||
|
||||
#. module: base
|
||||
#: field:base.language.import,code:0
|
||||
|
||||
@@ -7,14 +7,14 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-03-22 22:41+0000\n"
|
||||
"Last-Translator: Pierre Burnier <Unknown>\n"
|
||||
"PO-Revision-Date: 2011-05-16 16:57+0000\n"
|
||||
"Last-Translator: lolivier <olivier.lenoir@free.fr>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-03-23 06:23+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-17 06:04+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
@@ -8647,7 +8647,7 @@ msgstr "Danish / Dansk"
|
||||
#. module: base
|
||||
#: selection:ir.model.fields,select_level:0
|
||||
msgid "Advanced Search (deprecated)"
|
||||
msgstr ""
|
||||
msgstr "Recherche avancée (obsolète)"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.cx
|
||||
|
||||
@@ -9,12 +9,12 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-20 09:35+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Last-Translator: Serj Safarian <serjsafarian@gmail.com>\n"
|
||||
"Language-Team: Armenian <hy@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-21 05:01+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-21 06:14+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -9,12 +9,12 @@ msgstr ""
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-06 11:49+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Last-Translator: martin <Unknown>\n"
|
||||
"Language-Team: Macedonian <mk@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-07 04:53+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-07 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -7,14 +7,14 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-03-31 14:18+0000\n"
|
||||
"Last-Translator: Grzegorz Grzelak (OpenGLOBE.pl) <grzegorz@openglobe.pl>\n"
|
||||
"PO-Revision-Date: 2011-05-14 20:16+0000\n"
|
||||
"Last-Translator: Mariusz Wójtowicz <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-04-01 06:04+0000\n"
|
||||
"X-Generator: Launchpad (build 12559)\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-15 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.filters:0
|
||||
@@ -171,7 +171,7 @@ msgstr "Swaziland"
|
||||
#: code:addons/orm.py:3653
|
||||
#, python-format
|
||||
msgid "created."
|
||||
msgstr ""
|
||||
msgstr "utworzona."
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_woodsuppliers0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: pt_BR\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-10 19:52+0000\n"
|
||||
"Last-Translator: Fabien (Open ERP) <fp@tinyerp.com>\n"
|
||||
"PO-Revision-Date: 2011-05-22 04:28+0000\n"
|
||||
"Last-Translator: Emerson <Unknown>\n"
|
||||
"Language-Team: <pt@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-11 04:36+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-23 05:33+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.4\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-10 19:56+0000\n"
|
||||
"PO-Revision-Date: 2011-05-26 04:57+0000\n"
|
||||
"Last-Translator: Pomazan Bogdan <Unknown>\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-11 04:36+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-27 04:45+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -613,7 +613,7 @@ msgstr "Королевство Камбоджа"
|
||||
#: model:ir.ui.menu,name:base.menu_ir_sequence_form
|
||||
#: model:ir.ui.menu,name:base.next_id_5
|
||||
msgid "Sequences"
|
||||
msgstr "Последовательности"
|
||||
msgstr "Нумерация"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_base_language_import
|
||||
@@ -1139,7 +1139,7 @@ msgstr "Банк"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_exports_line
|
||||
msgid "ir.exports.line"
|
||||
msgstr "Строки экспорта"
|
||||
msgstr "ir.exports.line"
|
||||
|
||||
#. module: base
|
||||
#: help:base.language.install,overwrite:0
|
||||
@@ -1191,7 +1191,7 @@ msgstr ""
|
||||
#: field:res.config.users,login:0
|
||||
#: field:res.users,login:0
|
||||
msgid "Login"
|
||||
msgstr "Вход"
|
||||
msgstr "Логин"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.actions.server:0
|
||||
@@ -2069,7 +2069,7 @@ msgstr "Куба"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_res_partner_event
|
||||
msgid "res.partner.event"
|
||||
msgstr "События партнера"
|
||||
msgstr "res.partner.event"
|
||||
|
||||
#. module: base
|
||||
#: model:res.widget,title:base.facebook_widget
|
||||
@@ -2995,7 +2995,7 @@ msgstr "Испанский (HN) / Español (HN)"
|
||||
#. module: base
|
||||
#: view:ir.sequence.type:0
|
||||
msgid "Sequence Type"
|
||||
msgstr "Тип последовательности"
|
||||
msgstr "Тип нумерации"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.ui.view.custom:0
|
||||
@@ -3280,7 +3280,7 @@ msgstr "Договор уже зарегистрирован в системе."
|
||||
#. module: base
|
||||
#: help:ir.sequence,suffix:0
|
||||
msgid "Suffix value of the record for the sequence"
|
||||
msgstr "Суффикс записи для последовательности"
|
||||
msgstr "Суффикс записи для нумерации"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
@@ -3528,10 +3528,11 @@ msgid ""
|
||||
"Would your payment have been carried out after this mail was sent, please "
|
||||
"consider the present one as void."
|
||||
msgstr ""
|
||||
"Уведомляем, что наступило время следующего платежа. Если платёж был "
|
||||
"отправлен — предоставьте, пожалуйста, подробности этого платежа. Если платёж "
|
||||
"будет и далее задерживаться, свяжитесь, пожалуйста, с нами. "
|
||||
" Если платёж был отправлен до отправки этого письма — возможно, письмо "
|
||||
"Уведомляем, что наступило время следующего платежа. \n"
|
||||
"Если платёж был отправлен — предоставьте, пожалуйста, подробности этого "
|
||||
"платежа. \n"
|
||||
"Если платёж будет и далее задерживаться, свяжитесь, пожалуйста, с нами.\n"
|
||||
"Если платёж был отправлен до отправки этого письма — возможно, письмо "
|
||||
"отправлено по ошибке."
|
||||
|
||||
#. module: base
|
||||
@@ -4794,7 +4795,7 @@ msgstr "Бутан"
|
||||
#. module: base
|
||||
#: help:ir.sequence,number_next:0
|
||||
msgid "Next number of this sequence"
|
||||
msgstr "Следующее число в этой последовательности"
|
||||
msgstr "Следующее число в этой нумерации"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_11
|
||||
@@ -5170,7 +5171,7 @@ msgstr "Контакт"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_ui_menu
|
||||
msgid "ir.ui.menu"
|
||||
msgstr "Меню"
|
||||
msgstr "ir.ui.menu"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.us
|
||||
@@ -5197,7 +5198,7 @@ msgstr "Отчет RML"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_server_object_lines
|
||||
msgid "ir.server.object.lines"
|
||||
msgstr "События сервера"
|
||||
msgstr "ir.server.object.lines"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/module.py:531
|
||||
@@ -5503,7 +5504,7 @@ msgstr "Обновление / Установка модуля"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_actions_configuration_wizard
|
||||
msgid "ir.actions.configuration.wizard"
|
||||
msgstr "Диалог конфигурировая события"
|
||||
msgstr "ir.actions.configuration.wizard"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -5794,7 +5795,7 @@ msgstr "Дата создания"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_actions_todo
|
||||
msgid "ir.actions.todo"
|
||||
msgstr "ТОДО"
|
||||
msgstr "ir.actions.todo"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/res/res_config.py:94
|
||||
@@ -6227,7 +6228,7 @@ msgstr "Сбор средств"
|
||||
#: model:ir.actions.act_window,name:base.ir_sequence_type
|
||||
#: model:ir.ui.menu,name:base.menu_ir_sequence_type
|
||||
msgid "Sequence Codes"
|
||||
msgstr "Последовательность кодов"
|
||||
msgstr "Коды нумераций"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
@@ -6749,7 +6750,7 @@ msgstr "Мартиника (заморский регион Франции)"
|
||||
#. module: base
|
||||
#: view:ir.sequence.type:0
|
||||
msgid "Sequences Type"
|
||||
msgstr "Тип последовательностей"
|
||||
msgstr "Тип нумерации"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.res_request-act
|
||||
@@ -6803,7 +6804,7 @@ msgid ""
|
||||
"number of modules currently installed)..."
|
||||
msgstr ""
|
||||
"Пожалуйста подождите, эта операция может занять несколько минут (зависит от "
|
||||
"количества устанавливаемых модулей)..."
|
||||
"количества установленных модулей)..."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.ui.menu,child_id:0
|
||||
@@ -6990,7 +6991,7 @@ msgstr "Часовой пояс"
|
||||
#: model:ir.model,name:base.model_ir_actions_report_xml
|
||||
#: selection:ir.ui.menu,action:0
|
||||
msgid "ir.actions.report.xml"
|
||||
msgstr "События отчетов"
|
||||
msgstr "ir.actions.report.xml"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_miss
|
||||
@@ -7309,7 +7310,7 @@ msgstr "Месяц: %(месяц)ы"
|
||||
#: field:res.widget.user,sequence:0
|
||||
#: field:wizard.ir.model.menu.create.line,sequence:0
|
||||
msgid "Sequence"
|
||||
msgstr "Последовательность"
|
||||
msgstr "Нумерация"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.tn
|
||||
@@ -7865,8 +7866,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"Можно устанавливать новые модули для подключения новых функций, меню, "
|
||||
"отчётов или данных в вашем экземпляре OpenERP. Для установки модулей, "
|
||||
"нажмите на кнопку «Отметить для установки» в представлении в виде формы, "
|
||||
"затем нажмите «Применить отмеченные обновления», чтобы обновить вашу систему."
|
||||
"нажмите на кнопку «Запланировать установку» в представлении в виде формы, "
|
||||
"затем нажмите «Выполнить запланированные обновления», чтобы обновить вашу "
|
||||
"систему."
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_emails
|
||||
@@ -8055,7 +8057,7 @@ msgstr "Габон"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_model_data
|
||||
msgid "ir.model.data"
|
||||
msgstr "Данные"
|
||||
msgstr "ir.model.data"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model:0
|
||||
@@ -8350,7 +8352,7 @@ msgstr "Адресная книга"
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_sequence_type
|
||||
msgid "ir.sequence.type"
|
||||
msgstr "Тип последовательности"
|
||||
msgstr "ir.sequence.type"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.export,format:0
|
||||
@@ -8642,7 +8644,7 @@ msgstr "События клиента"
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
msgid "Schedule for Installation"
|
||||
msgstr "Расписание установки"
|
||||
msgstr "Запланировать установку"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_partner_wizard_ean_check
|
||||
@@ -8689,7 +8691,7 @@ msgstr "Вануату"
|
||||
#. module: base
|
||||
#: view:res.company:0
|
||||
msgid "Internal Header/Footer"
|
||||
msgstr "Внутренние верхний / нижний колонтитулы"
|
||||
msgstr "Внутренние верхний/нижний колонтитулы"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/module/wizard/base_export_language.py:59
|
||||
@@ -9488,7 +9490,7 @@ msgstr "Поле мастера"
|
||||
#. module: base
|
||||
#: help:ir.sequence,prefix:0
|
||||
msgid "Prefix value of the record for the sequence"
|
||||
msgstr "Префикс записи для последовательности"
|
||||
msgstr "Префикс для нумерации"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sc
|
||||
@@ -9542,7 +9544,7 @@ msgstr "Объект ресурса"
|
||||
#. module: base
|
||||
#: help:ir.sequence,number_increment:0
|
||||
msgid "The next number of the sequence will be incremented by this number"
|
||||
msgstr "Следующее число последовательности будет увеличено на это число"
|
||||
msgstr "Следующее число в нумерации будет увеличено на это число"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.cron,function:0
|
||||
|
||||
@@ -7,13 +7,13 @@ msgstr ""
|
||||
"Project-Id-Version: OpenERP Server 5.0.0\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-13 07:33+0000\n"
|
||||
"PO-Revision-Date: 2011-05-20 16:43+0000\n"
|
||||
"Last-Translator: Anders Wallenquist <anders.wallenquist@vertel.se>\n"
|
||||
"Language-Team: <>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-14 04:53+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-21 06:14+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -1522,6 +1522,8 @@ msgid ""
|
||||
"This wizard will scan all module repositories on the server side to detect "
|
||||
"newly added modules as well as any change to existing modules."
|
||||
msgstr ""
|
||||
"Denna guide söker igenom alla modularkiv på servern och upptäcker nya "
|
||||
"moduler liksom uppdateringar av befintliga moduler"
|
||||
|
||||
#. module: base
|
||||
#: field:res.company,logo:0
|
||||
@@ -3005,7 +3007,7 @@ msgid ""
|
||||
"The selected language has been successfully installed. You must change the "
|
||||
"preferences of the user and open a new menu to view the changes."
|
||||
msgstr ""
|
||||
"Språket du valde jhar installerats. Du kan ändra egenskaperna av användaren "
|
||||
"Språket du valde har installerats. Du kan ändra egenskaperna av användaren "
|
||||
"och öppna en meny för att se ändringarna."
|
||||
|
||||
#. module: base
|
||||
@@ -4614,7 +4616,7 @@ msgstr "lägg till"
|
||||
#: model:ir.actions.act_window,name:base.action_view_base_module_upgrade_window
|
||||
#: model:ir.ui.menu,name:base.menu_view_base_module_upgrade
|
||||
msgid "Apply Scheduled Upgrades"
|
||||
msgstr "Applicera planerade uppgraderingar"
|
||||
msgstr "Inför uppgraderingar"
|
||||
|
||||
#. module: base
|
||||
#: view:res.widget:0
|
||||
@@ -5433,7 +5435,7 @@ msgstr "Lägg till användare"
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.action_view_base_module_upgrade_install
|
||||
msgid "Module Upgrade Install"
|
||||
msgstr "Modul uppgradering installering"
|
||||
msgstr "Moduluppgradering"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.model,name:base.model_ir_actions_configuration_wizard
|
||||
@@ -5833,7 +5835,7 @@ msgstr "Kan inte skapa modulfilen: %s !"
|
||||
#. module: base
|
||||
#: model:ir.module.module,description:base.module_meta_information
|
||||
msgid "The kernel of OpenERP, needed for all installation."
|
||||
msgstr "OpenERP's kärna, behövs för alla installationer"
|
||||
msgstr "OpenERP's kärna, behövs för alla installationer."
|
||||
|
||||
#. module: base
|
||||
#: view:base.language.install:0
|
||||
@@ -6046,7 +6048,7 @@ msgstr "Vy"
|
||||
#: selection:ir.module.module,state:0
|
||||
#: selection:ir.module.module.dependency,state:0
|
||||
msgid "To be installed"
|
||||
msgstr "Att installeras"
|
||||
msgstr "Kommer att installeras"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,display_menu_tip:0
|
||||
@@ -6642,7 +6644,7 @@ msgstr ""
|
||||
#: selection:ir.module.module,state:0
|
||||
#: selection:ir.module.module.dependency,state:0
|
||||
msgid "Not Installed"
|
||||
msgstr "Inte installerat"
|
||||
msgstr "Inte installerad"
|
||||
|
||||
#. module: base
|
||||
#: view:workflow.activity:0
|
||||
@@ -7252,7 +7254,7 @@ msgstr "Serveråtgärder"
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
msgid "Cancel Install"
|
||||
msgstr "Avbryt installering"
|
||||
msgstr "Avbryt installationen"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.model.fields,selection:0
|
||||
@@ -8162,7 +8164,7 @@ msgstr "Administration"
|
||||
#. module: base
|
||||
#: view:base.module.update:0
|
||||
msgid "Click on Update below to start the process..."
|
||||
msgstr "Klicka op Uppdatera nedan för att starta bearbetningen..."
|
||||
msgstr "Klicka på \"Uppdatera\" nedan för att starta bearbetningen..."
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.ir
|
||||
|
||||
+196
-91
File diff suppressed because it is too large
Load Diff
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: openobject-addons\n"
|
||||
"Report-Msgid-Bugs-To: support@openerp.com\n"
|
||||
"POT-Creation-Date: 2011-01-11 11:14+0000\n"
|
||||
"PO-Revision-Date: 2011-05-04 18:57+0000\n"
|
||||
"PO-Revision-Date: 2011-05-29 23:23+0000\n"
|
||||
"Last-Translator: Phong Nguyen-Thanh <Unknown>\n"
|
||||
"Language-Team: Vietnamese <vi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-05 06:00+0000\n"
|
||||
"X-Launchpad-Export-Date: 2011-05-31 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 12959)\n"
|
||||
|
||||
#. module: base
|
||||
@@ -24,7 +24,7 @@ msgstr ""
|
||||
#: field:ir.rule,domain_force:0
|
||||
#: field:res.partner.title,domain:0
|
||||
msgid "Domain"
|
||||
msgstr "Vùng"
|
||||
msgstr "Miền"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.sh
|
||||
@@ -48,6 +48,8 @@ msgid ""
|
||||
"The second argument of the many2many field %s must be a SQL table !You used "
|
||||
"%s, which is not a valid SQL table name."
|
||||
msgstr ""
|
||||
"Đối số thứ hai trong quan hệ nhiều-nhiều %s phải là một bảng SQL ! Bạn đã "
|
||||
"dùng %s, đó không phải là một tên bảng SQL hợp lệ."
|
||||
|
||||
#. module: base
|
||||
#: view:ir.values:0
|
||||
@@ -104,7 +106,7 @@ msgstr "Quy trình công việc cho"
|
||||
#. module: base
|
||||
#: field:ir.actions.act_window,display_menu_tip:0
|
||||
msgid "Display Menu Tips"
|
||||
msgstr ""
|
||||
msgstr "Hiện chỉ dẫn trình đơn"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.module.module:0
|
||||
@@ -128,6 +130,9 @@ msgid ""
|
||||
"specified as a Python expression defining a list of triplets. For example: "
|
||||
"[('color','=','red')]"
|
||||
msgstr ""
|
||||
"Miền tùy chọn dùng để giới hạn các giá trị có thể đối với các trường quan "
|
||||
"hệ, được chỉ định bởi một biểu thức Python định nghĩa một danh sách của các "
|
||||
"bộ ba. Ví dụ: [('màu'='đỏ')]"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner,ref:0
|
||||
@@ -152,8 +157,9 @@ msgid ""
|
||||
"Properties of base fields cannot be altered in this manner! Please modify "
|
||||
"them through Python code, preferably through a custom addon!"
|
||||
msgstr ""
|
||||
"các thuộc tính cơ bản không thể thay đổi theo cách này ! hãy thay đổi chúng "
|
||||
"thông qua Python mã, tốt hơn thông qua một addon tùy chỉnh!"
|
||||
"Các thuộc tính của trường cơ bản không thể thay đổi theo cách này ! Vui lòng "
|
||||
"thay đổi chúng thông qua mã Python, tốt nhất là thông qua một addon tùy "
|
||||
"chỉnh!"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/osv.py:133
|
||||
@@ -229,7 +235,7 @@ msgstr "mới"
|
||||
#. module: base
|
||||
#: field:ir.actions.report.xml,multi:0
|
||||
msgid "On multiple doc."
|
||||
msgstr ""
|
||||
msgstr "Trong nhiều tài liệu."
|
||||
|
||||
#. module: base
|
||||
#: field:ir.module.category,module_nr:0
|
||||
@@ -239,7 +245,7 @@ msgstr "Số lượng mô-đun"
|
||||
#. module: base
|
||||
#: help:multi_company.default,company_dest_id:0
|
||||
msgid "Company to store the current record"
|
||||
msgstr ""
|
||||
msgstr "Công ty lưu trữ bản ghi hiện tại"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.bank.type.field,size:0
|
||||
@@ -258,6 +264,8 @@ msgid ""
|
||||
"Save this document to a %s file and edit it with a specific software or a "
|
||||
"text editor. The file encoding is UTF-8."
|
||||
msgstr ""
|
||||
"Lưu tài liệu này thành tập tin %s và chỉnh sửa bằng một phần mềm phù hợp "
|
||||
"hoặc trình soạnh thảo văn bản. Tập tin mã hóa dạng UTF-8"
|
||||
|
||||
#. module: base
|
||||
#: sql_constraint:res.lang:0
|
||||
@@ -393,12 +401,14 @@ msgid ""
|
||||
"If you check this, then the second time the user prints with same attachment "
|
||||
"name, it returns the previous report."
|
||||
msgstr ""
|
||||
"Nếu bạn đánh dấu mục này, khi người sử dụng in lần thứ hai với cùng tên tập "
|
||||
"tin đính kèm, hệ thống sẽ trả về báo cáo trước đó."
|
||||
|
||||
#. module: base
|
||||
#: code:addons/orm.py:904
|
||||
#, python-format
|
||||
msgid "The read method is not implemented on this object !"
|
||||
msgstr ""
|
||||
msgstr "Phương thức đọc không được hiện thực trên đối tượng này !"
|
||||
|
||||
#. module: base
|
||||
#: help:res.lang,iso_code:0
|
||||
@@ -414,7 +424,7 @@ msgstr "Hệ thống của bạn sẽ được cập nhật"
|
||||
#: field:ir.actions.todo,note:0
|
||||
#: selection:ir.property,type:0
|
||||
msgid "Text"
|
||||
msgstr "Chữ"
|
||||
msgstr "Văn bản"
|
||||
|
||||
#. module: base
|
||||
#: field:res.country,name:0
|
||||
@@ -435,7 +445,7 @@ msgstr "Lên kế hoạch nâng cấp"
|
||||
#: code:addons/orm.py:838
|
||||
#, python-format
|
||||
msgid "Key/value '%s' not found in selection field '%s'"
|
||||
msgstr ""
|
||||
msgstr "Khóa/giá trị '%s' không tìm thấy trong trường được chọn '%s'"
|
||||
|
||||
#. module: base
|
||||
#: help:res.country,code:0
|
||||
@@ -466,6 +476,7 @@ msgstr "Chưa được dịch"
|
||||
msgid ""
|
||||
"Context dictionary as Python expression, empty by default (Default: {})"
|
||||
msgstr ""
|
||||
"Từ điển bối cảnh là biểu thức Python, mặc định để trống (Mặc định: {})"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.ir_action_wizard
|
||||
@@ -477,13 +488,13 @@ msgstr ""
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_miscellaneoussuppliers0
|
||||
msgid "Miscellaneous Suppliers"
|
||||
msgstr ""
|
||||
msgstr "Các nhà cung cấp khác"
|
||||
|
||||
#. module: base
|
||||
#: code:addons/base/ir/ir_model.py:255
|
||||
#, python-format
|
||||
msgid "Custom fields must have a name that starts with 'x_' !"
|
||||
msgstr ""
|
||||
msgstr "Các trường được tùy chỉnh phải có tên bắt đầu bằng 'x_' !"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.server,action_id:0
|
||||
@@ -503,13 +514,13 @@ msgstr "Trích xuất hoàn tất"
|
||||
#. module: base
|
||||
#: view:ir.model:0
|
||||
msgid "Model Description"
|
||||
msgstr ""
|
||||
msgstr "Mô tả mô hình"
|
||||
|
||||
#. module: base
|
||||
#: help:ir.actions.act_window,src_model:0
|
||||
msgid ""
|
||||
"Optional model name of the objects on which this action should be visible"
|
||||
msgstr ""
|
||||
msgstr "Tùy chọn tên mô hình của đối tượng cho phép hiện hành động này"
|
||||
|
||||
#. module: base
|
||||
#: field:workflow.transition,trigger_expr_id:0
|
||||
@@ -571,12 +582,12 @@ msgstr ""
|
||||
#. module: base
|
||||
#: field:res.partner,title:0
|
||||
msgid "Partner Form"
|
||||
msgstr ""
|
||||
msgstr "Mẫu đối tác"
|
||||
|
||||
#. module: base
|
||||
#: selection:base.language.install,lang:0
|
||||
msgid "Swedish / svenska"
|
||||
msgstr ""
|
||||
msgstr "Tiếng Thụy điển"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.rs
|
||||
@@ -635,6 +646,7 @@ msgstr "Pa-pu-a Niu Ghi-nê"
|
||||
#: help:ir.actions.report.xml,report_type:0
|
||||
msgid "Report Type, e.g. pdf, html, raw, sxw, odt, html2html, mako2html, ..."
|
||||
msgstr ""
|
||||
"Loại Báo cáo, ví dụ pdf, html, raw, sxw, odt, html2html, mako2html, ..."
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_4
|
||||
@@ -736,7 +748,7 @@ msgstr ""
|
||||
#: code:addons/orm.py:1043
|
||||
#, python-format
|
||||
msgid "The unlink method is not implemented on this object !"
|
||||
msgstr ""
|
||||
msgstr "Phương thức unlink không được hiện thực trên đối tượng này !"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.actions.act_window,name:base.act_menu_create
|
||||
@@ -779,7 +791,7 @@ msgstr "ir.config_parameter"
|
||||
#. module: base
|
||||
#: selection:base.language.export,format:0
|
||||
msgid "TGZ Archive"
|
||||
msgstr ""
|
||||
msgstr "Lưu trữ TGZ"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
@@ -1678,7 +1690,8 @@ msgstr ""
|
||||
msgid ""
|
||||
"The Object name must start with x_ and not contain any special character !"
|
||||
msgstr ""
|
||||
"The Object name must start with x_ and not contain any special character !"
|
||||
"Tên đối tượng phải bắt đầu bằng x_ và không bao gồm bất kỳ ký tự đặc biệt "
|
||||
"nào!"
|
||||
|
||||
#. module: base
|
||||
#: field:ir.actions.configuration.wizard,note:0
|
||||
@@ -5705,7 +5718,7 @@ msgstr "Tự chọn"
|
||||
#. module: base
|
||||
#: view:res.request:0
|
||||
msgid "Current"
|
||||
msgstr "Current"
|
||||
msgstr "Hiện hành"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.category,name:base.res_partner_category_9
|
||||
@@ -6258,7 +6271,7 @@ msgstr ""
|
||||
#: field:res.partner.address,city:0
|
||||
#: field:res.partner.bank,city:0
|
||||
msgid "City"
|
||||
msgstr "Thành phố/Quận"
|
||||
msgstr "TP thuộc Tỉnh/Quận/Huyện"
|
||||
|
||||
#. module: base
|
||||
#: model:res.country,name:base.qa
|
||||
@@ -7083,7 +7096,7 @@ msgstr ""
|
||||
#: model:ir.actions.act_window,name:base.action_country_state
|
||||
#: model:ir.ui.menu,name:base.menu_country_state_partner
|
||||
msgid "Fed. States"
|
||||
msgstr "Bang/Tỉnh/TP"
|
||||
msgstr "Tiểu bang/TPTƯ/Tỉnh"
|
||||
|
||||
#. module: base
|
||||
#: view:ir.model:0
|
||||
@@ -7914,17 +7927,17 @@ msgstr ""
|
||||
#: field:res.currency,name:0
|
||||
#: field:res.currency.rate,currency_id:0
|
||||
msgid "Currency"
|
||||
msgstr "Tiền tệ"
|
||||
msgstr "Loại tiền tệ"
|
||||
|
||||
#. module: base
|
||||
#: field:res.partner.canal,name:0
|
||||
msgid "Channel Name"
|
||||
msgstr ""
|
||||
msgstr "Tên Kênh"
|
||||
|
||||
#. module: base
|
||||
#: view:res.lang:0
|
||||
msgid "5. %y, %Y ==> 08, 2008"
|
||||
msgstr ""
|
||||
msgstr "5. %y, %Y ==> 08, 2008"
|
||||
|
||||
#. module: base
|
||||
#: model:res.partner.title,shortcut:base.res_partner_title_ltd
|
||||
@@ -7935,12 +7948,12 @@ msgstr ""
|
||||
#: field:ir.values,res_id:0
|
||||
#: field:res.log,res_id:0
|
||||
msgid "Object ID"
|
||||
msgstr ""
|
||||
msgstr "Mã Đối tượng"
|
||||
|
||||
#. module: base
|
||||
#: view:res.company:0
|
||||
msgid "Landscape"
|
||||
msgstr ""
|
||||
msgstr "Khổ ngang"
|
||||
|
||||
#. module: base
|
||||
#: model:ir.ui.menu,name:base.menu_administration
|
||||
@@ -14813,3 +14826,6 @@ msgstr "Tiếng Nga"
|
||||
|
||||
#~ msgid "Corporation"
|
||||
#~ msgstr "Công ty"
|
||||
|
||||
#~ msgid "Ltd."
|
||||
#~ msgstr "TNHH"
|
||||
|
||||
@@ -1895,7 +1895,7 @@
|
||||
<field name="name">Config Wizard Steps</field>
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form editable="bottom" string="Config Wizard Steps">
|
||||
<form string="Config Wizard Steps">
|
||||
<group colspan="4" col="6">
|
||||
<field name="action_id"/>
|
||||
<field name="restart"/>
|
||||
|
||||
@@ -24,7 +24,6 @@ import time
|
||||
from operator import itemgetter
|
||||
|
||||
from osv import fields,osv
|
||||
import ir
|
||||
import netsvc
|
||||
from osv.orm import except_orm, browse_record
|
||||
import tools
|
||||
@@ -163,7 +162,7 @@ class ir_model(osv.osv):
|
||||
pass
|
||||
x_custom_model._name = model
|
||||
x_custom_model._module = False
|
||||
a = x_custom_model.createInstance(self.pool, '', cr)
|
||||
a = x_custom_model.createInstance(self.pool, cr)
|
||||
if (not a._columns) or ('x_name' in a._columns.keys()):
|
||||
x_name = 'x_name'
|
||||
else:
|
||||
@@ -746,7 +745,8 @@ class ir_model_data(osv.osv):
|
||||
cr.execute('select * from ir_values where model=%s and key=%s and name=%s'+where,(model, key, name))
|
||||
res = cr.fetchone()
|
||||
if not res:
|
||||
res = ir.ir_set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
|
||||
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
|
||||
res = ir_values_obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
|
||||
elif xml_id:
|
||||
cr.execute('UPDATE ir_values set value=%s WHERE model=%s and key=%s and name=%s'+where,(value, model, key, name))
|
||||
return True
|
||||
|
||||
@@ -44,15 +44,10 @@ class ir_translation(osv.osv):
|
||||
_log_access = False
|
||||
|
||||
def _get_language(self, cr, uid, context):
|
||||
lang_obj = self.pool.get('res.lang')
|
||||
lang_ids = lang_obj.search(cr, uid, [('translatable', '=', True)],
|
||||
context=context)
|
||||
langs = lang_obj.browse(cr, uid, lang_ids, context=context)
|
||||
res = [(lang.code, lang.name) for lang in langs]
|
||||
for lang_dict in tools.scan_languages():
|
||||
if lang_dict not in res:
|
||||
res.append(lang_dict)
|
||||
return res
|
||||
lang_model = self.pool.get('res.lang')
|
||||
lang_ids = lang_model.search(cr, uid, [('translatable', '=', True)], context=context)
|
||||
lang_data = lang_model.read(cr, uid, lang_ids, ['code', 'name'], context=context)
|
||||
return [(d['code'], d['name']) for d in lang_data]
|
||||
|
||||
_columns = {
|
||||
'name': fields.char('Field Name', size=128, required=True),
|
||||
|
||||
@@ -24,7 +24,7 @@ import base64
|
||||
import re
|
||||
|
||||
import tools
|
||||
import addons
|
||||
import openerp.modules
|
||||
from osv import fields, osv
|
||||
from tools.translate import _
|
||||
|
||||
@@ -229,7 +229,7 @@ class ir_ui_menu(osv.osv):
|
||||
if not path:
|
||||
return False
|
||||
path_info = path.split(',')
|
||||
icon_path = addons.get_module_resource(path_info[0],path_info[1])
|
||||
icon_path = openerp.modules.get_module_resource(path_info[0],path_info[1])
|
||||
icon_image = False
|
||||
if icon_path:
|
||||
try:
|
||||
|
||||
@@ -46,6 +46,7 @@ def _check_xml(self, cr, uid, ids, context=None):
|
||||
|
||||
class view_custom(osv.osv):
|
||||
_name = 'ir.ui.view.custom'
|
||||
_order = 'create_date desc' # search(limit=1) should return the last customization
|
||||
_columns = {
|
||||
'ref_id': fields.many2one('ir.ui.view', 'Original View', select=True),
|
||||
'user_id': fields.many2one('res.users', 'User', select=True),
|
||||
|
||||
@@ -106,12 +106,13 @@ class ir_values(osv.osv):
|
||||
value = pickle.dumps(value)
|
||||
if meta:
|
||||
meta = pickle.dumps(meta)
|
||||
assert isinstance(models, (list, tuple)), models
|
||||
ids_res = []
|
||||
for model in models:
|
||||
if isinstance(model, (list, tuple)):
|
||||
model,res_id = model
|
||||
else:
|
||||
res_id=False
|
||||
res_id = False
|
||||
if replace:
|
||||
search_criteria = [
|
||||
('key', '=', key),
|
||||
@@ -144,13 +145,17 @@ class ir_values(osv.osv):
|
||||
ids_res.append(self.create(cr, uid, vals))
|
||||
return ids_res
|
||||
|
||||
def get(self, cr, uid, key, key2, models, meta=False, context={}, res_id_req=False, without_user=True, key2_req=True):
|
||||
def get(self, cr, uid, key, key2, models, meta=False, context=None, res_id_req=False, without_user=True, key2_req=True):
|
||||
if context is None:
|
||||
context = {}
|
||||
result = []
|
||||
assert isinstance(models, (list, tuple)), models
|
||||
|
||||
for m in models:
|
||||
if isinstance(m, (list, tuple)):
|
||||
m, res_id = m
|
||||
else:
|
||||
res_id=False
|
||||
res_id = False
|
||||
|
||||
where = ['key=%s','model=%s']
|
||||
params = [key, str(m)]
|
||||
@@ -159,7 +164,7 @@ class ir_values(osv.osv):
|
||||
params.append(key2[:200])
|
||||
elif key2_req and not meta:
|
||||
where.append('key2 is null')
|
||||
if res_id_req and (models[-1][0]==m):
|
||||
if res_id_req and (models[-1][0] == m):
|
||||
if res_id:
|
||||
where.append('res_id=%s')
|
||||
params.append(res_id)
|
||||
|
||||
@@ -30,7 +30,7 @@ import urllib
|
||||
import zipfile
|
||||
import zipimport
|
||||
|
||||
import addons
|
||||
import openerp.modules as addons
|
||||
import pooler
|
||||
import release
|
||||
import tools
|
||||
@@ -79,8 +79,7 @@ class module(osv.osv):
|
||||
info = {}
|
||||
try:
|
||||
info = addons.load_information_from_description_file(name)
|
||||
if 'version' in info:
|
||||
info['version'] = release.major_version + '.' + info['version']
|
||||
info['version'] = release.major_version + '.' + info['version']
|
||||
except Exception:
|
||||
cls.__logger.debug('Error when trying to fetch informations for '
|
||||
'module %s', name, exc_info=True)
|
||||
|
||||
@@ -20,8 +20,8 @@
|
||||
##############################################################################
|
||||
|
||||
import time
|
||||
from osv import fields,osv
|
||||
|
||||
from osv import osv
|
||||
from osv import fields
|
||||
|
||||
#
|
||||
# Sale/Purchase Canal, Media
|
||||
@@ -31,7 +31,7 @@ class res_partner_canal(osv.osv):
|
||||
_description = "Channels"
|
||||
_order = 'name'
|
||||
_columns = {
|
||||
'name': fields.char('Channel Name',size=64, required=True),
|
||||
'name': fields.char('Channel Name', size=64, required=True),
|
||||
'active': fields.boolean('Active'),
|
||||
}
|
||||
_defaults = {
|
||||
@@ -39,16 +39,10 @@ class res_partner_canal(osv.osv):
|
||||
}
|
||||
res_partner_canal()
|
||||
|
||||
def _links_get(self, cr, uid, context={}):
|
||||
obj = self.pool.get('res.request.link')
|
||||
ids = obj.search(cr, uid, [])
|
||||
res = obj.read(cr, uid, ids, ['object', 'name'], context)
|
||||
return [(r['object'], r['name']) for r in res]
|
||||
|
||||
class res_partner_event(osv.osv):
|
||||
_name = "res.partner.event"
|
||||
_columns = {
|
||||
'name': fields.char('Events',size=64, required=True),
|
||||
'name': fields.char('Events', size=64, required=True),
|
||||
'description': fields.text('Description'),
|
||||
'partner_id': fields.many2one('res.partner', 'Partner', select=True),
|
||||
'date': fields.datetime('Date', size=16),
|
||||
@@ -61,5 +55,4 @@ class res_partner_event(osv.osv):
|
||||
}
|
||||
res_partner_event()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -188,40 +188,27 @@ class res_company(osv.osv):
|
||||
tools.config['root_path'], '..', 'pixmaps', 'openerp-header.png'),
|
||||
'rb') .read().encode('base64')
|
||||
|
||||
def _get_header3(self,cr,uid,ids):
|
||||
return """
|
||||
_header = """
|
||||
<header>
|
||||
<pageTemplate>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="786" height="525"/>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="%s" height="%s"/>
|
||||
<pageGraphics>
|
||||
<fill color="black"/>
|
||||
<stroke color="black"/>
|
||||
<setFont name="DejaVu Sans" size="8"/>
|
||||
<drawString x="25" y="555"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
|
||||
<drawString x="%s" y="%s"> [[ formatLang(time.strftime("%%Y-%%m-%%d"), date=True) ]] [[ time.strftime("%%H:%%M") ]]</drawString>
|
||||
<setFont name="DejaVu Sans Bold" size="10"/>
|
||||
<drawString x="382" y="555">[[ company.partner_id.name ]]</drawString>
|
||||
<drawCentredString x="%s" y="%s">[[ company.partner_id.name ]]</drawCentredString>
|
||||
<stroke color="#000000"/>
|
||||
<lines>25 550 818 550</lines>
|
||||
<lines>%s</lines>
|
||||
</pageGraphics>
|
||||
</pageTemplate>
|
||||
</header>"""
|
||||
def _get_header2(self,cr,uid,ids):
|
||||
return """
|
||||
<header>
|
||||
<pageTemplate>
|
||||
<frame id="first" x1="28.0" y1="28.0" width="539" height="772"/>
|
||||
<pageGraphics>
|
||||
<fill color="black"/>
|
||||
<stroke color="black"/>
|
||||
<setFont name="DejaVu Sans" size="8"/>
|
||||
<drawString x="1.0cm" y="28.3cm"> [[ formatLang(time.strftime("%Y-%m-%d"), date=True) ]] [[ time.strftime("%H:%M") ]]</drawString>
|
||||
<setFont name="DejaVu Sans Bold" size="10"/>
|
||||
<drawString x="9.3cm" y="28.3cm">[[ company.partner_id.name ]]</drawString>
|
||||
<stroke color="#000000"/>
|
||||
<lines>1.0cm 28.1cm 20.1cm 28.1cm</lines>
|
||||
</pageGraphics>
|
||||
</pageTemplate>
|
||||
</pageTemplate>
|
||||
</header>"""
|
||||
|
||||
_header2 = _header % (539, 772, "1.0cm", "28.3cm", "11.1cm", "28.3cm", "1.0cm 28.1cm 20.1cm 28.1cm")
|
||||
|
||||
_header3 = _header % (786, 525, 25, 555, 440, 555, "25 550 818 550")
|
||||
|
||||
def _get_header(self,cr,uid,ids):
|
||||
try :
|
||||
header_file = tools.file_open(os.path.join('base', 'report', 'corporate_rml_header.rml'))
|
||||
@@ -267,8 +254,8 @@ class res_company(osv.osv):
|
||||
_defaults = {
|
||||
'currency_id': _get_euro,
|
||||
'rml_header':_get_header,
|
||||
'rml_header2': _get_header2,
|
||||
'rml_header3': _get_header3,
|
||||
'rml_header2': _header2,
|
||||
'rml_header3': _header3,
|
||||
#'logo':_get_logo
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
import time
|
||||
import netsvc
|
||||
from osv import fields, osv
|
||||
import ir
|
||||
|
||||
from tools.misc import currency
|
||||
from tools.translate import _
|
||||
|
||||
@@ -50,9 +50,9 @@ class lang(osv.osv):
|
||||
if not lang:
|
||||
return False
|
||||
lang_ids = self.search(cr, uid, [('code','=', lang)])
|
||||
ir_values_obj = self.pool.get('ir.values')
|
||||
if not lang_ids:
|
||||
lang_id = self.load_lang(cr, uid, lang)
|
||||
self.load_lang(cr, uid, lang)
|
||||
ir_values_obj = self.pool.get('ir.values')
|
||||
default_value = ir_values_obj.get(cr, uid, 'default', False, ['res.partner'])
|
||||
if not default_value:
|
||||
ir_values_obj.set(cr, uid, 'default', False, 'lang', ['res.partner'], lang)
|
||||
|
||||
@@ -38,7 +38,7 @@ class res_widget_user(osv.osv):
|
||||
_order = "sequence"
|
||||
_columns = {
|
||||
'sequence': fields.integer('Sequence'),
|
||||
'user_id': fields.many2one('res.users','User', select=1),
|
||||
'user_id': fields.many2one('res.users','User', select=1, ondelete='cascade'),
|
||||
'widget_id': fields.many2one('res.widget','Widget',required=True, ondelete='cascade'),
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@
|
||||
<rng:element name="form">
|
||||
<rng:ref name="overload"/>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="link"/></rng:optional>
|
||||
<rng:ref name="container"/>
|
||||
@@ -126,7 +125,14 @@
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="colors"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="color"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional>
|
||||
<rng:attribute name="editable">
|
||||
<rng:choice>
|
||||
<rng:value>top</rng:value>
|
||||
<rng:value>bottom</rng:value>
|
||||
</rng:choice>
|
||||
</rng:attribute>
|
||||
</rng:optional>
|
||||
<rng:optional><rng:attribute name="toolbar"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="link"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="type"/></rng:optional>
|
||||
@@ -412,7 +418,6 @@
|
||||
<rng:attribute name="name" />
|
||||
<rng:ref name="overload"/>
|
||||
<rng:ref name="access_rights"/>
|
||||
<rng:optional><rng:attribute name="editable"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="domain_filter"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="attrs"/></rng:optional>
|
||||
<rng:optional><rng:attribute name="string"/></rng:optional>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Library-wide configuration variables.
|
||||
|
||||
For now, configuration code is in openerp.tools.config. It is in mainly
|
||||
unprocessed form, e.g. addons_path is a string with commas-separated
|
||||
paths. The aim is to have code related to configuration (command line
|
||||
parsing, configuration file loading and saving, ...) in this module
|
||||
and provide real Python variables, e.g. addons_paths is really a list
|
||||
of paths.
|
||||
|
||||
"""
|
||||
|
||||
import deprecation
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Regroup variables for deprecated features.
|
||||
|
||||
To keep the OpenERP server backward compatible with older modules, some
|
||||
additional code is needed throughout the core library. This module keeps
|
||||
track of those specific measures by providing variables that can be unset
|
||||
by the user to check if her code is future proof.
|
||||
|
||||
"""
|
||||
|
||||
# If True, the Python modules inside the openerp namespace are made available
|
||||
# without the 'openerp.' prefix. E.g. openerp.osv.osv and osv.osv refer to the
|
||||
# same module.
|
||||
# Introduced around 2011.02.
|
||||
open_openerp_namespace = True
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -1,8 +1,8 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2011 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
|
||||
@@ -15,12 +15,16 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Lower-level database access.
|
||||
|
||||
from ir import *
|
||||
This module provides access to the underlying database without going
|
||||
through the ORM. The goal is to gather sql_db.py and other various db
|
||||
code.
|
||||
|
||||
"""
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 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
|
||||
@@ -15,26 +16,29 @@
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import pickle
|
||||
import openerp.osv as osv
|
||||
import openerp.pooler as pooler
|
||||
""" Modules (also called addons) management.
|
||||
|
||||
def ir_set(cr, uid, key, key2, name, models, value, replace=True, isobject=False, meta=None):
|
||||
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
|
||||
return ir_values_obj.set(cr, uid, key, key2, name, models, value, replace, isobject, meta)
|
||||
"""
|
||||
|
||||
def ir_del(cr, uid, id):
|
||||
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
|
||||
return ir_values_obj.unlink(cr, uid, [id])
|
||||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
import openerp.modules.loading
|
||||
import openerp.modules.migration
|
||||
import openerp.modules.module
|
||||
|
||||
# TODO temporarily expose those things
|
||||
from openerp.modules.module import \
|
||||
get_modules, get_modules_with_version, \
|
||||
load_information_from_description_file, \
|
||||
get_module_resource, zip_directory, \
|
||||
get_module_path, initialize_sys_path, \
|
||||
register_module_classes, init_module_models
|
||||
|
||||
from openerp.modules.loading import load_modules
|
||||
|
||||
def ir_get(cr, uid, key, key2, models, meta=False, context=None, res_id_req=False):
|
||||
ir_values_obj = pooler.get_pool(cr.dbname).get('ir.values')
|
||||
res = ir_values_obj.get(cr, uid, key, key2, models, meta=meta, context=context, res_id_req=res_id_req)
|
||||
return res
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import openerp.modules
|
||||
|
||||
def is_initialized(cr):
|
||||
""" Check if a database has been initialized for the ORM.
|
||||
|
||||
The database can be initialized with the 'initialize' function below.
|
||||
|
||||
"""
|
||||
cr.execute("SELECT relname FROM pg_class WHERE relkind='r' AND relname='ir_module_module'")
|
||||
return len(cr.fetchall()) > 0
|
||||
|
||||
def initialize(cr):
|
||||
""" Initialize a database with for the ORM.
|
||||
|
||||
This executes base/base.sql, creates the ir_module_categories (taken
|
||||
from each module descriptor file), and creates the ir_module_module
|
||||
and ir_model_data entries.
|
||||
|
||||
"""
|
||||
f = openerp.modules.get_module_resource('base', 'base.sql')
|
||||
base_sql_file = openerp.tools.misc.file_open(f)
|
||||
try:
|
||||
cr.execute(base_sql_file.read())
|
||||
cr.commit()
|
||||
finally:
|
||||
base_sql_file.close()
|
||||
|
||||
for i in openerp.modules.get_modules():
|
||||
mod_path = openerp.modules.get_module_path(i)
|
||||
if not mod_path:
|
||||
continue
|
||||
|
||||
# This will raise an exception if no/unreadable descriptor file.
|
||||
info = openerp.modules.load_information_from_description_file(i)
|
||||
|
||||
if not info:
|
||||
continue
|
||||
categories = info['category'].split('/')
|
||||
category_id = create_categories(cr, categories)
|
||||
|
||||
if info['installable']:
|
||||
if info['active']:
|
||||
state = 'to install'
|
||||
else:
|
||||
state = 'uninstalled'
|
||||
else:
|
||||
state = 'uninstallable'
|
||||
|
||||
cr.execute('INSERT INTO ir_module_module \
|
||||
(author, website, name, shortdesc, description, \
|
||||
category_id, state, certificate, web, license) \
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id', (
|
||||
info['author'],
|
||||
info['website'], i, info['name'],
|
||||
info['description'], category_id, state, info['certificate'],
|
||||
info['web'],
|
||||
info['license']))
|
||||
id = cr.fetchone()[0]
|
||||
cr.execute('INSERT INTO ir_model_data \
|
||||
(name,model,module, res_id, noupdate) VALUES (%s,%s,%s,%s,%s)', (
|
||||
'module_meta_information', 'ir.module.module', i, id, True))
|
||||
dependencies = info['depends']
|
||||
for d in dependencies:
|
||||
cr.execute('INSERT INTO ir_module_module_dependency \
|
||||
(module_id,name) VALUES (%s, %s)', (id, d))
|
||||
cr.commit()
|
||||
|
||||
def create_categories(cr, categories):
|
||||
""" Create the ir_module_category entries for some categories.
|
||||
|
||||
categories is a list of strings forming a single category with its
|
||||
parent categories, like ['Grand Parent', 'Parent', 'Child'].
|
||||
|
||||
Return the database id of the (last) category.
|
||||
|
||||
"""
|
||||
p_id = None
|
||||
while categories:
|
||||
if p_id is not None:
|
||||
cr.execute('SELECT id \
|
||||
FROM ir_module_category \
|
||||
WHERE name=%s AND parent_id=%s', (categories[0], p_id))
|
||||
else:
|
||||
cr.execute('SELECT id \
|
||||
FROM ir_module_category \
|
||||
WHERE name=%s AND parent_id IS NULL', (categories[0],))
|
||||
c_id = cr.fetchone()
|
||||
if not c_id:
|
||||
cr.execute('INSERT INTO ir_module_category \
|
||||
(name, parent_id) \
|
||||
VALUES (%s, %s) RETURNING id', (categories[0], p_id))
|
||||
c_id = cr.fetchone()[0]
|
||||
else:
|
||||
c_id = c_id[0]
|
||||
p_id = c_id
|
||||
categories = categories[1:]
|
||||
return p_id
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,215 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Modules dependency graph. """
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import itertools
|
||||
import zipimport
|
||||
|
||||
import openerp
|
||||
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
import openerp.pooler as pooler
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
|
||||
import zipfile
|
||||
import openerp.release as release
|
||||
|
||||
import re
|
||||
import base64
|
||||
from zipfile import PyZipFile, ZIP_DEFLATED
|
||||
from cStringIO import StringIO
|
||||
|
||||
import logging
|
||||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
|
||||
class Graph(dict):
|
||||
""" Modules dependency graph.
|
||||
|
||||
The graph is a mapping from module name to Nodes.
|
||||
|
||||
"""
|
||||
|
||||
def add_node(self, name, deps):
|
||||
max_depth, father = 0, None
|
||||
for n in [Node(x, self) for x in deps]:
|
||||
if n.depth >= max_depth:
|
||||
father = n
|
||||
max_depth = n.depth
|
||||
if father:
|
||||
return father.add_child(name)
|
||||
else:
|
||||
return Node(name, self)
|
||||
|
||||
def update_from_db(self, cr):
|
||||
if not len(self):
|
||||
return
|
||||
# update the graph with values from the database (if exist)
|
||||
## First, we set the default values for each package in graph
|
||||
additional_data = dict.fromkeys(self.keys(), {'id': 0, 'state': 'uninstalled', 'dbdemo': False, 'installed_version': None})
|
||||
## Then we get the values from the database
|
||||
cr.execute('SELECT name, id, state, demo AS dbdemo, latest_version AS installed_version'
|
||||
' FROM ir_module_module'
|
||||
' WHERE name IN %s',(tuple(additional_data),)
|
||||
)
|
||||
|
||||
## and we update the default values with values from the database
|
||||
additional_data.update(dict([(x.pop('name'), x) for x in cr.dictfetchall()]))
|
||||
|
||||
for package in self.values():
|
||||
for k, v in additional_data[package.name].items():
|
||||
setattr(package, k, v)
|
||||
|
||||
def add_module(self, cr, module, force=None):
|
||||
self.add_modules(cr, [module], force)
|
||||
|
||||
def add_modules(self, cr, module_list, force=None):
|
||||
if force is None:
|
||||
force = []
|
||||
packages = []
|
||||
len_graph = len(self)
|
||||
for module in module_list:
|
||||
# This will raise an exception if no/unreadable descriptor file.
|
||||
# NOTE The call to load_information_from_description_file is already
|
||||
# done by db.initialize, so it is possible to not do it again here.
|
||||
info = openerp.modules.module.load_information_from_description_file(module)
|
||||
if info['installable']:
|
||||
packages.append((module, info)) # TODO directly a dict, like in get_modules_with_version
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: not installable, skipped' % (module))
|
||||
|
||||
dependencies = dict([(p, info['depends']) for p, info in packages])
|
||||
current, later = set([p for p, info in packages]), set()
|
||||
|
||||
while packages and current > later:
|
||||
package, info = packages[0]
|
||||
deps = info['depends']
|
||||
|
||||
# if all dependencies of 'package' are already in the graph, add 'package' in the graph
|
||||
if reduce(lambda x, y: x and y in self, deps, True):
|
||||
if not package in current:
|
||||
packages.pop(0)
|
||||
continue
|
||||
later.clear()
|
||||
current.remove(package)
|
||||
node = self.add_node(package, deps)
|
||||
node.data = info
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if package in tools.config[kind] or 'all' in tools.config[kind] or kind in force:
|
||||
setattr(node, kind, True)
|
||||
else:
|
||||
later.add(package)
|
||||
packages.append((package, info))
|
||||
packages.pop(0)
|
||||
|
||||
self.update_from_db(cr)
|
||||
|
||||
for package in later:
|
||||
unmet_deps = filter(lambda p: p not in self, dependencies[package])
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'module %s: Unmet dependencies: %s' % (package, ', '.join(unmet_deps)))
|
||||
|
||||
result = len(self) - len_graph
|
||||
if result != len(module_list):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'Not all modules have loaded.')
|
||||
return result
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
level = 0
|
||||
done = set(self.keys())
|
||||
while done:
|
||||
level_modules = [(name, module) for name, module in self.items() if module.depth==level]
|
||||
for name, module in level_modules:
|
||||
done.remove(name)
|
||||
yield module
|
||||
level += 1
|
||||
|
||||
|
||||
class Singleton(object):
|
||||
def __new__(cls, name, graph):
|
||||
if name in graph:
|
||||
inst = graph[name]
|
||||
else:
|
||||
inst = object.__new__(cls)
|
||||
inst.name = name
|
||||
graph[name] = inst
|
||||
return inst
|
||||
|
||||
|
||||
class Node(Singleton):
|
||||
""" One module in the modules dependency graph.
|
||||
|
||||
Node acts as a per-module singleton.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, name, graph):
|
||||
self.graph = graph
|
||||
if not hasattr(self, 'children'):
|
||||
self.children = []
|
||||
if not hasattr(self, 'depth'):
|
||||
self.depth = 0
|
||||
|
||||
def add_child(self, name):
|
||||
node = Node(name, self.graph)
|
||||
node.depth = self.depth + 1
|
||||
if node not in self.children:
|
||||
self.children.append(node)
|
||||
for attr in ('init', 'update', 'demo'):
|
||||
if hasattr(self, attr):
|
||||
setattr(node, attr, True)
|
||||
self.children.sort(lambda x, y: cmp(x.name, y.name))
|
||||
return node
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
super(Singleton, self).__setattr__(name, value)
|
||||
if name in ('init', 'update', 'demo'):
|
||||
tools.config[name][self.name] = 1
|
||||
for child in self.children:
|
||||
setattr(child, name, value)
|
||||
if name == 'depth':
|
||||
for child in self.children:
|
||||
setattr(child, name, value + 1)
|
||||
|
||||
def __iter__(self):
|
||||
return itertools.chain(iter(self.children), *map(iter, self.children))
|
||||
|
||||
def __str__(self):
|
||||
return self._pprint()
|
||||
|
||||
def _pprint(self, depth=0):
|
||||
s = '%s\n' % self.name
|
||||
for c in self.children:
|
||||
s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
|
||||
return s
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,421 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Modules (also called addons) management.
|
||||
|
||||
"""
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import itertools
|
||||
import zipimport
|
||||
|
||||
import openerp
|
||||
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
import openerp.pooler as pooler
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
|
||||
import zipfile
|
||||
import openerp.release as release
|
||||
|
||||
import re
|
||||
import base64
|
||||
from zipfile import PyZipFile, ZIP_DEFLATED
|
||||
from cStringIO import StringIO
|
||||
|
||||
import logging
|
||||
|
||||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
import openerp.modules.migration
|
||||
|
||||
from openerp.modules.module import \
|
||||
get_modules, get_modules_with_version, \
|
||||
load_information_from_description_file, \
|
||||
get_module_resource, zip_directory, \
|
||||
get_module_path, initialize_sys_path, \
|
||||
register_module_classes, init_module_models
|
||||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
|
||||
def open_openerp_namespace():
|
||||
# See comment for open_openerp_namespace.
|
||||
if openerp.conf.deprecation.open_openerp_namespace:
|
||||
for k, v in list(sys.modules.items()):
|
||||
if k.startswith('openerp.') and sys.modules.get(k[8:]) is None:
|
||||
sys.modules[k[8:]] = v
|
||||
|
||||
|
||||
def load_module_graph(cr, graph, status=None, perform_checks=True, skip_modules=None, report=None):
|
||||
"""Migrates+Updates or Installs all module nodes from ``graph``
|
||||
:param graph: graph of module nodes to load
|
||||
:param status: status dictionary for keeping track of progress
|
||||
:param perform_checks: whether module descriptors should be checked for validity (prints warnings
|
||||
for same cases, and even raise osv_except if certificate is invalid)
|
||||
:param skip_modules: optional list of module names (packages) which have previously been loaded and can be skipped
|
||||
:return: list of modules that were installed or updated
|
||||
"""
|
||||
def process_sql_file(cr, fp):
|
||||
queries = fp.read().split(';')
|
||||
for query in queries:
|
||||
new_query = ' '.join(query.split())
|
||||
if new_query:
|
||||
cr.execute(new_query)
|
||||
|
||||
def load_init_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'init_xml')
|
||||
|
||||
def load_update_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'update_xml')
|
||||
|
||||
def load_demo_xml(cr, m, idref, mode):
|
||||
_load_data(cr, m, idref, mode, 'demo_xml')
|
||||
|
||||
def load_data(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'data')
|
||||
|
||||
def load_demo(cr, module_name, idref, mode):
|
||||
_load_data(cr, module_name, idref, mode, 'demo')
|
||||
|
||||
def load_test(cr, module_name, idref, mode):
|
||||
cr.commit()
|
||||
if not tools.config.options['test_disable']:
|
||||
try:
|
||||
_load_data(cr, module_name, idref, mode, 'test')
|
||||
except Exception, e:
|
||||
logging.getLogger('test').exception('Tests failed to execute in module %s', module_name)
|
||||
finally:
|
||||
if tools.config.options['test_commit']:
|
||||
cr.commit()
|
||||
else:
|
||||
cr.rollback()
|
||||
|
||||
def _load_data(cr, module_name, idref, mode, kind):
|
||||
"""
|
||||
|
||||
kind: data, demo, test, init_xml, update_xml, demo_xml.
|
||||
|
||||
noupdate is False, unless it is demo data or it is csv data in
|
||||
init mode.
|
||||
|
||||
"""
|
||||
for filename in package.data[kind]:
|
||||
log = logging.getLogger('init')
|
||||
log.info("module %s: loading %s", module_name, filename)
|
||||
_, ext = os.path.splitext(filename)
|
||||
pathname = os.path.join(module_name, filename)
|
||||
fp = tools.file_open(pathname)
|
||||
noupdate = False
|
||||
if kind in ('demo', 'demo_xml'):
|
||||
noupdate = True
|
||||
try:
|
||||
if ext == '.csv':
|
||||
if kind in ('init', 'init_xml'):
|
||||
noupdate = True
|
||||
tools.convert_csv_import(cr, module_name, pathname, fp.read(), idref, mode, noupdate)
|
||||
elif ext == '.sql':
|
||||
process_sql_file(cr, fp)
|
||||
elif ext == '.yml':
|
||||
tools.convert_yaml_import(cr, module_name, fp, idref, mode, noupdate)
|
||||
else:
|
||||
tools.convert_xml_import(cr, module_name, fp, idref, mode, noupdate, report)
|
||||
finally:
|
||||
fp.close()
|
||||
|
||||
if status is None:
|
||||
status = {}
|
||||
|
||||
processed_modules = []
|
||||
statusi = 0
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
migrations = openerp.modules.migration.MigrationManager(cr, graph)
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'loading %d packages..' % len(graph))
|
||||
|
||||
# register, instanciate and initialize models for each modules
|
||||
for package in graph:
|
||||
if skip_modules and package.name in skip_modules:
|
||||
continue
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: loading objects' % package.name)
|
||||
migrations.migrate_module(package, 'pre')
|
||||
register_module_classes(package.name)
|
||||
models = pool.instanciate(package.name, cr)
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
init_module_models(cr, package.name, models)
|
||||
cr.commit()
|
||||
|
||||
# load data for each modules
|
||||
modobj = pool.get('ir.module.module')
|
||||
for package in graph:
|
||||
status['progress'] = (float(statusi)+0.1) / len(graph)
|
||||
m = package.name
|
||||
mid = package.id
|
||||
|
||||
if skip_modules and m in skip_modules:
|
||||
continue
|
||||
|
||||
if perform_checks:
|
||||
modobj.check(cr, 1, [mid])
|
||||
|
||||
idref = {}
|
||||
status['progress'] = (float(statusi)+0.4) / len(graph)
|
||||
|
||||
mode = 'update'
|
||||
if hasattr(package, 'init') or package.state == 'to install':
|
||||
mode = 'init'
|
||||
|
||||
if hasattr(package, 'init') or hasattr(package, 'update') or package.state in ('to install', 'to upgrade'):
|
||||
if package.state=='to upgrade':
|
||||
# upgrading the module information
|
||||
modobj.write(cr, 1, [mid], modobj.get_values_from_terp(package.data))
|
||||
load_init_xml(cr, m, idref, mode)
|
||||
load_update_xml(cr, m, idref, mode)
|
||||
load_data(cr, m, idref, mode)
|
||||
if hasattr(package, 'demo') or (package.dbdemo and package.state != 'installed'):
|
||||
status['progress'] = (float(statusi)+0.75) / len(graph)
|
||||
load_demo_xml(cr, m, idref, mode)
|
||||
load_demo(cr, m, idref, mode)
|
||||
cr.execute('update ir_module_module set demo=%s where id=%s', (True, mid))
|
||||
|
||||
# launch tests only in demo mode, as most tests will depend
|
||||
# on demo data. Other tests can be added into the regular
|
||||
# 'data' section, but should probably not alter the data,
|
||||
# as there is no rollback.
|
||||
load_test(cr, m, idref, mode)
|
||||
|
||||
processed_modules.append(package.name)
|
||||
|
||||
migrations.migrate_module(package, 'post')
|
||||
|
||||
ver = release.major_version + '.' + package.data['version']
|
||||
# Set new modules and dependencies
|
||||
modobj.write(cr, 1, [mid], {'state': 'installed', 'latest_version': ver})
|
||||
cr.commit()
|
||||
# Update translations for all installed languages
|
||||
modobj.update_translations(cr, 1, [mid], None)
|
||||
cr.commit()
|
||||
|
||||
package.state = 'installed'
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
if hasattr(package, kind):
|
||||
delattr(package, kind)
|
||||
|
||||
statusi += 1
|
||||
|
||||
cr.commit()
|
||||
|
||||
return processed_modules
|
||||
|
||||
def _check_module_names(cr, module_names):
|
||||
mod_names = set(module_names)
|
||||
if 'base' in mod_names:
|
||||
# ignore dummy 'all' module
|
||||
if 'all' in mod_names:
|
||||
mod_names.remove('all')
|
||||
if mod_names:
|
||||
cr.execute("SELECT count(id) AS count FROM ir_module_module WHERE name in %s", (tuple(mod_names),))
|
||||
if cr.dictfetchone()['count'] != len(mod_names):
|
||||
# find out what module name(s) are incorrect:
|
||||
cr.execute("SELECT name FROM ir_module_module")
|
||||
incorrect_names = mod_names.difference([x['name'] for x in cr.dictfetchall()])
|
||||
logging.getLogger('init').warning('invalid module names, ignored: %s', ", ".join(incorrect_names))
|
||||
|
||||
def load_modules(db, force_demo=False, status=None, update_module=False):
|
||||
# TODO status['progress'] reporting is broken: used twice (and reset each
|
||||
# time to zero) in load_module_graph, not fine-grained enough.
|
||||
# It should be a method exposed by the pool.
|
||||
|
||||
initialize_sys_path()
|
||||
|
||||
open_openerp_namespace()
|
||||
|
||||
force = []
|
||||
if force_demo:
|
||||
force.append('demo')
|
||||
|
||||
cr = db.cursor()
|
||||
try:
|
||||
if not openerp.modules.db.is_initialized(cr):
|
||||
logger.notifyChannel("init", netsvc.LOG_INFO, "init db")
|
||||
openerp.modules.db.initialize(cr)
|
||||
tools.config["init"]["all"] = 1
|
||||
tools.config['update']['all'] = 1
|
||||
if not tools.config['without_demo']:
|
||||
tools.config["demo"]['all'] = 1
|
||||
|
||||
# This is a brand new pool, just created in pooler.get_db_and_pool()
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
|
||||
processed_modules = []
|
||||
report = tools.assertion_report()
|
||||
# NOTE: Try to also load the modules that have been marked as uninstallable previously...
|
||||
STATES_TO_LOAD = ['installed', 'to upgrade', 'uninstallable']
|
||||
if 'base' in tools.config['update'] or 'all' in tools.config['update']:
|
||||
cr.execute("update ir_module_module set state=%s where name=%s and state=%s", ('to upgrade', 'base', 'installed'))
|
||||
|
||||
# STEP 1: LOAD BASE (must be done before module dependencies can be computed for later steps)
|
||||
graph = openerp.modules.graph.Graph()
|
||||
graph.add_module(cr, 'base', force)
|
||||
if not graph:
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, 'module base cannot be loaded! (hint: verify addons-path)')
|
||||
raise osv.osv.except_osv(_('Could not load base module'), _('module base cannot be loaded! (hint: verify addons-path)'))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, perform_checks=(not update_module), report=report))
|
||||
|
||||
if tools.config['load_language']:
|
||||
for lang in tools.config['load_language'].split(','):
|
||||
tools.load_language(cr, lang)
|
||||
|
||||
# STEP 2: Mark other modules to be loaded/updated
|
||||
if update_module:
|
||||
modobj = pool.get('ir.module.module')
|
||||
if ('base' in tools.config['init']) or ('base' in tools.config['update']):
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'updating modules list')
|
||||
modobj.update_list(cr, 1)
|
||||
|
||||
_check_module_names(cr, itertools.chain(tools.config['init'].keys(), tools.config['update'].keys()))
|
||||
|
||||
mods = [k for k in tools.config['init'] if tools.config['init'][k]]
|
||||
if mods:
|
||||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'uninstalled'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_install(cr, 1, ids)
|
||||
|
||||
mods = [k for k in tools.config['update'] if tools.config['update'][k]]
|
||||
if mods:
|
||||
ids = modobj.search(cr, 1, ['&', ('state', '=', 'installed'), ('name', 'in', mods)])
|
||||
if ids:
|
||||
modobj.button_upgrade(cr, 1, ids)
|
||||
|
||||
cr.execute("update ir_module_module set state=%s where name=%s", ('installed', 'base'))
|
||||
|
||||
STATES_TO_LOAD += ['to install']
|
||||
|
||||
|
||||
# STEP 3: Load marked modules (skipping base which was done in STEP 1)
|
||||
loop_guardrail = 0
|
||||
while True:
|
||||
loop_guardrail += 1
|
||||
if loop_guardrail > 100:
|
||||
raise ValueError('Possible recursive module tree detected, aborting.')
|
||||
cr.execute("SELECT name from ir_module_module WHERE state IN %s" ,(tuple(STATES_TO_LOAD),))
|
||||
|
||||
module_list = [name for (name,) in cr.fetchall() if name not in graph]
|
||||
if not module_list:
|
||||
break
|
||||
|
||||
new_modules_in_graph = graph.add_modules(cr, module_list, force)
|
||||
if new_modules_in_graph == 0:
|
||||
# nothing to load
|
||||
break
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_DEBUG, 'Updating graph with %d more modules' % (len(module_list)))
|
||||
processed_modules.extend(load_module_graph(cr, graph, status, report=report, skip_modules=processed_modules))
|
||||
|
||||
# load custom models
|
||||
cr.execute('select model from ir_model where state=%s', ('manual',))
|
||||
for model in cr.dictfetchall():
|
||||
pool.get('ir.model').instanciate(cr, 1, model['model'], {})
|
||||
|
||||
# STEP 4: Finish and cleanup
|
||||
if processed_modules:
|
||||
cr.execute("""select model,name from ir_model where id NOT IN (select distinct model_id from ir_model_access)""")
|
||||
for (model, name) in cr.fetchall():
|
||||
model_obj = pool.get(model)
|
||||
if model_obj and not isinstance(model_obj, osv.osv.osv_memory):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'object %s (%s) has no access rules!' % (model, name))
|
||||
|
||||
# Temporary warning while we remove access rights on osv_memory objects, as they have
|
||||
# been replaced by owner-only access rights
|
||||
cr.execute("""select distinct mod.model, mod.name from ir_model_access acc, ir_model mod where acc.model_id = mod.id""")
|
||||
for (model, name) in cr.fetchall():
|
||||
model_obj = pool.get(model)
|
||||
if isinstance(model_obj, osv.osv.osv_memory):
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'In-memory object %s (%s) should not have explicit access rules!' % (model, name))
|
||||
|
||||
cr.execute("SELECT model from ir_model")
|
||||
for (model,) in cr.fetchall():
|
||||
obj = pool.get(model)
|
||||
if obj:
|
||||
obj._check_removed_columns(cr, log=True)
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, "Model %s is referenced but not present in the orm pool!" % model)
|
||||
|
||||
# Cleanup orphan records
|
||||
pool.get('ir.model.data')._process_end(cr, 1, processed_modules)
|
||||
|
||||
if report.get_report():
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, report)
|
||||
|
||||
for kind in ('init', 'demo', 'update'):
|
||||
tools.config[kind] = {}
|
||||
|
||||
cr.commit()
|
||||
if update_module:
|
||||
# Remove records referenced from ir_model_data for modules to be
|
||||
# removed (and removed the references from ir_model_data).
|
||||
cr.execute("select id,name from ir_module_module where state=%s", ('to remove',))
|
||||
for mod_id, mod_name in cr.fetchall():
|
||||
cr.execute('select model,res_id from ir_model_data where noupdate=%s and module=%s order by id desc', (False, mod_name,))
|
||||
for rmod, rid in cr.fetchall():
|
||||
uid = 1
|
||||
rmod_module= pool.get(rmod)
|
||||
if rmod_module:
|
||||
# TODO group by module so that we can delete multiple ids in a call
|
||||
rmod_module.unlink(cr, uid, [rid])
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'Could not locate %s to remove res=%d' % (rmod,rid))
|
||||
cr.execute('delete from ir_model_data where noupdate=%s and module=%s', (False, mod_name,))
|
||||
cr.commit()
|
||||
|
||||
# Remove menu items that are not referenced by any of other
|
||||
# (child) menu item, ir_values, or ir_model_data.
|
||||
# This code could be a method of ir_ui_menu.
|
||||
# TODO: remove menu without actions of children
|
||||
while True:
|
||||
cr.execute('''delete from
|
||||
ir_ui_menu
|
||||
where
|
||||
(id not IN (select parent_id from ir_ui_menu where parent_id is not null))
|
||||
and
|
||||
(id not IN (select res_id from ir_values where model='ir.ui.menu'))
|
||||
and
|
||||
(id not IN (select res_id from ir_model_data where model='ir.ui.menu'))''')
|
||||
cr.commit()
|
||||
if not cr.rowcount:
|
||||
break
|
||||
else:
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'removed %d unused menus' % (cr.rowcount,))
|
||||
|
||||
# Pretend that modules to be removed are actually uninstalled.
|
||||
cr.execute("update ir_module_module set state=%s where state=%s", ('uninstalled', 'to remove',))
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,204 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Modules migration handling. """
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import itertools
|
||||
import zipimport
|
||||
|
||||
import openerp
|
||||
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
import openerp.pooler as pooler
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
|
||||
import zipfile
|
||||
import openerp.release as release
|
||||
|
||||
import re
|
||||
import base64
|
||||
from zipfile import PyZipFile, ZIP_DEFLATED
|
||||
from cStringIO import StringIO
|
||||
|
||||
import logging
|
||||
|
||||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
|
||||
class MigrationManager(object):
|
||||
"""
|
||||
This class manage the migration of modules
|
||||
Migrations files must be python files containing a "migrate(cr, installed_version)" function.
|
||||
Theses files must respect a directory tree structure: A 'migrations' folder which containt a
|
||||
folder by version. Version can be 'module' version or 'server.module' version (in this case,
|
||||
the files will only be processed by this version of the server). Python file names must start
|
||||
by 'pre' or 'post' and will be executed, respectively, before and after the module initialisation
|
||||
Example:
|
||||
|
||||
<moduledir>
|
||||
`-- migrations
|
||||
|-- 1.0
|
||||
| |-- pre-update_table_x.py
|
||||
| |-- pre-update_table_y.py
|
||||
| |-- post-clean-data.py
|
||||
| `-- README.txt # not processed
|
||||
|-- 5.0.1.1 # files in this folder will be executed only on a 5.0 server
|
||||
| |-- pre-delete_table_z.py
|
||||
| `-- post-clean-data.py
|
||||
`-- foo.py # not processed
|
||||
|
||||
This similar structure is generated by the maintenance module with the migrations files get by
|
||||
the maintenance contract
|
||||
|
||||
"""
|
||||
def __init__(self, cr, graph):
|
||||
self.cr = cr
|
||||
self.graph = graph
|
||||
self.migrations = {}
|
||||
self._get_files()
|
||||
|
||||
def _get_files(self):
|
||||
|
||||
"""
|
||||
import addons.base.maintenance.utils as maintenance_utils
|
||||
maintenance_utils.update_migrations_files(self.cr)
|
||||
#"""
|
||||
|
||||
for pkg in self.graph:
|
||||
self.migrations[pkg.name] = {}
|
||||
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
|
||||
continue
|
||||
|
||||
get_module_filetree = openerp.modules.module.get_module_filetree
|
||||
self.migrations[pkg.name]['module'] = get_module_filetree(pkg.name, 'migrations') or {}
|
||||
self.migrations[pkg.name]['maintenance'] = get_module_filetree('base', 'maintenance/migrations/' + pkg.name) or {}
|
||||
|
||||
def migrate_module(self, pkg, stage):
|
||||
assert stage in ('pre', 'post')
|
||||
stageformat = {'pre': '[>%s]',
|
||||
'post': '[%s>]',
|
||||
}
|
||||
|
||||
if not (hasattr(pkg, 'update') or pkg.state == 'to upgrade'):
|
||||
return
|
||||
|
||||
def convert_version(version):
|
||||
if version.startswith(release.major_version) and version != release.major_version:
|
||||
return version # the version number already containt the server version
|
||||
return "%s.%s" % (release.major_version, version)
|
||||
|
||||
def _get_migration_versions(pkg):
|
||||
def __get_dir(tree):
|
||||
return [d for d in tree if tree[d] is not None]
|
||||
|
||||
versions = list(set(
|
||||
__get_dir(self.migrations[pkg.name]['module']) +
|
||||
__get_dir(self.migrations[pkg.name]['maintenance'])
|
||||
))
|
||||
versions.sort(key=lambda k: parse_version(convert_version(k)))
|
||||
return versions
|
||||
|
||||
def _get_migration_files(pkg, version, stage):
|
||||
""" return a list of tuple (module, file)
|
||||
"""
|
||||
m = self.migrations[pkg.name]
|
||||
lst = []
|
||||
|
||||
mapping = {'module': opj(pkg.name, 'migrations'),
|
||||
'maintenance': opj('base', 'maintenance', 'migrations', pkg.name),
|
||||
}
|
||||
|
||||
for x in mapping.keys():
|
||||
if version in m[x]:
|
||||
for f in m[x][version]:
|
||||
if m[x][version][f] is not None:
|
||||
continue
|
||||
if not f.startswith(stage + '-'):
|
||||
continue
|
||||
lst.append(opj(mapping[x], version, f))
|
||||
lst.sort()
|
||||
return lst
|
||||
|
||||
def mergedict(a, b):
|
||||
a = a.copy()
|
||||
a.update(b)
|
||||
return a
|
||||
|
||||
from openerp.tools.parse_version import parse_version
|
||||
|
||||
parsed_installed_version = parse_version(pkg.installed_version or '')
|
||||
current_version = parse_version(convert_version(pkg.data['version']))
|
||||
|
||||
versions = _get_migration_versions(pkg)
|
||||
|
||||
for version in versions:
|
||||
if parsed_installed_version < parse_version(convert_version(version)) <= current_version:
|
||||
|
||||
strfmt = {'addon': pkg.name,
|
||||
'stage': stage,
|
||||
'version': stageformat[stage] % version,
|
||||
}
|
||||
|
||||
for pyfile in _get_migration_files(pkg, version, stage):
|
||||
name, ext = os.path.splitext(os.path.basename(pyfile))
|
||||
if ext.lower() != '.py':
|
||||
continue
|
||||
mod = fp = fp2 = None
|
||||
try:
|
||||
fp = tools.file_open(pyfile)
|
||||
|
||||
# imp.load_source need a real file object, so we create
|
||||
# one from the file-like object we get from file_open
|
||||
fp2 = os.tmpfile()
|
||||
fp2.write(fp.read())
|
||||
fp2.seek(0)
|
||||
try:
|
||||
mod = imp.load_source(name, pyfile, fp2)
|
||||
logger.notifyChannel('migration', netsvc.LOG_INFO, 'module %(addon)s: Running migration %(version)s %(name)s' % mergedict({'name': mod.__name__}, strfmt))
|
||||
mod.migrate(self.cr, pkg.installed_version)
|
||||
except ImportError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Unable to load %(stage)s-migration file %(file)s' % mergedict({'file': pyfile}, strfmt))
|
||||
raise
|
||||
except AttributeError:
|
||||
logger.notifyChannel('migration', netsvc.LOG_ERROR, 'module %(addon)s: Each %(stage)s-migration file must have a "migrate(cr, installed_version)" function' % strfmt)
|
||||
except:
|
||||
raise
|
||||
finally:
|
||||
if fp:
|
||||
fp.close()
|
||||
if fp2:
|
||||
fp2.close()
|
||||
if mod:
|
||||
del mod
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,379 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
# Copyright (C) 2010-2011 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
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import os, sys, imp
|
||||
from os.path import join as opj
|
||||
import itertools
|
||||
import zipimport
|
||||
|
||||
import openerp
|
||||
|
||||
import openerp.osv as osv
|
||||
import openerp.tools as tools
|
||||
import openerp.tools.osutil as osutil
|
||||
from openerp.tools.safe_eval import safe_eval as eval
|
||||
import openerp.pooler as pooler
|
||||
from openerp.tools.translate import _
|
||||
|
||||
import openerp.netsvc as netsvc
|
||||
|
||||
import zipfile
|
||||
import openerp.release as release
|
||||
|
||||
import re
|
||||
import base64
|
||||
from zipfile import PyZipFile, ZIP_DEFLATED
|
||||
from cStringIO import StringIO
|
||||
|
||||
import logging
|
||||
|
||||
import openerp.modules.db
|
||||
import openerp.modules.graph
|
||||
|
||||
_ad = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons') # default addons path (base)
|
||||
ad_paths = []
|
||||
|
||||
# Modules already loaded
|
||||
loaded = []
|
||||
|
||||
logger = netsvc.Logger()
|
||||
|
||||
def initialize_sys_path():
|
||||
global ad_paths
|
||||
|
||||
if ad_paths:
|
||||
return
|
||||
|
||||
ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
|
||||
|
||||
sys.path.insert(1, _ad)
|
||||
|
||||
ad_cnt=1
|
||||
for adp in ad_paths:
|
||||
if adp != _ad:
|
||||
sys.path.insert(ad_cnt, adp)
|
||||
ad_cnt+=1
|
||||
|
||||
ad_paths.append(_ad) # for get_module_path
|
||||
|
||||
|
||||
def get_module_path(module, downloaded=False):
|
||||
"""Return the path of the given module.
|
||||
|
||||
Search the addons paths and return the first path where the given
|
||||
module is found. If downloaded is True, return the default addons
|
||||
path if nothing else is found.
|
||||
|
||||
"""
|
||||
initialize_sys_path()
|
||||
for adp in ad_paths:
|
||||
if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
|
||||
return opj(adp, module)
|
||||
|
||||
if downloaded:
|
||||
return opj(_ad, module)
|
||||
logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
|
||||
return False
|
||||
|
||||
|
||||
def get_module_filetree(module, dir='.'):
|
||||
path = get_module_path(module)
|
||||
if not path:
|
||||
return False
|
||||
|
||||
dir = os.path.normpath(dir)
|
||||
if dir == '.':
|
||||
dir = ''
|
||||
if dir.startswith('..') or (dir and dir[0] == '/'):
|
||||
raise Exception('Cannot access file outside the module')
|
||||
|
||||
if not os.path.isdir(path):
|
||||
# zipmodule
|
||||
zip = zipfile.ZipFile(path + ".zip")
|
||||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
else:
|
||||
files = osutil.listdir(path, True)
|
||||
|
||||
tree = {}
|
||||
for f in files:
|
||||
if not f.startswith(dir):
|
||||
continue
|
||||
|
||||
if dir:
|
||||
f = f[len(dir)+int(not dir.endswith('/')):]
|
||||
lst = f.split(os.sep)
|
||||
current = tree
|
||||
while len(lst) != 1:
|
||||
current = current.setdefault(lst.pop(0), {})
|
||||
current[lst.pop(0)] = None
|
||||
|
||||
return tree
|
||||
|
||||
def zip_directory(directory, b64enc=True, src=True):
|
||||
"""Compress a directory
|
||||
|
||||
@param directory: The directory to compress
|
||||
@param base64enc: if True the function will encode the zip file with base64
|
||||
@param src: Integrate the source files
|
||||
|
||||
@return: a string containing the zip file
|
||||
"""
|
||||
|
||||
RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
|
||||
|
||||
def _zippy(archive, path, src=True):
|
||||
path = os.path.abspath(path)
|
||||
base = os.path.basename(path)
|
||||
for f in osutil.listdir(path, True):
|
||||
bf = os.path.basename(f)
|
||||
if not RE_exclude.search(bf) and (src or bf in ('__openerp__.py', '__terp__.py') or not bf.endswith('.py')):
|
||||
archive.write(os.path.join(path, f), os.path.join(base, f))
|
||||
|
||||
archname = StringIO()
|
||||
archive = PyZipFile(archname, "w", ZIP_DEFLATED)
|
||||
|
||||
# for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
|
||||
directory = tools.ustr(directory).encode('utf-8')
|
||||
|
||||
archive.writepy(directory)
|
||||
_zippy(archive, directory, src=src)
|
||||
archive.close()
|
||||
archive_data = archname.getvalue()
|
||||
archname.close()
|
||||
|
||||
if b64enc:
|
||||
return base64.encodestring(archive_data)
|
||||
|
||||
return archive_data
|
||||
|
||||
def get_module_as_zip(modulename, b64enc=True, src=True):
|
||||
"""Generate a module as zip file with the source or not and can do a base64 encoding
|
||||
|
||||
@param modulename: The module name
|
||||
@param b64enc: if True the function will encode the zip file with base64
|
||||
@param src: Integrate the source files
|
||||
|
||||
@return: a stream to store in a file-like object
|
||||
"""
|
||||
|
||||
ap = get_module_path(str(modulename))
|
||||
if not ap:
|
||||
raise Exception('Unable to find path for module %s' % modulename)
|
||||
|
||||
ap = ap.encode('utf8')
|
||||
if os.path.isfile(ap + '.zip'):
|
||||
val = file(ap + '.zip', 'rb').read()
|
||||
if b64enc:
|
||||
val = base64.encodestring(val)
|
||||
else:
|
||||
val = zip_directory(ap, b64enc, src)
|
||||
|
||||
return val
|
||||
|
||||
|
||||
def get_module_resource(module, *args):
|
||||
"""Return the full path of a resource of the given module.
|
||||
|
||||
@param module: the module
|
||||
@param args: the resource path components
|
||||
|
||||
@return: absolute path to the resource
|
||||
|
||||
TODO name it get_resource_path
|
||||
TODO make it available inside on osv object (self.get_resource_path)
|
||||
"""
|
||||
a = get_module_path(module)
|
||||
if not a: return False
|
||||
resource_path = opj(a, *args)
|
||||
if zipfile.is_zipfile( a +'.zip') :
|
||||
zip = zipfile.ZipFile( a + ".zip")
|
||||
files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
|
||||
resource_path = '/'.join(args)
|
||||
if resource_path in files:
|
||||
return opj(a, resource_path)
|
||||
elif os.path.exists(resource_path):
|
||||
return resource_path
|
||||
return False
|
||||
|
||||
|
||||
def load_information_from_description_file(module):
|
||||
"""
|
||||
:param module: The name of the module (sale, purchase, ...)
|
||||
"""
|
||||
|
||||
terp_file = get_module_resource(module, '__openerp__.py')
|
||||
if not terp_file:
|
||||
terp_file = get_module_resource(module, '__terp__.py')
|
||||
mod_path = get_module_path(module)
|
||||
if terp_file:
|
||||
info = {}
|
||||
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
||||
terp_f = tools.file_open(terp_file)
|
||||
try:
|
||||
info = eval(terp_f.read())
|
||||
except Exception:
|
||||
logger.notifyChannel('modules', netsvc.LOG_ERROR,
|
||||
'module %s: exception while evaluating file %s' %
|
||||
(module, terp_file))
|
||||
raise
|
||||
finally:
|
||||
terp_f.close()
|
||||
# TODO the version should probably be mandatory
|
||||
info.setdefault('version', '0')
|
||||
info.setdefault('category', 'Uncategorized')
|
||||
info.setdefault('depends', [])
|
||||
info.setdefault('author', '')
|
||||
info.setdefault('website', '')
|
||||
info.setdefault('name', False)
|
||||
info.setdefault('description', '')
|
||||
info['certificate'] = info.get('certificate') or None
|
||||
info['web'] = info.get('web') or False
|
||||
info['license'] = info.get('license') or 'AGPL-3'
|
||||
info.setdefault('installable', True)
|
||||
info.setdefault('active', False)
|
||||
for kind in ['data', 'demo', 'test',
|
||||
'init_xml', 'update_xml', 'demo_xml']:
|
||||
info.setdefault(kind, [])
|
||||
return info
|
||||
|
||||
#TODO: refactor the logger in this file to follow the logging guidelines
|
||||
# for 6.0
|
||||
logging.getLogger('modules').debug('module %s: no descriptor file'
|
||||
' found: __openerp__.py or __terp__.py (deprecated)', module)
|
||||
return {}
|
||||
|
||||
|
||||
def init_module_models(cr, module_name, obj_list):
|
||||
""" Initialize a list of models.
|
||||
|
||||
Call _auto_init and init on each model to create or update the
|
||||
database tables supporting the models.
|
||||
|
||||
TODO better explanation of _auto_init and init.
|
||||
|
||||
"""
|
||||
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO,
|
||||
'module %s: creating or updating database tables' % module_name)
|
||||
# TODO _auto_init doesn't seem to return anything
|
||||
# so this todo list would be useless.
|
||||
todo = []
|
||||
for obj in obj_list:
|
||||
try:
|
||||
# TODO the module in the context doesn't seem usefull:
|
||||
# it is available (at least) in the class' _module attribute.
|
||||
# (So module_name would be useless too.)
|
||||
result = obj._auto_init(cr, {'module': module_name})
|
||||
except Exception, e:
|
||||
raise
|
||||
if result:
|
||||
todo += result
|
||||
if hasattr(obj, 'init'):
|
||||
obj.init(cr)
|
||||
cr.commit()
|
||||
todo.sort()
|
||||
for t in todo:
|
||||
t[1](cr, *t[2])
|
||||
cr.commit()
|
||||
|
||||
|
||||
def load_module(module_name):
|
||||
""" Load a Python module found on the addons paths."""
|
||||
fm = imp.find_module(module_name, ad_paths)
|
||||
try:
|
||||
imp.load_module(module_name, *fm)
|
||||
finally:
|
||||
if fm[0]:
|
||||
fm[0].close()
|
||||
|
||||
|
||||
def register_module_classes(m):
|
||||
""" Register module named m, if not already registered.
|
||||
|
||||
This will load the module and register all of its models. (Actually, the
|
||||
explicit constructor call of each of the models inside the module will
|
||||
register them.)
|
||||
|
||||
"""
|
||||
|
||||
def log(e):
|
||||
mt = isinstance(e, zipimport.ZipImportError) and 'zip ' or ''
|
||||
msg = "Couldn't load %smodule %s" % (mt, m)
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, msg)
|
||||
logger.notifyChannel('init', netsvc.LOG_CRITICAL, e)
|
||||
|
||||
global loaded
|
||||
if m in loaded:
|
||||
return
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
|
||||
mod_path = get_module_path(m)
|
||||
|
||||
initialize_sys_path()
|
||||
try:
|
||||
zip_mod_path = mod_path + '.zip'
|
||||
if not os.path.isfile(zip_mod_path):
|
||||
load_module(m)
|
||||
else:
|
||||
zimp = zipimport.zipimporter(zip_mod_path)
|
||||
zimp.load_module(m)
|
||||
except Exception, e:
|
||||
log(e)
|
||||
raise
|
||||
else:
|
||||
loaded.append(m)
|
||||
|
||||
|
||||
def get_modules():
|
||||
"""Returns the list of module names
|
||||
"""
|
||||
def listdir(dir):
|
||||
def clean(name):
|
||||
name = os.path.basename(name)
|
||||
if name[-4:] == '.zip':
|
||||
name = name[:-4]
|
||||
return name
|
||||
|
||||
def is_really_module(name):
|
||||
name = opj(dir, name)
|
||||
return os.path.isdir(name) or zipfile.is_zipfile(name)
|
||||
return map(clean, filter(is_really_module, os.listdir(dir)))
|
||||
|
||||
plist = []
|
||||
initialize_sys_path()
|
||||
for ad in ad_paths:
|
||||
plist.extend(listdir(ad))
|
||||
return list(set(plist))
|
||||
|
||||
|
||||
def get_modules_with_version():
|
||||
modules = get_modules()
|
||||
res = {}
|
||||
for module in modules:
|
||||
try:
|
||||
info = load_information_from_description_file(module)
|
||||
res[module] = "%s.%s" % (release.major_version, info['version'])
|
||||
except Exception, e:
|
||||
continue
|
||||
return res
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
@@ -0,0 +1,110 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# OpenERP, Open Source Management Solution
|
||||
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
""" Models registries.
|
||||
|
||||
"""
|
||||
|
||||
import openerp.sql_db
|
||||
|
||||
|
||||
class BoundRegistry(object):
|
||||
""" Model registry/database connection pair."""
|
||||
def __init__(self, db, registry):
|
||||
self.db = db
|
||||
self.registry = registry
|
||||
|
||||
|
||||
class RegistryManager(object):
|
||||
""" Model registries manager.
|
||||
|
||||
The manager is responsible for creation and deletion of bound model
|
||||
registries (essentially database connection/model registry pairs).
|
||||
|
||||
"""
|
||||
|
||||
# TODO maybe should receive the addons paths
|
||||
def __init__(self):
|
||||
# Mapping between db name and bound model registry.
|
||||
# Accessed through the methods below.
|
||||
self.bound_registries = {}
|
||||
|
||||
|
||||
def get(self, db_name, force_demo=False, status=None, update_module=False,
|
||||
pooljobs=True):
|
||||
""" Return a bound registry for a given database name."""
|
||||
|
||||
if db_name in self.bound_registries:
|
||||
bound_registry = self.bound_registries[db_name]
|
||||
else:
|
||||
bound_registry = self.new(db_name, force_demo, status,
|
||||
update_module, pooljobs)
|
||||
return bound_registry
|
||||
|
||||
|
||||
def new(self, db_name, force_demo=False, status=None,
|
||||
update_module=False, pooljobs=True):
|
||||
""" Create and return a new bound registry for a given database name.
|
||||
|
||||
The (possibly) previous bound registry for that database name is
|
||||
discarded.
|
||||
|
||||
"""
|
||||
|
||||
import openerp.modules
|
||||
import openerp.osv.osv as osv_osv
|
||||
db = openerp.sql_db.db_connect(db_name)
|
||||
pool = osv_osv.osv_pool()
|
||||
|
||||
# Initializing a registry will call general code which will in turn
|
||||
# call registries.get (this object) to obtain the registry being
|
||||
# initialized. Make it available in the bound_registries dictionary
|
||||
# then remove it if an exception is raised.
|
||||
self.delete(db_name)
|
||||
bound_registry = BoundRegistry(db, pool)
|
||||
self.bound_registries[db_name] = bound_registry
|
||||
try:
|
||||
# This should be a method on BoundRegistry
|
||||
openerp.modules.load_modules(db, force_demo, status, update_module)
|
||||
except Exception:
|
||||
del self.bound_registries[db_name]
|
||||
raise
|
||||
|
||||
cr = db.cursor()
|
||||
try:
|
||||
pool.do_parent_store(cr)
|
||||
pool.get('ir.actions.report.xml').register_all(cr)
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
if pooljobs:
|
||||
pool.get('ir.cron').restart(db.dbname)
|
||||
|
||||
return bound_registry
|
||||
|
||||
|
||||
def delete(self, db_name):
|
||||
if db_name in self.bound_registries:
|
||||
del self.bound_registries[db_name]
|
||||
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
+22
-7
@@ -24,6 +24,7 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
import cgitb
|
||||
import errno
|
||||
import logging
|
||||
import logging.handlers
|
||||
@@ -213,6 +214,19 @@ def init_logger():
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(int(tools.config['log_level'] or '0'))
|
||||
|
||||
# A alternative logging scheme for automated runs of the
|
||||
# server intended to test it.
|
||||
def init_alternative_logger():
|
||||
class H(logging.Handler):
|
||||
def emit(self, record):
|
||||
if record.levelno > 20:
|
||||
print record.levelno, record.pathname, record.msg
|
||||
handler = H()
|
||||
logger = logging.getLogger()
|
||||
logger.handlers = []
|
||||
logger.addHandler(handler)
|
||||
logger.setLevel(logging.ERROR)
|
||||
|
||||
class Agent(object):
|
||||
"""Singleton that keeps track of cancellable tasks to run at a given
|
||||
timestamp.
|
||||
@@ -277,12 +291,13 @@ class Agent(object):
|
||||
time.sleep(1)
|
||||
time.sleep(60)
|
||||
|
||||
agent_runner = threading.Thread(target=Agent.runner, name="netsvc.Agent.runner")
|
||||
# the agent runner is a typical daemon thread, that will never quit and must be
|
||||
# terminated when the main process exits - with no consequence (the processing
|
||||
# threads it spawns are not marked daemon)
|
||||
agent_runner.setDaemon(True)
|
||||
agent_runner.start()
|
||||
def start_agent():
|
||||
agent_runner = threading.Thread(target=Agent.runner, name="netsvc.Agent.runner")
|
||||
# the agent runner is a typical daemon thread, that will never quit and must be
|
||||
# terminated when the main process exits - with no consequence (the processing
|
||||
# threads it spawns are not marked daemon)
|
||||
agent_runner.setDaemon(True)
|
||||
agent_runner.start()
|
||||
|
||||
import traceback
|
||||
|
||||
@@ -416,7 +431,7 @@ class OpenERPDispatcher:
|
||||
except Exception, e:
|
||||
self.log('exception', tools.exception_to_unicode(e))
|
||||
tb = getattr(e, 'traceback', sys.exc_info())
|
||||
tb_s = "".join(traceback.format_exception(*tb))
|
||||
tb_s = cgitb.text(tb)
|
||||
if tools.config['debug_mode']:
|
||||
import pdb
|
||||
pdb.post_mortem(tb[2])
|
||||
|
||||
@@ -63,7 +63,13 @@ class _column(object):
|
||||
_symbol_set = (_symbol_c, _symbol_f)
|
||||
_symbol_get = None
|
||||
|
||||
def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, **args):
|
||||
def __init__(self, string='unknown', required=False, readonly=False, domain=None, context=None, states=None, priority=0, change_default=False, size=None, ondelete="set null", translate=False, select=False, manual=False, **args):
|
||||
"""
|
||||
|
||||
The 'manual' keyword argument specifies if the field is a custom one.
|
||||
It corresponds to the 'state' column in ir_model_fields.
|
||||
|
||||
"""
|
||||
if domain is None:
|
||||
domain = []
|
||||
if context is None:
|
||||
@@ -84,6 +90,7 @@ class _column(object):
|
||||
self.read = False
|
||||
self.view_load = 0
|
||||
self.select = select
|
||||
self.manual = manual
|
||||
self.selectable = True
|
||||
self.group_operator = args.get('group_operator', False)
|
||||
for a in args:
|
||||
|
||||
+223
-10
@@ -63,6 +63,39 @@ from openerp.tools.safe_eval import safe_eval as eval
|
||||
from openerp.tools import SKIPPED_ELEMENT_TYPES
|
||||
|
||||
regex_order = re.compile('^(([a-z0-9_]+|"[a-z0-9_]+")( *desc| *asc)?( *, *|))+$', re.I)
|
||||
regex_object_name = re.compile(r'^[a-z0-9_.]+$')
|
||||
|
||||
# Mapping between openerp module names and their osv classes.
|
||||
module_class_list = {}
|
||||
|
||||
def check_object_name(name):
|
||||
""" Check if the given name is a valid openerp object name.
|
||||
|
||||
The _name attribute in osv and osv_memory object is subject to
|
||||
some restrictions. This function returns True or False whether
|
||||
the given name is allowed or not.
|
||||
|
||||
TODO: this is an approximation. The goal in this approximation
|
||||
is to disallow uppercase characters (in some places, we quote
|
||||
table/column names and in other not, which leads to this kind
|
||||
of errors:
|
||||
|
||||
psycopg2.ProgrammingError: relation "xxx" does not exist).
|
||||
|
||||
The same restriction should apply to both osv and osv_memory
|
||||
objects for consistency.
|
||||
|
||||
"""
|
||||
if regex_object_name.match(name) is None:
|
||||
return False
|
||||
return True
|
||||
|
||||
def raise_on_invalid_object_name(name):
|
||||
if not check_object_name(name):
|
||||
msg = "The _name attribute %s is not valid." % name
|
||||
logger = netsvc.Logger()
|
||||
logger.notifyChannel('orm', netsvc.LOG_ERROR, msg)
|
||||
raise except_orm('ValueError', msg)
|
||||
|
||||
POSTGRES_CONFDELTYPES = {
|
||||
'RESTRICT': 'r',
|
||||
@@ -384,6 +417,21 @@ def get_pg_type(f):
|
||||
|
||||
|
||||
class orm_template(object):
|
||||
""" Base class for OpenERP models.
|
||||
|
||||
OpenERP models are created by inheriting from this class (although
|
||||
not directly; more specifically by inheriting from osv or
|
||||
osv_memory). The constructor is called once, usually directly
|
||||
after the class definition, e.g.:
|
||||
|
||||
class user(osv):
|
||||
...
|
||||
user()
|
||||
|
||||
The system will later instanciate the class once per database (on
|
||||
which the class' module is installed).
|
||||
|
||||
"""
|
||||
_name = None
|
||||
_columns = {}
|
||||
_constraints = []
|
||||
@@ -421,6 +469,18 @@ class orm_template(object):
|
||||
raise NotImplementedError(_('The read_group method is not implemented on this object !'))
|
||||
|
||||
def _field_create(self, cr, context=None):
|
||||
"""
|
||||
|
||||
Create/update entries in ir_model, ir_model_data, and ir_model_fields.
|
||||
|
||||
- create an entry in ir_model (if there is not already one),
|
||||
- create an entry in ir_model_data (if there is not already one, and if
|
||||
'module' is in the context),
|
||||
- update ir_model_fields with the fields found in _columns
|
||||
(TODO there is some redundancy as _columns is updated from
|
||||
ir_model_fields in __init__).
|
||||
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
cr.execute("SELECT id FROM ir_model WHERE model=%s", (self._name,))
|
||||
@@ -458,6 +518,7 @@ class orm_template(object):
|
||||
'readonly': (f.readonly and 1) or 0,
|
||||
'required': (f.required and 1) or 0,
|
||||
'selectable': (f.selectable and 1) or 0,
|
||||
'translate': (f.translate and 1) or 0,
|
||||
'relation_field': (f._type=='one2many' and isinstance(f, fields.one2many)) and f._fields_id or '',
|
||||
}
|
||||
# When its a custom field,it does not contain f.select
|
||||
@@ -474,13 +535,13 @@ class orm_template(object):
|
||||
vals['id'] = id
|
||||
cr.execute("""INSERT INTO ir_model_fields (
|
||||
id, model_id, model, name, field_description, ttype,
|
||||
relation,view_load,state,select_level,relation_field
|
||||
relation,view_load,state,select_level,relation_field, translate
|
||||
) VALUES (
|
||||
%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
|
||||
%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s
|
||||
)""", (
|
||||
id, vals['model_id'], vals['model'], vals['name'], vals['field_description'], vals['ttype'],
|
||||
vals['relation'], bool(vals['view_load']), 'base',
|
||||
vals['select_level'], vals['relation_field']
|
||||
vals['select_level'], vals['relation_field'], bool(vals['translate'])
|
||||
))
|
||||
if 'module' in context:
|
||||
name1 = 'field_' + self._table + '_' + k
|
||||
@@ -497,20 +558,118 @@ class orm_template(object):
|
||||
cr.commit()
|
||||
cr.execute("""UPDATE ir_model_fields SET
|
||||
model_id=%s, field_description=%s, ttype=%s, relation=%s,
|
||||
view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s
|
||||
view_load=%s, select_level=%s, readonly=%s ,required=%s, selectable=%s, relation_field=%s, translate=%s
|
||||
WHERE
|
||||
model=%s AND name=%s""", (
|
||||
vals['model_id'], vals['field_description'], vals['ttype'],
|
||||
vals['relation'], bool(vals['view_load']),
|
||||
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], vals['model'], vals['name']
|
||||
vals['select_level'], bool(vals['readonly']), bool(vals['required']), bool(vals['selectable']), vals['relation_field'], bool(vals['translate']), vals['model'], vals['name']
|
||||
))
|
||||
break
|
||||
cr.commit()
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
raise_on_invalid_object_name(self._name)
|
||||
self._field_create(cr, context=context)
|
||||
|
||||
def __init__(self, cr):
|
||||
#
|
||||
# Goal: try to apply inheritance at the instanciation level and
|
||||
# put objects in the pool var
|
||||
#
|
||||
@classmethod
|
||||
def makeInstance(cls, pool, cr, attributes):
|
||||
""" Instanciate a given model.
|
||||
|
||||
This class method instanciates the class of some model (i.e. a class
|
||||
deriving from osv or osv_memory). The class might be the class passed
|
||||
in argument or, if it inherits from another class, a class constructed
|
||||
by combining the two classes.
|
||||
|
||||
The ``attributes`` argument specifies which parent class attributes
|
||||
have to be combined.
|
||||
|
||||
TODO: the creation of the combined class is repeated at each call of
|
||||
this method. This is probably unnecessary.
|
||||
|
||||
"""
|
||||
parent_names = getattr(cls, '_inherit', None)
|
||||
if parent_names:
|
||||
if isinstance(parent_names, (str, unicode)):
|
||||
name = cls._name or parent_names
|
||||
parent_names = [parent_names]
|
||||
else:
|
||||
name = cls._name
|
||||
|
||||
if not name:
|
||||
raise TypeError('_name is mandatory in case of multiple inheritance')
|
||||
|
||||
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
|
||||
parent_class = pool.get(parent_name).__class__
|
||||
if not pool.get(parent_name):
|
||||
raise TypeError('The model "%s" specifies an unexisting parent class "%s"\n'
|
||||
'You may need to add a dependency on the parent class\' module.' % (name, parent_name))
|
||||
nattr = {}
|
||||
for s in attributes:
|
||||
new = copy.copy(getattr(pool.get(parent_name), s))
|
||||
if s == '_columns':
|
||||
# Don't _inherit custom fields.
|
||||
for c in new.keys():
|
||||
if new[c].manual:
|
||||
del new[c]
|
||||
if hasattr(new, 'update'):
|
||||
new.update(cls.__dict__.get(s, {}))
|
||||
elif s=='_constraints':
|
||||
for c in cls.__dict__.get(s, []):
|
||||
exist = False
|
||||
for c2 in range(len(new)):
|
||||
#For _constraints, we should check field and methods as well
|
||||
if new[c2][2]==c[2] and (new[c2][0] == c[0] \
|
||||
or getattr(new[c2][0],'__name__', True) == \
|
||||
getattr(c[0],'__name__', False)):
|
||||
# If new class defines a constraint with
|
||||
# same function name, we let it override
|
||||
# the old one.
|
||||
new[c2] = c
|
||||
exist = True
|
||||
break
|
||||
if not exist:
|
||||
new.append(c)
|
||||
else:
|
||||
new.extend(cls.__dict__.get(s, []))
|
||||
nattr[s] = new
|
||||
cls = type(name, (cls, parent_class), nattr)
|
||||
obj = object.__new__(cls)
|
||||
obj.__init__(pool, cr)
|
||||
return obj
|
||||
|
||||
def __new__(cls):
|
||||
""" Register this model.
|
||||
|
||||
This doesn't create an instance but simply register the model
|
||||
as being part of the module where it is defined.
|
||||
|
||||
TODO make it possible to not even have to call the constructor
|
||||
to be registered.
|
||||
|
||||
"""
|
||||
|
||||
# Set the module name (e.g. base, sale, accounting, ...) on the class.
|
||||
module = cls.__module__.split('.')[0]
|
||||
if not hasattr(cls, '_module'):
|
||||
cls._module = module
|
||||
|
||||
# Remember which models to instanciate for this module.
|
||||
module_class_list.setdefault(cls._module, []).append(cls)
|
||||
|
||||
# Since we don't return an instance here, the __init__
|
||||
# method won't be called.
|
||||
return None
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
""" Initialize a model and make it part of the given registry."""
|
||||
pool.add(self._name, self)
|
||||
self.pool = pool
|
||||
|
||||
if not self._name and not hasattr(self, '_inherit'):
|
||||
name = type(self).__name__.split('.')[0]
|
||||
msg = "The class %s has to have a _name attribute" % name
|
||||
@@ -1798,6 +1957,23 @@ class orm_template(object):
|
||||
"""
|
||||
return self._name_search(cr, user, name, args, operator, context, limit)
|
||||
|
||||
def name_create(self, cr, uid, name, context=None):
|
||||
"""
|
||||
Creates a new record by calling :py:meth:`~osv.osv.osv.create` with only one
|
||||
value provided: the name of the new record (``_rec_name`` field).
|
||||
The new record will also be initialized with any default values applicable
|
||||
to this model, or provided through the context. The usual behavior of
|
||||
:py:meth:`~osv.osv.osv.create` applies.
|
||||
Similarly, this method may raise an exception if the model has multiple
|
||||
required fields and some do not have default values.
|
||||
|
||||
:param name: name of the record to create
|
||||
|
||||
:return: the :py:meth:`~osv.osv.osv.name_get` value for the newly-created record.
|
||||
"""
|
||||
rec_id = self.create(cr, uid, {self._rec_name: name}, context);
|
||||
return self.name_get(cr, uid, [rec_id], context)[0]
|
||||
|
||||
# private implementation of name_search, allows passing a dedicated user for the name_get part to
|
||||
# solve some access rights issues
|
||||
def _name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100, name_get_uid=None):
|
||||
@@ -1899,8 +2075,12 @@ class orm_memory(orm_template):
|
||||
_max_hours = config.get('osv_memory_age_limit')
|
||||
_check_time = 20
|
||||
|
||||
def __init__(self, cr):
|
||||
super(orm_memory, self).__init__(cr)
|
||||
@classmethod
|
||||
def createInstance(cls, pool, cr):
|
||||
return cls.makeInstance(pool, cr, ['_columns', '_defaults'])
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
super(orm_memory, self).__init__(pool, cr)
|
||||
self.datas = {}
|
||||
self.next_id = 0
|
||||
self.check_id = 0
|
||||
@@ -2366,6 +2546,22 @@ class orm(orm_template):
|
||||
self._table, column['attname'])
|
||||
|
||||
def _auto_init(self, cr, context=None):
|
||||
"""
|
||||
|
||||
Call _field_create and, unless _auto is False:
|
||||
|
||||
- create the corresponding table in database for the model,
|
||||
- possibly add the parent columns in database,
|
||||
- possibly add the columns 'create_uid', 'create_date', 'write_uid',
|
||||
'write_date' in database if _log_access is True (the default),
|
||||
- report on database columns no more existing in _columns,
|
||||
- remove no more existing not null constraints,
|
||||
- alter existing database columns to match _columns,
|
||||
- create database tables to match _columns,
|
||||
- add database indices to match _columns,
|
||||
|
||||
"""
|
||||
raise_on_invalid_object_name(self._name)
|
||||
if context is None:
|
||||
context = {}
|
||||
store_compute = False
|
||||
@@ -2771,8 +2967,22 @@ class orm(orm_template):
|
||||
cr.commit()
|
||||
return todo_end
|
||||
|
||||
def __init__(self, cr):
|
||||
super(orm, self).__init__(cr)
|
||||
@classmethod
|
||||
def createInstance(cls, pool, cr):
|
||||
return cls.makeInstance(pool, cr, ['_columns', '_defaults',
|
||||
'_inherits', '_constraints', '_sql_constraints'])
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
"""
|
||||
|
||||
- copy the stored fields' functions in the osv_pool,
|
||||
- update the _columns with the fields found in ir_model_fields,
|
||||
- ensure there is a many2one for each _inherits'd parent,
|
||||
- update the children's _columns,
|
||||
- give a chance to each field to initialize itself.
|
||||
|
||||
"""
|
||||
super(orm, self).__init__(pool, cr)
|
||||
|
||||
if not hasattr(self, '_log_access'):
|
||||
# if not access is not specify, it is the same value as _auto
|
||||
@@ -2829,6 +3039,7 @@ class orm(orm_template):
|
||||
'size': field['size'],
|
||||
'ondelete': field['on_delete'],
|
||||
'translate': (field['translate']),
|
||||
'manual': True,
|
||||
#'select': int(field['select_level'])
|
||||
}
|
||||
|
||||
@@ -2856,6 +3067,8 @@ class orm(orm_template):
|
||||
for f in self._columns:
|
||||
self._columns[f].restart()
|
||||
|
||||
__init__.__doc__ = orm_template.__init__.__doc__ + __init__.__doc__
|
||||
|
||||
#
|
||||
# Update objects that uses this one to update their _inherits fields
|
||||
#
|
||||
|
||||
+41
-135
@@ -23,18 +23,18 @@
|
||||
# OSV: Objects Services
|
||||
#
|
||||
|
||||
import sys
|
||||
import inspect
|
||||
import orm
|
||||
import openerp.netsvc as netsvc
|
||||
import openerp.pooler as pooler
|
||||
import openerp.sql_db as sql_db
|
||||
import copy
|
||||
import logging
|
||||
from psycopg2 import IntegrityError, errorcodes
|
||||
from openerp.tools.func import wraps
|
||||
from openerp.tools.translate import translate
|
||||
|
||||
module_list = []
|
||||
module_class_list = {}
|
||||
class_pool = {}
|
||||
from openerp.osv.orm import module_class_list
|
||||
|
||||
class except_osv(Exception):
|
||||
def __init__(self, name, value, exc_type='warning'):
|
||||
@@ -92,7 +92,7 @@ class object_proxy(netsvc.Service):
|
||||
ids = args[3]
|
||||
else:
|
||||
ids = []
|
||||
cr = pooler.get_db_only(dbname).cursor()
|
||||
cr = sql_db.db_connect(db_name).cursor()
|
||||
return src(obj, cr, uid, ids, context=(ctx or {}))
|
||||
except Exception:
|
||||
pass
|
||||
@@ -103,7 +103,7 @@ class object_proxy(netsvc.Service):
|
||||
# be returned, it is the best we have.
|
||||
|
||||
try:
|
||||
cr = pooler.get_db_only(dbname).cursor()
|
||||
cr = sql_db.db_connect(db_name).cursor()
|
||||
res = translate(cr, name=False, source_type=ttype,
|
||||
lang=lang, source=src)
|
||||
if res:
|
||||
@@ -117,7 +117,7 @@ class object_proxy(netsvc.Service):
|
||||
return tr(src, 'code')
|
||||
|
||||
try:
|
||||
if not pooler.get_pool(dbname)._ready:
|
||||
if pooler.get_pool(dbname)._init:
|
||||
raise except_osv('Database not ready', 'Currently, this database is not fully loaded and can not be used.')
|
||||
return f(self, dbname, *args, **kwargs)
|
||||
except orm.except_orm, inst:
|
||||
@@ -202,158 +202,64 @@ class object_proxy(netsvc.Service):
|
||||
cr.close()
|
||||
return res
|
||||
|
||||
object_proxy()
|
||||
|
||||
class osv_pool(object):
|
||||
""" Model registry for a particular database.
|
||||
|
||||
The registry is essentially a mapping between model names and model
|
||||
instances. There is one registry instance per database.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self._ready = False
|
||||
self.obj_pool = {}
|
||||
self.module_object_list = {}
|
||||
self.created = []
|
||||
self.obj_pool = {} # model name/model instance mapping
|
||||
self._sql_error = {}
|
||||
self._store_function = {}
|
||||
self._init = True
|
||||
self._init_parent = {}
|
||||
self.logger = logging.getLogger("pool")
|
||||
|
||||
def init_set(self, cr, mode):
|
||||
different = mode != self._init
|
||||
if different:
|
||||
if mode:
|
||||
self._init_parent = {}
|
||||
if not mode:
|
||||
for o in self._init_parent:
|
||||
self.get(o)._parent_store_compute(cr)
|
||||
self._init = mode
|
||||
|
||||
self._ready = True
|
||||
return different
|
||||
|
||||
def do_parent_store(self, cr):
|
||||
for o in self._init_parent:
|
||||
self.get(o)._parent_store_compute(cr)
|
||||
self._init = False
|
||||
|
||||
def obj_list(self):
|
||||
""" Return the list of model names in this registry."""
|
||||
return self.obj_pool.keys()
|
||||
|
||||
# adds a new object instance to the object pool.
|
||||
# if it already existed, the instance is replaced
|
||||
def add(self, name, obj_inst):
|
||||
if name in self.obj_pool:
|
||||
del self.obj_pool[name]
|
||||
self.obj_pool[name] = obj_inst
|
||||
module = obj_inst.__class__.__module__.split('.')[0]
|
||||
self.module_object_list.setdefault(module, []).append(obj_inst)
|
||||
def add(self, model_name, model):
|
||||
""" Add or replace a model in the registry."""
|
||||
self.obj_pool[model_name] = model
|
||||
|
||||
# Return None if object does not exist
|
||||
def get(self, name):
|
||||
obj = self.obj_pool.get(name, None)
|
||||
return obj
|
||||
""" Return a model for a given name or None if it doesn't exist."""
|
||||
return self.obj_pool.get(name)
|
||||
|
||||
#TODO: pass a list of modules to load
|
||||
def instanciate(self, module, cr):
|
||||
""" Instanciate all the classes of a given module for a particular db."""
|
||||
|
||||
res = []
|
||||
class_list = module_class_list.get(module, [])
|
||||
for klass in class_list:
|
||||
res.append(klass.createInstance(self, module, cr))
|
||||
|
||||
# Instanciate classes registered through their constructor and
|
||||
# add them to the pool.
|
||||
for klass in module_class_list.get(module, []):
|
||||
res.append(klass.createInstance(self, cr))
|
||||
|
||||
return res
|
||||
|
||||
class osv_base(object):
|
||||
def __init__(self, pool, cr):
|
||||
pool.add(self._name, self)
|
||||
self.pool = pool
|
||||
super(osv_base, self).__init__(cr)
|
||||
|
||||
def __new__(cls):
|
||||
module = cls.__module__.split('.')[0]
|
||||
if not hasattr(cls, '_module'):
|
||||
cls._module = module
|
||||
module_class_list.setdefault(cls._module, []).append(cls)
|
||||
class_pool[cls._name] = cls
|
||||
if module not in module_list:
|
||||
module_list.append(cls._module)
|
||||
return None
|
||||
class osv_memory(orm.orm_memory):
|
||||
""" Deprecated class. """
|
||||
pass
|
||||
|
||||
class osv_memory(osv_base, orm.orm_memory):
|
||||
#
|
||||
# Goal: try to apply inheritancy at the instanciation level and
|
||||
# put objects in the pool var
|
||||
#
|
||||
def createInstance(cls, pool, module, cr):
|
||||
parent_names = getattr(cls, '_inherit', None)
|
||||
if parent_names:
|
||||
if isinstance(parent_names, (str, unicode)):
|
||||
name = cls._name or parent_names
|
||||
parent_names = [parent_names]
|
||||
else:
|
||||
name = cls._name
|
||||
if not name:
|
||||
raise TypeError('_name is mandatory in case of multiple inheritance')
|
||||
|
||||
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
|
||||
parent_class = pool.get(parent_name).__class__
|
||||
assert pool.get(parent_name), "parent class %s does not exist in module %s !" % (parent_name, module)
|
||||
nattr = {}
|
||||
for s in ('_columns', '_defaults'):
|
||||
new = copy.copy(getattr(pool.get(parent_name), s))
|
||||
if hasattr(new, 'update'):
|
||||
new.update(cls.__dict__.get(s, {}))
|
||||
else:
|
||||
new.extend(cls.__dict__.get(s, []))
|
||||
nattr[s] = new
|
||||
cls = type(name, (cls, parent_class), nattr)
|
||||
class osv(orm.orm):
|
||||
""" Deprecated class. """
|
||||
pass
|
||||
|
||||
obj = object.__new__(cls)
|
||||
obj.__init__(pool, cr)
|
||||
return obj
|
||||
createInstance = classmethod(createInstance)
|
||||
|
||||
class osv(osv_base, orm.orm):
|
||||
#
|
||||
# Goal: try to apply inheritancy at the instanciation level and
|
||||
# put objects in the pool var
|
||||
#
|
||||
def createInstance(cls, pool, module, cr):
|
||||
parent_names = getattr(cls, '_inherit', None)
|
||||
if parent_names:
|
||||
if isinstance(parent_names, (str, unicode)):
|
||||
name = cls._name or parent_names
|
||||
parent_names = [parent_names]
|
||||
else:
|
||||
name = cls._name
|
||||
if not name:
|
||||
raise TypeError('_name is mandatory in case of multiple inheritance')
|
||||
|
||||
for parent_name in ((type(parent_names)==list) and parent_names or [parent_names]):
|
||||
parent_class = pool.get(parent_name).__class__
|
||||
assert pool.get(parent_name), "parent class %s does not exist in module %s !" % (parent_name, module)
|
||||
nattr = {}
|
||||
for s in ('_columns', '_defaults', '_inherits', '_constraints', '_sql_constraints'):
|
||||
new = copy.copy(getattr(pool.get(parent_name), s))
|
||||
if hasattr(new, 'update'):
|
||||
new.update(cls.__dict__.get(s, {}))
|
||||
else:
|
||||
if s=='_constraints':
|
||||
for c in cls.__dict__.get(s, []):
|
||||
exist = False
|
||||
for c2 in range(len(new)):
|
||||
#For _constraints, we should check field and methods as well
|
||||
if new[c2][2]==c[2] and (new[c2][0] == c[0] \
|
||||
or getattr(new[c2][0],'__name__', True) == \
|
||||
getattr(c[0],'__name__', False)):
|
||||
# If new class defines a constraint with
|
||||
# same function name, we let it override
|
||||
# the old one.
|
||||
new[c2] = c
|
||||
exist = True
|
||||
break
|
||||
if not exist:
|
||||
new.append(c)
|
||||
else:
|
||||
new.extend(cls.__dict__.get(s, []))
|
||||
nattr[s] = new
|
||||
cls = type(name, (cls, parent_class), nattr)
|
||||
obj = object.__new__(cls)
|
||||
obj.__init__(pool, cr)
|
||||
return obj
|
||||
createInstance = classmethod(createInstance)
|
||||
def start_object_proxy():
|
||||
object_proxy()
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
+31
-45
@@ -19,64 +19,50 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
pool_dic = {}
|
||||
""" Functions kept for backward compatibility.
|
||||
|
||||
They are simple wrappers around a global RegistryManager methods.
|
||||
|
||||
"""
|
||||
|
||||
from openerp.modules.registry import RegistryManager
|
||||
|
||||
_Registries = None
|
||||
|
||||
|
||||
def ensure_registries():
|
||||
global _Registries
|
||||
if _Registries is None:
|
||||
_Registries = RegistryManager()
|
||||
|
||||
|
||||
def get_db_and_pool(db_name, force_demo=False, status=None, update_module=False, pooljobs=True):
|
||||
if not status:
|
||||
status={}
|
||||
"""Create and return a database connection and a newly initialized registry."""
|
||||
ensure_registries()
|
||||
bound_registry = _Registries.get(db_name, force_demo, status, update_module, pooljobs)
|
||||
return bound_registry.db, bound_registry.registry
|
||||
|
||||
db = get_db_only(db_name)
|
||||
|
||||
if db_name in pool_dic:
|
||||
pool = pool_dic[db_name]
|
||||
else:
|
||||
import openerp.addons as addons
|
||||
import openerp.osv.osv as osv_osv
|
||||
pool = osv_osv.osv_pool()
|
||||
pool_dic[db_name] = pool
|
||||
|
||||
try:
|
||||
addons.load_modules(db, force_demo, status, update_module)
|
||||
except Exception:
|
||||
del pool_dic[db_name]
|
||||
raise
|
||||
|
||||
cr = db.cursor()
|
||||
try:
|
||||
pool.init_set(cr, False)
|
||||
pool.get('ir.actions.report.xml').register_all(cr)
|
||||
cr.commit()
|
||||
finally:
|
||||
cr.close()
|
||||
|
||||
if pooljobs:
|
||||
pool.get('ir.cron').restart(db.dbname)
|
||||
return db, pool
|
||||
def delete_pool(db_name):
|
||||
"""Delete an existing registry."""
|
||||
ensure_registries()
|
||||
_Registries.delete(db_name)
|
||||
|
||||
|
||||
def restart_pool(db_name, force_demo=False, status=None, update_module=False):
|
||||
if db_name in pool_dic:
|
||||
del pool_dic[db_name]
|
||||
return get_db_and_pool(db_name, force_demo, status, update_module=update_module)
|
||||
|
||||
|
||||
def get_db_only(db_name):
|
||||
# ATTENTION:
|
||||
# do not put this import outside this function
|
||||
# sql_db must not be loaded before the logger is initialized.
|
||||
# sql_db import psycopg2.tool which create a default logger if there is not.
|
||||
# this resulting of having the logs outputed twice...
|
||||
import openerp.sql_db as sql_db
|
||||
db = sql_db.db_connect(db_name)
|
||||
return db
|
||||
"""Delete an existing registry and return a database connection and a newly initialized registry."""
|
||||
ensure_registries()
|
||||
bound_registry = _Registries.new(db_name, force_demo, status, update_module, True)
|
||||
return bound_registry.db, bound_registry.registry
|
||||
|
||||
|
||||
def get_db(db_name):
|
||||
"""Return a database connection. The corresponding registry is initialized."""
|
||||
return get_db_and_pool(db_name)[0]
|
||||
|
||||
|
||||
def get_pool(db_name, force_demo=False, status=None, update_module=False):
|
||||
pool = get_db_and_pool(db_name, force_demo, status, update_module)[1]
|
||||
return pool
|
||||
"""Return a model registry."""
|
||||
return get_db_and_pool(db_name, force_demo, status, update_module)[1]
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
@@ -27,7 +27,7 @@ import openerp.netsvc as netsvc
|
||||
import openerp.pooler as pooler
|
||||
|
||||
import openerp.tools as tools
|
||||
import openerp.addons as addons
|
||||
import openerp.modules
|
||||
import print_xml
|
||||
import render
|
||||
import urllib
|
||||
@@ -119,7 +119,7 @@ class report_rml(report_int):
|
||||
pos_xml = i.end()
|
||||
|
||||
doc = print_xml.document(cr, uid, {}, {})
|
||||
tmpl_path = addons.get_module_resource('base', 'report', 'corporate_defaults.xml')
|
||||
tmpl_path = openerp.modules.get_module_resource('base', 'report', 'corporate_defaults.xml')
|
||||
doc.parse(tmpl_path, [uid], 'res.users', context)
|
||||
corporate_header = doc.xml_get()
|
||||
doc.close()
|
||||
|
||||
@@ -27,7 +27,7 @@ try:
|
||||
import Image
|
||||
except ImportError:
|
||||
import logging
|
||||
logging.getLogger('init').warning('Python Imaging not installed, you can use only .JPG pictures !')
|
||||
logging.warning('Python Imaging not installed, you can use only .JPG pictures !')
|
||||
|
||||
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user