2006-12-07 13:41:40 +00:00
|
|
|
##############################################################################
|
|
|
|
#
|
2008-06-16 07:24:04 +00:00
|
|
|
# Copyright (c) 2004-2008 TINY SPRL. (http://tiny.be)
|
2006-12-07 13:41:40 +00:00
|
|
|
#
|
2008-06-16 06:44:24 +00:00
|
|
|
# $Id$
|
2006-12-07 13:41:40 +00:00
|
|
|
#
|
|
|
|
# WARNING: This program as such is intended to be used by professional
|
|
|
|
# programmers who take the whole responsability of assessing all potential
|
|
|
|
# consequences resulting from its eventual inadequacies and bugs
|
|
|
|
# End users who are looking for a ready-to-use solution with commercial
|
|
|
|
# garantees and support are strongly adviced to contact a Free Software
|
|
|
|
# Service Company
|
|
|
|
#
|
|
|
|
# This program is Free Software; you can redistribute it and/or
|
|
|
|
# modify it under the terms of the GNU General Public License
|
|
|
|
# as published by the Free Software Foundation; either version 2
|
|
|
|
# of the License, or (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
|
|
# along with this program; if not, write to the Free Software
|
|
|
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
#
|
|
|
|
##############################################################################
|
|
|
|
|
|
|
|
import os, sys, imp
|
|
|
|
import itertools
|
|
|
|
from sets import Set
|
|
|
|
|
|
|
|
import osv
|
|
|
|
import tools
|
|
|
|
import pooler
|
|
|
|
|
|
|
|
|
|
|
|
import netsvc
|
|
|
|
from osv import fields
|
|
|
|
|
2007-04-21 13:32:18 +00:00
|
|
|
import zipfile
|
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
logger = netsvc.Logger()
|
|
|
|
|
|
|
|
opj = os.path.join
|
2008-07-15 08:10:40 +00:00
|
|
|
|
|
|
|
_ad = opj(tools.config['root_path'], 'addons') # default addons path (base)
|
|
|
|
ad = tools.config['addons_path'] # alternate addons path
|
|
|
|
|
|
|
|
sys.path.insert(1, _ad)
|
|
|
|
if ad != _ad:
|
|
|
|
sys.path.insert(1, ad)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
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)
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
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
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
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, 'childs'):
|
|
|
|
self.childs = []
|
|
|
|
if not hasattr(self, 'depth'):
|
|
|
|
self.depth = 0
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
def addChild(self, name):
|
|
|
|
node = Node(name, self.graph)
|
|
|
|
node.depth = self.depth + 1
|
|
|
|
if node not in self.childs:
|
|
|
|
self.childs.append(node)
|
|
|
|
for attr in ('init', 'update', 'demo'):
|
|
|
|
if hasattr(self, attr):
|
|
|
|
setattr(node, attr, True)
|
|
|
|
self.childs.sort(lambda x,y: cmp(x.name, y.name))
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
def hasChild(self, name):
|
|
|
|
return Node(name, self.graph) in self.childs or \
|
|
|
|
bool([c for c in self.childs if c.hasChild(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.childs:
|
|
|
|
setattr(child, name, value)
|
|
|
|
if name == 'depth':
|
|
|
|
for child in self.childs:
|
|
|
|
setattr(child, name, value + 1)
|
|
|
|
|
|
|
|
def __iter__(self):
|
|
|
|
return itertools.chain(iter(self.childs), *map(iter, self.childs))
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self._pprint()
|
|
|
|
|
|
|
|
def _pprint(self, depth=0):
|
|
|
|
s = '%s\n' % self.name
|
|
|
|
for c in self.childs:
|
|
|
|
s += '%s`-> %s' % (' ' * depth, c._pprint(depth+1))
|
|
|
|
return s
|
|
|
|
|
2008-07-15 08:10:40 +00:00
|
|
|
def _get_module_path(module):
|
|
|
|
if os.path.exists(opj(ad, module)):
|
|
|
|
return opj(ad, module)
|
|
|
|
return opj(_ad, module)
|
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
def create_graph(module_list, force=None):
|
|
|
|
if not force:
|
|
|
|
force=[]
|
2006-12-07 13:41:40 +00:00
|
|
|
graph = Graph()
|
|
|
|
packages = []
|
|
|
|
|
|
|
|
for module in module_list:
|
2007-05-03 19:21:49 +00:00
|
|
|
if module[-4:]=='.zip':
|
|
|
|
module = module[:-4]
|
2008-07-15 08:10:40 +00:00
|
|
|
mod_path = _get_module_path(module)
|
|
|
|
terp_file = opj(mod_path, '__terp__.py')
|
2007-05-03 19:21:49 +00:00
|
|
|
if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
|
2007-07-12 15:31:22 +00:00
|
|
|
try:
|
|
|
|
info = eval(tools.file_open(terp_file).read())
|
|
|
|
except:
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:eval file %s' % (module, terp_file))
|
|
|
|
raise
|
2006-12-07 13:41:40 +00:00
|
|
|
if info.get('installable', True):
|
|
|
|
packages.append((module, info.get('depends', []), info))
|
|
|
|
|
|
|
|
current,later = Set([p for p, dep, data in packages]), Set()
|
|
|
|
while packages and current > later:
|
|
|
|
package, deps, datas = packages[0]
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
# 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):
|
2007-07-30 13:35:27 +00:00
|
|
|
if not package in current:
|
|
|
|
packages.pop(0)
|
|
|
|
continue
|
2006-12-07 13:41:40 +00:00
|
|
|
later.clear()
|
|
|
|
current.remove(package)
|
|
|
|
graph.addNode(package, deps)
|
|
|
|
node = Node(package, graph)
|
|
|
|
node.datas = datas
|
|
|
|
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, datas))
|
2007-07-30 13:35:27 +00:00
|
|
|
packages.pop(0)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
for package in later:
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:Unmet dependency' % package)
|
2007-08-09 13:12:25 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
return graph
|
|
|
|
|
|
|
|
def init_module_objects(cr, module_name, obj_list):
|
|
|
|
pool = pooler.get_pool(cr.dbname)
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:creating or updating database tables' % module_name)
|
|
|
|
for obj in obj_list:
|
|
|
|
if hasattr(obj, 'init'):
|
|
|
|
obj.init(cr)
|
2008-06-17 20:17:23 +00:00
|
|
|
obj._auto_init(cr, {'module': module_name})
|
2006-12-07 13:41:40 +00:00
|
|
|
cr.commit()
|
|
|
|
|
2007-08-09 13:12:25 +00:00
|
|
|
def load_module_graph(cr, graph, status=None, **kwargs):
|
|
|
|
# **kwargs is passed directly to convert_xml_import
|
2007-07-23 05:16:03 +00:00
|
|
|
if not status:
|
|
|
|
status={}
|
2008-04-16 20:00:51 +00:00
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
status = status.copy()
|
2006-12-07 13:41:40 +00:00
|
|
|
package_todo = []
|
|
|
|
statusi = 0
|
|
|
|
for package in graph:
|
|
|
|
status['progress'] = (float(statusi)+0.1)/len(graph)
|
|
|
|
m = package.name
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s' % m)
|
|
|
|
sys.stdout.flush()
|
|
|
|
pool = pooler.get_pool(cr.dbname)
|
2008-03-26 13:15:13 +00:00
|
|
|
modules = pool.instanciate(m, cr)
|
2006-12-07 13:41:40 +00:00
|
|
|
cr.execute('select state, demo from ir_module_module where name=%s', (m,))
|
|
|
|
(package_state, package_demo) = (cr.rowcount and cr.fetchone()) or ('uninstalled', False)
|
|
|
|
idref = {}
|
|
|
|
status['progress'] = (float(statusi)+0.4)/len(graph)
|
|
|
|
if hasattr(package, 'init') or hasattr(package, 'update') or package_state in ('to install', 'to upgrade'):
|
|
|
|
init_module_objects(cr, m, modules)
|
|
|
|
for kind in ('init', 'update'):
|
|
|
|
for filename in package.datas.get('%s_xml' % kind, []):
|
|
|
|
mode = 'update'
|
|
|
|
if hasattr(package, 'init') or package_state=='to install':
|
|
|
|
mode = 'init'
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, filename))
|
|
|
|
name, ext = os.path.splitext(filename)
|
|
|
|
if ext == '.csv':
|
2007-01-25 15:23:10 +00:00
|
|
|
tools.convert_csv_import(cr, m, os.path.basename(filename), tools.file_open(opj(m, filename)).read(), idref, mode=mode)
|
2006-12-07 13:41:40 +00:00
|
|
|
elif ext == '.sql':
|
|
|
|
queries = tools.file_open(opj(m, filename)).read().split(';')
|
|
|
|
for query in queries:
|
|
|
|
new_query = ' '.join(query.split())
|
|
|
|
if new_query:
|
|
|
|
cr.execute(new_query)
|
|
|
|
else:
|
2008-07-01 20:47:53 +00:00
|
|
|
tools.convert_xml_import(cr, m, tools.file_open(opj(m, filename)), idref, mode=mode, **kwargs)
|
2006-12-07 13:41:40 +00:00
|
|
|
if hasattr(package, 'demo') or (package_demo and package_state != 'installed'):
|
|
|
|
status['progress'] = (float(statusi)+0.75)/len(graph)
|
|
|
|
for xml in package.datas.get('demo_xml', []):
|
|
|
|
name, ext = os.path.splitext(xml)
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:loading %s' % (m, xml))
|
|
|
|
if ext == '.csv':
|
2007-09-26 05:20:40 +00:00
|
|
|
tools.convert_csv_import(cr, m, os.path.basename(xml), tools.file_open(opj(m, xml)).read(), idref, noupdate=True)
|
2006-12-07 13:41:40 +00:00
|
|
|
else:
|
2008-07-01 20:47:53 +00:00
|
|
|
tools.convert_xml_import(cr, m, tools.file_open(opj(m, xml)), idref, noupdate=True, **kwargs)
|
2007-03-14 14:53:22 +00:00
|
|
|
cr.execute('update ir_module_module set demo=%s where name=%s', (True, package.name))
|
2006-12-07 13:41:40 +00:00
|
|
|
package_todo.append(package.name)
|
2007-03-14 14:53:22 +00:00
|
|
|
cr.execute("update ir_module_module set state='installed' where state in ('to upgrade', 'to install') and name=%s", (package.name,))
|
2007-08-30 14:22:09 +00:00
|
|
|
cr.commit()
|
2006-12-07 13:41:40 +00:00
|
|
|
statusi+=1
|
|
|
|
|
2007-02-07 15:44:53 +00:00
|
|
|
pool = pooler.get_pool(cr.dbname)
|
2008-04-16 20:00:51 +00:00
|
|
|
cr.execute('select * from ir_model where state=%s', ('manual',))
|
|
|
|
for model in cr.dictfetchall():
|
|
|
|
pool.get('ir.model').instanciate(cr, 1, model['model'], {})
|
|
|
|
|
2007-02-07 15:44:53 +00:00
|
|
|
pool.get('ir.model.data')._process_end(cr, 1, package_todo)
|
|
|
|
cr.commit()
|
2006-12-07 13:41:40 +00:00
|
|
|
|
|
|
|
def register_classes():
|
|
|
|
module_list = os.listdir(ad)
|
2008-07-15 09:16:15 +00:00
|
|
|
module_names = [os.path.basename(m) for m in module_list]
|
|
|
|
module_list += [m for m in os.listdir(_ad) if m not in module_names]
|
2008-07-15 08:10:40 +00:00
|
|
|
|
2006-12-07 13:41:40 +00:00
|
|
|
for package in create_graph(module_list):
|
|
|
|
m = package.name
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:registering classes' % m)
|
|
|
|
sys.stdout.flush()
|
2007-07-30 13:35:27 +00:00
|
|
|
|
2008-07-15 08:10:40 +00:00
|
|
|
mod_path = _get_module_path(m)
|
|
|
|
if not os.path.isfile(mod_path + '.zip'):
|
2007-04-21 13:32:18 +00:00
|
|
|
# XXX must restrict to only addons paths
|
|
|
|
imp.load_module(m, *imp.find_module(m))
|
2007-07-30 13:35:27 +00:00
|
|
|
else:
|
2007-04-21 13:32:18 +00:00
|
|
|
import zipimport
|
2008-07-15 08:10:40 +00:00
|
|
|
mod_path = mod_path + '.zip'
|
2007-07-11 07:33:18 +00:00
|
|
|
try:
|
|
|
|
zimp = zipimport.zipimporter(mod_path)
|
|
|
|
zimp.load_module(m)
|
|
|
|
except zipimport.ZipImportError:
|
|
|
|
logger.notifyChannel('init', netsvc.LOG_ERROR, 'Couldn\'t find module %s' % m)
|
2006-12-07 13:41:40 +00:00
|
|
|
|
2007-07-23 05:16:03 +00:00
|
|
|
def load_modules(db, force_demo=False, status=None, update_module=False):
|
|
|
|
if not status:
|
|
|
|
status={}
|
2006-12-28 11:06:46 +00:00
|
|
|
cr = db.cursor()
|
2006-12-07 13:41:40 +00:00
|
|
|
force = []
|
|
|
|
if force_demo:
|
|
|
|
force.append('demo')
|
|
|
|
if update_module:
|
2006-12-28 14:41:28 +00:00
|
|
|
cr.execute("select name from ir_module_module where state in ('installed', 'to install', 'to upgrade','to remove')")
|
2006-12-07 13:41:40 +00:00
|
|
|
else:
|
2006-12-28 14:41:28 +00:00
|
|
|
cr.execute("select name from ir_module_module where state in ('installed', 'to upgrade', 'to remove')")
|
2006-12-07 13:41:40 +00:00
|
|
|
module_list = [name for (name,) in cr.fetchall()]
|
|
|
|
graph = create_graph(module_list, force)
|
2007-08-09 13:12:25 +00:00
|
|
|
report = tools.assertion_report()
|
|
|
|
load_module_graph(cr, graph, status, report=report)
|
|
|
|
if report.get_report():
|
2007-08-09 13:12:28 +00:00
|
|
|
logger.notifyChannel('init', netsvc.LOG_INFO, 'assert:%s' % report)
|
2006-12-28 14:41:28 +00:00
|
|
|
|
2006-12-29 08:35:25 +00:00
|
|
|
for kind in ('init', 'demo', 'update'):
|
|
|
|
tools.config[kind]={}
|
|
|
|
|
2006-12-28 14:41:28 +00:00
|
|
|
cr.commit()
|
2006-12-28 11:06:46 +00:00
|
|
|
if update_module:
|
|
|
|
cr.execute("select id,name from ir_module_module where state in ('to remove')")
|
|
|
|
for mod_id, mod_name in cr.fetchall():
|
2006-12-28 14:41:28 +00:00
|
|
|
pool = pooler.get_pool(cr.dbname)
|
2006-12-28 11:06:46 +00:00
|
|
|
cr.execute('select model,res_id from ir_model_data where not noupdate and module=%s order by id desc', (mod_name,))
|
|
|
|
for rmod,rid in cr.fetchall():
|
2006-12-28 11:18:07 +00:00
|
|
|
#
|
|
|
|
# TO BE Improved:
|
|
|
|
# I can not use the class_pool has _table could be defined in __init__
|
|
|
|
# and I can not use the pool has the module could not be loaded in the pool
|
|
|
|
#
|
2006-12-28 14:41:28 +00:00
|
|
|
uid = 1
|
|
|
|
pool.get(rmod).unlink(cr, uid, [rid])
|
2006-12-28 11:06:46 +00:00
|
|
|
cr.commit()
|
|
|
|
#
|
|
|
|
# TODO: remove menu without actions of childs
|
|
|
|
#
|
|
|
|
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
|
2007-06-12 08:01:42 +00:00
|
|
|
(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'))''')
|
2006-12-28 11:06:46 +00:00
|
|
|
|
|
|
|
cr.execute("update ir_module_module set state=%s where state in ('to remove')", ('uninstalled', ))
|
|
|
|
cr.commit()
|
2006-12-28 14:41:28 +00:00
|
|
|
pooler.restart_pool(cr.dbname)
|
2006-12-07 13:41:40 +00:00
|
|
|
cr.close()
|
|
|
|
|