2008-07-23 15:01:27 +00:00
# -*- encoding: utf-8 -*-
2006-12-07 13:41:40 +00:00
##############################################################################
#
2008-11-07 05:35:10 +00:00
# OpenERP, Open Source Management Solution
2009-01-04 22:13:29 +00:00
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
2008-11-03 18:27:16 +00:00
# $Id$
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# 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 3 of the License, or
# (at your option) any later version.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# 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.
2006-12-07 13:41:40 +00:00
#
2008-11-03 18:27:16 +00:00
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
2006-12-07 13:41:40 +00:00
#
##############################################################################
import os , sys , imp
2008-12-04 16:11:41 +00:00
from os . path import join as opj
2006-12-07 13:41:40 +00:00
import itertools
2008-12-16 10:23:23 +00:00
import zipimport
2006-12-07 13:41:40 +00:00
import osv
import tools
2008-12-04 16:11:41 +00:00
import tools . osutil
2006-12-07 13:41:40 +00:00
import pooler
import netsvc
from osv import fields
2007-04-21 13:32:18 +00:00
import zipfile
2008-12-04 16:11:41 +00:00
import release
2007-04-21 13:32:18 +00:00
2009-01-02 14:43:25 +00:00
import re
import base64
from zipfile import PyZipFile , ZIP_DEFLATED
2009-01-09 11:35:25 +00:00
from cStringIO import StringIO
2009-01-02 14:43:25 +00:00
2006-12-07 13:41:40 +00:00
logger = netsvc . Logger ( )
2008-07-22 14:24:36 +00:00
_ad = os . path . abspath ( opj ( tools . config [ ' root_path ' ] , ' addons ' ) ) # default addons path (base)
ad = os . path . abspath ( tools . config [ ' addons_path ' ] ) # alternate addons path
2008-07-15 08:10:40 +00:00
sys . path . insert ( 1 , _ad )
if ad != _ad :
2008-07-22 14:24:36 +00:00
sys . path . insert ( 1 , ad )
2006-12-07 13:41:40 +00:00
2008-11-22 15:48:29 +00:00
# Modules already loaded
loaded = [ ]
2009-01-14 09:12:23 +00:00
2006-12-07 13:41:40 +00:00
class Graph ( dict ) :
2008-07-22 14:24:36 +00:00
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 )
2009-07-07 06:10:58 +00:00
2009-03-05 00:00:47 +00:00
def update_from_db ( self , cr ) :
# 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 ) ' % ( ' , ' . join ( [ ' %s ' ] * len ( self ) ) ) ,
additional_data . keys ( )
)
2008-07-22 14:24:36 +00:00
2009-03-05 00:00:47 +00:00
## and we update the default values with values from the database
additional_data . update ( dict ( [ ( x . pop ( ' name ' ) , x ) for x in cr . dictfetchall ( ) ] ) )
2007-08-09 13:12:25 +00:00
2009-03-05 00:00:47 +00:00
for package in self . values ( ) :
for k , v in additional_data [ package . name ] . items ( ) :
setattr ( package , k , v )
2009-01-14 09:12:23 +00:00
2006-12-07 13:41:40 +00:00
2009-03-05 00:00:47 +00:00
def __iter__ ( self ) :
2009-03-11 18:08:27 +00:00
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
2009-03-05 00:00:47 +00:00
class Singleton ( object ) :
2008-07-22 14:24:36 +00:00
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
2006-12-07 13:41:40 +00:00
2009-01-14 09:12:23 +00:00
2006-12-07 13:41:40 +00:00
class Node ( Singleton ) :
2008-07-22 14:24:36 +00:00
def __init__ ( self , name , graph ) :
self . graph = graph
2009-03-05 00:00:47 +00:00
if not hasattr ( self , ' children ' ) :
self . children = [ ]
2008-07-22 14:24:36 +00:00
if not hasattr ( self , ' depth ' ) :
self . depth = 0
def addChild ( self , name ) :
node = Node ( name , self . graph )
node . depth = self . depth + 1
2009-03-05 00:00:47 +00:00
if node not in self . children :
self . children . append ( node )
2008-07-22 14:24:36 +00:00
for attr in ( ' init ' , ' update ' , ' demo ' ) :
if hasattr ( self , attr ) :
setattr ( node , attr , True )
2009-03-05 00:00:47 +00:00
self . children . sort ( lambda x , y : cmp ( x . name , y . name ) )
2008-07-22 14:24:36 +00:00
def __setattr__ ( self , name , value ) :
super ( Singleton , self ) . __setattr__ ( name , value )
if name in ( ' init ' , ' update ' , ' demo ' ) :
tools . config [ name ] [ self . name ] = 1
2009-03-05 00:00:47 +00:00
for child in self . children :
2008-07-22 14:24:36 +00:00
setattr ( child , name , value )
if name == ' depth ' :
2009-03-05 00:00:47 +00:00
for child in self . children :
2008-07-22 14:24:36 +00:00
setattr ( child , name , value + 1 )
def __iter__ ( self ) :
2009-03-05 00:00:47 +00:00
return itertools . chain ( iter ( self . children ) , * map ( iter , self . children ) )
2009-07-07 06:10:58 +00:00
2008-07-22 14:24:36 +00:00
def __str__ ( self ) :
return self . _pprint ( )
def _pprint ( self , depth = 0 ) :
s = ' %s \n ' % self . name
2009-03-05 00:00:47 +00:00
for c in self . children :
2008-07-22 14:24:36 +00:00
s + = ' %s `-> %s ' % ( ' ' * depth , c . _pprint ( depth + 1 ) )
return s
2006-12-07 13:41:40 +00:00
2009-01-14 09:12:23 +00:00
2009-03-02 13:31:40 +00:00
def get_module_path ( module , downloaded = False ) :
2009-01-14 09:12:23 +00:00
""" Return the path of the given module. """
2008-07-22 14:24:36 +00:00
if os . path . exists ( opj ( ad , module ) ) or os . path . exists ( opj ( ad , ' %s .zip ' % module ) ) :
return opj ( ad , module )
2008-07-16 09:07:15 +00:00
2008-07-22 14:24:36 +00:00
if os . path . exists ( opj ( _ad , module ) ) or os . path . exists ( opj ( _ad , ' %s .zip ' % module ) ) :
return opj ( _ad , module )
2009-03-02 13:31:40 +00:00
if downloaded :
return opj ( _ad , module )
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_WARNING , ' module %s : module not found ' % ( module , ) )
2008-10-27 12:18:52 +00:00
return False
2008-12-04 16:11:41 +00:00
2009-01-14 09:12:23 +00:00
2008-12-04 16:11:41 +00:00
def get_module_filetree ( module , dir = ' . ' ) :
path = get_module_path ( module )
if not path :
return False
2009-01-14 09:12:23 +00:00
2008-12-04 16:11:41 +00:00
dir = os . path . normpath ( dir )
2009-01-14 07:59:02 +00:00
if dir == ' . ' :
dir = ' '
if dir . startswith ( ' .. ' ) or ( dir and dir [ 0 ] == ' / ' ) :
2008-12-04 16:11:41 +00:00
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 = tools . osutil . listdir ( path , True )
2009-01-14 09:12:23 +00:00
2008-12-04 16:11:41 +00:00
tree = { }
for f in files :
if not f . startswith ( dir ) :
continue
2009-01-14 09:12:23 +00:00
2009-01-14 08:10:41 +00:00
if dir :
f = f [ len ( dir ) + int ( not dir . endswith ( ' / ' ) ) : ]
2008-12-04 16:11:41 +00:00
lst = f . split ( os . sep )
current = tree
while len ( lst ) != 1 :
current = current . setdefault ( lst . pop ( 0 ) , { } )
current [ lst . pop ( 0 ) ] = None
2009-01-14 09:12:23 +00:00
2008-12-04 16:11:41 +00:00
return tree
2009-01-22 15:25:36 +00:00
def get_module_as_zip_from_module_directory ( module_directory , b64enc = True , src = True ) :
""" Compress a module directory
2009-01-02 14:43:25 +00:00
2009-01-22 15:25:36 +00:00
@param module_directory : The module directory
@param base64enc : 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
"""
2009-01-14 09:12:23 +00:00
2009-01-02 14:43:25 +00:00
RE_exclude = re . compile ( ' (?:^ \ ..+ \ .swp$)|(?: \ .py[oc]$)|(?: \ .bak$)|(?: \ .~.~$) ' , re . I )
2009-01-14 09:12:23 +00:00
2009-01-02 14:43:25 +00:00
def _zippy ( archive , path , src = True ) :
path = os . path . abspath ( path )
base = os . path . basename ( path )
for f in tools . osutil . listdir ( path , True ) :
bf = os . path . basename ( f )
2009-01-22 14:58:53 +00:00
if not RE_exclude . search ( bf ) and ( src or bf == ' __terp__.py ' or not bf . endswith ( ' .py ' ) ) :
2009-01-02 14:43:25 +00:00
archive . write ( os . path . join ( path , f ) , os . path . join ( base , f ) )
2009-01-14 09:12:23 +00:00
2009-01-22 15:25:36 +00:00
archname = StringIO ( )
archive = PyZipFile ( archname , " w " , ZIP_DEFLATED )
archive . writepy ( module_directory )
_zippy ( archive , module_directory , src = src )
archive . close ( )
val = archname . getvalue ( )
archname . close ( )
if b64enc :
val = base64 . encodestring ( val )
return val
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
"""
2009-01-02 14:43:25 +00:00
ap = get_module_path ( str ( modulename ) )
if not ap :
raise Exception ( ' Unable to find path for module %s ' % modulename )
2009-01-14 09:12:23 +00:00
ap = ap . encode ( ' utf8 ' )
2009-01-02 14:43:25 +00:00
if os . path . isfile ( ap + ' .zip ' ) :
val = file ( ap + ' .zip ' , ' rb ' ) . read ( )
2009-01-22 15:25:36 +00:00
if b64enc :
val = base64 . encodestring ( val )
2009-01-02 14:43:25 +00:00
else :
2009-01-22 15:25:36 +00:00
val = get_module_as_zip_from_module_directory ( ap , b64enc , src )
2009-01-02 14:43:25 +00:00
return val
2008-07-16 06:43:50 +00:00
def get_module_resource ( module , * args ) :
2008-07-22 14:24:36 +00:00
""" Return the full path of a resource of the given module.
2008-07-16 06:43:50 +00:00
2008-07-22 14:24:36 +00:00
@param module : the module
@param args : the resource path components
2008-07-16 06:43:50 +00:00
2008-07-22 14:24:36 +00:00
@return : absolute path to the resource
"""
2008-10-27 12:18:52 +00:00
a = get_module_path ( module )
return a and opj ( a , * args ) or False
2008-07-16 06:43:50 +00:00
2009-01-14 09:12:23 +00:00
2008-07-16 06:43:50 +00:00
def get_modules ( ) :
2008-07-22 14:24:36 +00:00
""" Returns the list of module names
"""
2008-11-20 12:12:43 +00:00
def listdir ( dir ) :
def clean ( name ) :
name = os . path . basename ( name )
if name [ - 4 : ] == ' .zip ' :
name = name [ : - 4 ]
return name
2008-12-15 12:29:58 +00:00
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 ) ) )
2008-07-16 06:43:50 +00:00
2008-11-20 12:12:43 +00:00
return list ( set ( listdir ( ad ) + listdir ( _ad ) ) )
2008-07-16 06:43:50 +00:00
2009-02-11 16:19:06 +00:00
def get_modules_with_version ( ) :
modules = get_modules ( )
res = { }
for module in modules :
terp = get_module_resource ( module , ' __terp__.py ' )
try :
info = eval ( tools . file_open ( terp ) . read ( ) )
res [ module ] = " %s . %s " % ( release . major_version , info [ ' version ' ] )
except Exception , e :
continue
return res
2009-01-14 09:12:23 +00:00
2009-02-05 10:30:42 +00:00
def create_graph ( cr , module_list , force = None ) :
2008-07-22 14:24:36 +00:00
graph = Graph ( )
2009-02-05 10:30:42 +00:00
upgrade_graph ( graph , cr , module_list , force )
2009-01-31 20:29:14 +00:00
return graph
2008-07-22 14:24:36 +00:00
2009-02-05 10:30:42 +00:00
def upgrade_graph ( graph , cr , module_list , force = None ) :
2009-01-29 09:59:37 +00:00
if force is None :
force = [ ]
packages = [ ]
2009-01-31 20:29:14 +00:00
len_graph = len ( graph )
2008-07-22 14:24:36 +00:00
for module in module_list :
2009-02-05 10:30:42 +00:00
mod_path = get_module_path ( module )
2008-07-22 14:24:36 +00:00
terp_file = get_module_resource ( module , ' __terp__.py ' )
2009-02-05 10:30:42 +00:00
if not mod_path or not terp_file :
cr . execute ( " update ir_module_module set state= %s where name= %s " , ( ' uninstallable ' , module ) )
2009-01-14 09:12:23 +00:00
continue
2009-02-05 10:30:42 +00:00
2008-11-07 05:35:10 +00:00
if os . path . isfile ( terp_file ) or zipfile . is_zipfile ( mod_path + ' .zip ' ) :
2008-07-22 14:24:36 +00:00
try :
info = eval ( tools . file_open ( terp_file ) . read ( ) )
except :
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_ERROR , ' module %s : eval file %s ' % ( module , terp_file ) )
2008-07-22 14:24:36 +00:00
raise
if info . get ( ' installable ' , True ) :
packages . append ( ( module , info . get ( ' depends ' , [ ] ) , info ) )
2009-07-07 06:10:58 +00:00
2009-01-14 09:12:23 +00:00
2008-11-20 17:16:29 +00:00
dependencies = dict ( [ ( p , deps ) for p , deps , data in packages ] )
2009-03-05 00:00:47 +00:00
current , later = set ( [ p for p , dep , data in packages ] ) , set ( )
2008-07-22 14:24:36 +00:00
while packages and current > later :
2008-12-04 16:11:41 +00:00
package , deps , data = packages [ 0 ]
2008-07-22 14:24:36 +00:00
# if all dependencies of 'package' are already in the graph, add 'package' in the graph
2009-01-14 09:12:23 +00:00
if reduce ( lambda x , y : x and y in graph , deps , True ) :
2008-07-22 14:24:36 +00:00
if not package in current :
packages . pop ( 0 )
continue
later . clear ( )
current . remove ( package )
graph . addNode ( package , deps )
node = Node ( package , graph )
2008-12-04 16:11:41 +00:00
node . data = data
2008-07-22 14:24:36 +00:00
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 )
2008-12-04 16:11:41 +00:00
packages . append ( ( package , deps , data ) )
2008-07-22 14:24:36 +00:00
packages . pop ( 0 )
2009-01-14 09:12:23 +00:00
2009-03-05 00:00:47 +00:00
graph . update_from_db ( cr )
2008-07-22 14:24:36 +00:00
for package in later :
2008-11-20 17:16:29 +00:00
unmet_deps = filter ( lambda p : p not in graph , dependencies [ package ] )
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_ERROR , ' module %s : Unmet dependencies: %s ' % ( package , ' , ' . join ( unmet_deps ) ) )
2009-07-07 06:10:58 +00:00
2009-01-31 20:29:14 +00:00
result = len ( graph ) - len_graph
if result != len ( module_list ) :
logger . notifyChannel ( ' init ' , netsvc . LOG_WARNING , ' Not all modules have loaded. ' )
return result
2006-12-07 13:41:40 +00:00
2009-01-14 09:12:23 +00:00
2006-12-07 13:41:40 +00:00
def init_module_objects ( cr , module_name , obj_list ) :
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' module %s : creating or updating database tables ' % module_name )
todo = [ ]
2008-07-22 14:24:36 +00:00
for obj in obj_list :
2008-12-14 16:46:47 +00:00
result = obj . _auto_init ( cr , { ' module ' : module_name } )
if result :
todo + = result
2009-02-11 15:01:24 +00:00
if hasattr ( obj , ' init ' ) :
obj . init ( cr )
2008-07-22 14:24:36 +00:00
cr . commit ( )
2008-12-14 16:46:47 +00:00
todo . sort ( )
for t in todo :
t [ 1 ] ( cr , * t [ 2 ] )
cr . commit ( )
2006-12-07 13:41:40 +00:00
2009-01-14 09:12:23 +00:00
2008-11-22 15:48:29 +00:00
def register_class ( m ) :
2008-12-16 10:23:23 +00:00
"""
Register module named m , if not already registered
"""
2009-01-07 14:52:34 +00:00
def log ( e ) :
mt = isinstance ( e , zipimport . ZipImportError ) and ' zip ' or ' '
2009-01-19 20:51:52 +00:00
msg = " Couldn ' t load %s module %s " % ( mt , m )
2009-01-07 14:52:34 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_CRITICAL , msg )
logger . notifyChannel ( ' init ' , netsvc . LOG_CRITICAL , e )
2008-11-22 15:48:29 +00:00
global loaded
if m in loaded :
return
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' module %s : registering objects ' % m )
2008-11-22 15:48:29 +00:00
mod_path = get_module_path ( m )
2009-01-19 20:51:52 +00:00
2008-12-16 10:23:23 +00:00
try :
zip_mod_path = mod_path + ' .zip '
if not os . path . isfile ( zip_mod_path ) :
2009-01-02 22:44:12 +00:00
fm = imp . find_module ( m , [ ad , _ad ] )
try :
imp . load_module ( m , * fm )
finally :
if fm [ 0 ] :
fm [ 0 ] . close ( )
2008-12-16 10:23:23 +00:00
else :
zimp = zipimport . zipimporter ( zip_mod_path )
2008-11-22 15:48:29 +00:00
zimp . load_module ( m )
2009-01-07 14:52:34 +00:00
except Exception , e :
log ( e )
2008-12-16 10:23:23 +00:00
raise
2009-01-07 14:52:34 +00:00
else :
loaded . append ( m )
2008-11-22 15:48:29 +00:00
2008-12-04 16:11:41 +00:00
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
2009-01-14 09:12:23 +00:00
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
2008-12-04 16:11:41 +00:00
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
2009-01-14 09:12:23 +00:00
This similar structure is generated by the maintenance module with the migrations files get by
2008-12-04 16:11:41 +00:00
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
2009-01-14 09:12:23 +00:00
2008-12-04 16:11:41 +00:00
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 (
2009-01-14 09:12:23 +00:00
__get_dir ( self . migrations [ pkg . name ] [ ' module ' ] ) +
2008-12-04 16:11:41 +00:00
__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 = [ ]
2009-01-14 09:12:23 +00:00
2009-01-22 16:16:16 +00:00
mapping = { ' module ' : opj ( pkg . name , ' migrations ' ) ,
' maintenance ' : opj ( ' base ' , ' maintenance ' , ' migrations ' , pkg . name ) ,
2008-12-04 16:11:41 +00:00
}
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
2009-01-22 16:16:16 +00:00
lst . append ( opj ( mapping [ x ] , version , f ) )
lst . sort ( )
2008-12-04 16:11:41 +00:00
return lst
2009-01-14 09:12:23 +00:00
def mergedict ( a , b ) :
2008-12-04 16:11:41 +00:00
a = a . copy ( )
a . update ( b )
return a
from tools . parse_version import parse_version
2008-12-11 12:47:24 +00:00
parsed_installed_version = parse_version ( pkg . installed_version or ' ' )
2008-12-04 16:11:41 +00:00
current_version = parse_version ( convert_version ( pkg . data . get ( ' version ' , ' 0 ' ) ) )
2009-07-07 06:10:58 +00:00
2008-12-04 16:11:41 +00:00
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 ,
}
2009-01-14 09:12:23 +00:00
2009-01-22 16:16:16 +00:00
for pyfile in _get_migration_files ( pkg , version , stage ) :
2008-12-04 16:11:41 +00:00
name , ext = os . path . splitext ( os . path . basename ( pyfile ) )
if ext . lower ( ) != ' .py ' :
continue
2009-01-09 15:54:38 +00:00
mod = fp = fp2 = None
2008-12-04 16:11:41 +00:00
try :
2009-01-22 16:16:16 +00:00
fp = tools . file_open ( pyfile )
2009-01-14 09:12:23 +00:00
# imp.load_source need a real file object, so we create
2009-01-21 12:19:36 +00:00
# one from the file-like object we get from file_open
2009-01-09 15:54:38 +00:00
fp2 = os . tmpfile ( )
fp2 . write ( fp . read ( ) )
fp2 . seek ( 0 )
try :
mod = imp . load_source ( name , pyfile , fp2 )
2009-01-22 16:16:16 +00:00
logger . notifyChannel ( ' migration ' , netsvc . LOG_INFO , ' module %(addon)s : Running migration %(version)s %(name)s ' % mergedict ( { ' name ' : mod . __name__ } , strfmt ) )
2009-01-09 15:54:38 +00:00
mod . migrate ( self . cr , pkg . installed_version )
except ImportError :
2009-01-22 16:16:16 +00:00
logger . notifyChannel ( ' migration ' , netsvc . LOG_ERROR , ' module %(addon)s : Unable to load %(stage)s -migration file %(file)s ' % mergedict ( { ' file ' : pyfile } , strfmt ) )
2009-01-09 15:54:38 +00:00
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
2009-01-14 09:12:23 +00:00
2008-11-22 15:48:29 +00:00
2009-01-08 09:02:00 +00:00
def load_module_graph ( cr , graph , status = None , perform_checks = True , * * kwargs ) :
2008-07-22 14:24:36 +00:00
# **kwargs is passed directly to convert_xml_import
if not status :
2009-01-14 09:12:23 +00:00
status = { }
2008-07-22 14:24:36 +00:00
status = status . copy ( )
package_todo = [ ]
statusi = 0
2008-12-04 16:11:41 +00:00
pool = pooler . get_pool ( cr . dbname )
migrations = MigrationManager ( cr , graph )
2009-01-29 09:59:37 +00:00
has_updates = False
2009-01-07 15:53:45 +00:00
modobj = None
2009-03-15 18:00:17 +00:00
for package in graph :
2009-07-07 09:47:02 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' module %s : loading objects ' % package . name )
2009-03-15 18:00:17 +00:00
migrations . migrate_module ( package , ' pre ' )
2009-07-07 09:47:02 +00:00
register_class ( package . name )
modules = pool . instanciate ( package . name , cr )
2009-03-15 18:00:17 +00:00
if hasattr ( package , ' init ' ) or hasattr ( package , ' update ' ) or package . state in ( ' to install ' , ' to upgrade ' ) :
2009-07-07 09:47:02 +00:00
init_module_objects ( cr , package . name , modules )
2009-03-15 18:00:17 +00:00
cr . commit ( )
2009-07-07 09:47:02 +00:00
for package in graph :
2009-01-14 09:12:23 +00:00
status [ ' progress ' ] = ( float ( statusi ) + 0.1 ) / len ( graph )
2009-07-07 09:47:02 +00:00
m = package . name
mid = package . id
2008-11-07 05:35:10 +00:00
2009-01-07 15:53:45 +00:00
if modobj is None :
modobj = pool . get ( ' ir.module.module ' )
2009-01-08 09:02:00 +00:00
if modobj and perform_checks :
2009-01-07 15:53:45 +00:00
modobj . check ( cr , 1 , [ mid ] )
2008-07-22 14:24:36 +00:00
idref = { }
2009-01-14 09:12:23 +00:00
status [ ' progress ' ] = ( float ( statusi ) + 0.4 ) / len ( graph )
2009-07-07 06:10:58 +00:00
2009-02-17 13:02:00 +00:00
mode = ' update '
2009-02-20 10:29:55 +00:00
if hasattr ( package , ' init ' ) or package . state == ' to install ' :
2009-02-17 13:02:00 +00:00
mode = ' init '
2009-03-15 18:00:17 +00:00
2008-12-04 16:11:41 +00:00
if hasattr ( package , ' init ' ) or hasattr ( package , ' update ' ) or package . state in ( ' to install ' , ' to upgrade ' ) :
2009-01-29 09:59:37 +00:00
has_updates = True
2008-07-22 14:24:36 +00:00
for kind in ( ' init ' , ' update ' ) :
2008-12-04 16:11:41 +00:00
for filename in package . data . get ( ' %s _xml ' % kind , [ ] ) :
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' module %s : loading %s ' % ( m , filename ) )
2008-07-22 14:24:36 +00:00
name , ext = os . path . splitext ( filename )
2008-12-04 16:11:41 +00:00
fp = tools . file_open ( opj ( m , filename ) )
2008-07-22 14:24:36 +00:00
if ext == ' .csv ' :
2008-12-04 16:11:41 +00:00
tools . convert_csv_import ( cr , m , os . path . basename ( filename ) , fp . read ( ) , idref , mode = mode )
2008-07-22 14:24:36 +00:00
elif ext == ' .sql ' :
2008-12-04 16:11:41 +00:00
queries = fp . read ( ) . split ( ' ; ' )
2008-07-22 14:24:36 +00:00
for query in queries :
new_query = ' ' . join ( query . split ( ) )
if new_query :
cr . execute ( new_query )
else :
2008-12-04 16:11:41 +00:00
tools . convert_xml_import ( cr , m , fp , idref , mode = mode , * * kwargs )
fp . close ( )
if hasattr ( package , ' demo ' ) or ( package . dbdemo and package . state != ' installed ' ) :
2009-01-14 09:12:23 +00:00
status [ ' progress ' ] = ( float ( statusi ) + 0.75 ) / len ( graph )
2008-12-04 16:11:41 +00:00
for xml in package . data . get ( ' demo_xml ' , [ ] ) :
2008-07-22 14:24:36 +00:00
name , ext = os . path . splitext ( xml )
2008-12-14 16:46:47 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' module %s : loading %s ' % ( m , xml ) )
2008-12-04 16:11:41 +00:00
fp = tools . file_open ( opj ( m , xml ) )
2008-07-22 14:24:36 +00:00
if ext == ' .csv ' :
2009-01-21 12:19:36 +00:00
tools . convert_csv_import ( cr , m , os . path . basename ( xml ) , fp . read ( ) , idref , mode = mode , noupdate = True )
2008-07-22 14:24:36 +00:00
else :
2009-01-21 12:19:36 +00:00
tools . convert_xml_import ( cr , m , fp , idref , mode = mode , noupdate = True , * * kwargs )
2008-12-04 16:11:41 +00:00
fp . close ( )
2008-12-09 12:37:22 +00:00
cr . execute ( ' update ir_module_module set demo= %s where id= %s ' , ( True , mid ) )
2009-07-07 09:47:02 +00:00
package_todo . append ( package . name )
2008-12-09 14:31:53 +00:00
2009-01-29 09:59:37 +00:00
migrations . migrate_module ( package , ' post ' )
2009-07-07 06:10:58 +00:00
2008-10-27 13:07:56 +00:00
if modobj :
2009-01-21 12:19:36 +00:00
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 } )
2009-01-22 18:27:00 +00:00
cr . commit ( )
2009-01-21 12:19:36 +00:00
# Update translations for all installed languages
2008-10-27 13:07:56 +00:00
modobj . update_translations ( cr , 1 , [ mid ] , None )
cr . commit ( )
2009-07-07 06:10:58 +00:00
2009-01-29 09:59:37 +00:00
package . state = ' installed '
for kind in ( ' init ' , ' demo ' , ' update ' ) :
if hasattr ( package , kind ) :
delattr ( package , kind )
2009-01-21 12:19:36 +00:00
2009-01-14 09:12:23 +00:00
statusi + = 1
2009-07-07 06:10:58 +00:00
2008-12-10 15:06:32 +00:00
cr . execute ( ' select model from ir_model where state= %s ' , ( ' manual ' , ) )
2008-07-22 14:24:36 +00:00
for model in cr . dictfetchall ( ) :
pool . get ( ' ir.model ' ) . instanciate ( cr , 1 , model [ ' model ' ] , { } )
pool . get ( ' ir.model.data ' ) . _process_end ( cr , 1 , package_todo )
cr . commit ( )
2009-07-07 06:10:58 +00:00
2009-01-29 09:59:37 +00:00
return has_updates
2009-01-14 09:12:23 +00:00
2007-07-23 05:16:03 +00:00
def load_modules ( db , force_demo = False , status = None , update_module = False ) :
2008-07-22 14:24:36 +00:00
if not status :
2009-01-14 09:12:23 +00:00
status = { }
2008-12-16 10:23:23 +00:00
2008-07-22 14:24:36 +00:00
cr = db . cursor ( )
2008-12-16 18:15:35 +00:00
force = [ ]
if force_demo :
force . append ( ' demo ' )
pool = pooler . get_pool ( cr . dbname )
2008-12-16 10:23:23 +00:00
try :
report = tools . assertion_report ( )
2009-04-28 14:11:24 +00:00
# NOTE: Try to also load the modules that have been marked as uninstallable previously...
STATES_TO_LOAD = [ ' installed ' , ' to upgrade ' , ' uninstallable ' ]
2009-02-05 10:30:42 +00:00
graph = create_graph ( cr , [ ' base ' ] , force )
2009-07-07 06:10:58 +00:00
2009-06-05 09:34:14 +00:00
has_updates = load_module_graph ( cr , graph , status , perform_checks = ( not update_module ) , report = report )
2008-12-16 10:23:23 +00:00
2009-06-05 09:34:14 +00:00
if update_module :
2008-12-16 10:23:23 +00:00
modobj = pool . get ( ' ir.module.module ' )
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' updating modules list ' )
2009-03-16 17:49:01 +00:00
if ( ' base ' in tools . config [ ' init ' ] ) or ( ' base ' in tools . config [ ' update ' ] ) :
modobj . update_list ( cr , 1 )
2008-12-16 10:23:23 +00:00
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 )
2009-01-14 09:12:23 +00:00
2008-12-16 10:23:23 +00:00
mods = [ k for k in tools . config [ ' update ' ] if tools . config [ ' update ' ] [ k ] ]
if mods :
2009-01-14 09:12:23 +00:00
ids = modobj . search ( cr , 1 , [ ' & ' , ( ' state ' , ' = ' , ' installed ' ) , ( ' name ' , ' in ' , mods ) ] )
2008-12-16 10:23:23 +00:00
if ids :
modobj . button_upgrade ( cr , 1 , ids )
2009-01-14 09:12:23 +00:00
2008-12-16 10:23:23 +00:00
cr . execute ( " update ir_module_module set state= %s where name= %s " , ( ' installed ' , ' base ' ) )
2009-07-07 06:10:58 +00:00
2009-01-29 09:59:37 +00:00
STATES_TO_LOAD + = [ ' to install ' ]
2009-07-07 06:10:58 +00:00
2009-01-31 20:29:14 +00:00
loop_guardrail = 0
2009-01-29 09:59:37 +00:00
while True :
2009-01-31 20:29:14 +00:00
loop_guardrail + = 1
if loop_guardrail > 100 :
raise ProgrammingError ( )
2009-01-29 09:59:37 +00:00
cr . execute ( " SELECT name from ir_module_module WHERE state in ( %s ) " % ' , ' . join ( [ ' %s ' ] * len ( STATES_TO_LOAD ) ) , STATES_TO_LOAD )
module_list = [ name for ( name , ) in cr . fetchall ( ) if name not in graph ]
if not module_list :
break
2009-01-31 20:29:14 +00:00
2009-02-05 10:30:42 +00:00
new_modules_in_graph = upgrade_graph ( graph , cr , module_list , force )
2009-01-31 20:29:14 +00:00
if new_modules_in_graph == 0 :
# nothing to load
break
2009-07-07 06:10:58 +00:00
2009-01-31 20:29:14 +00:00
logger . notifyChannel ( ' init ' , netsvc . LOG_DEBUG , ' Updating graph with %d more modules ' % ( len ( module_list ) ) )
2009-01-29 09:59:37 +00:00
r = load_module_graph ( cr , graph , status , report = report )
has_updates = has_updates or r
if has_updates :
cr . execute ( """ select model,name from ir_model where id not in (select model_id from ir_model_access) """ )
for ( model , name ) in cr . fetchall ( ) :
logger . notifyChannel ( ' init ' , netsvc . LOG_WARNING , ' object %s ( %s ) has no 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 )
2009-01-31 20:29:14 +00:00
2008-12-16 10:23:23 +00:00
if report . get_report ( ) :
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , report )
2008-07-22 14:24:36 +00:00
2008-12-16 10:23:23 +00:00
for kind in ( ' init ' , ' demo ' , ' update ' ) :
2009-01-14 09:12:23 +00:00
tools . config [ kind ] = { }
2008-07-22 14:24:36 +00:00
cr . commit ( )
2008-12-16 10:23:23 +00:00
if update_module :
2008-12-30 13:08:29 +00:00
cr . execute ( " select id,name from ir_module_module where state= %s " , ( ' to remove ' , ) )
2008-12-16 10:23:23 +00:00
for mod_id , mod_name in cr . fetchall ( ) :
2008-12-30 13:08:29 +00:00
cr . execute ( ' select model,res_id from ir_model_data where noupdate= %s and module= %s order by id desc ' , ( False , mod_name , ) )
2009-01-14 09:12:23 +00:00
for rmod , rid in cr . fetchall ( ) :
2008-12-16 10:23:23 +00:00
uid = 1
pool . get ( rmod ) . unlink ( cr , uid , [ rid ] )
2009-01-02 14:57:26 +00:00
cr . execute ( ' delete from ir_model_data where noupdate= %s and module= %s ' , ( False , mod_name , ) )
2008-12-16 10:23:23 +00:00
cr . commit ( )
#
2009-03-05 00:00:47 +00:00
# TODO: remove menu without actions of children
2008-12-16 10:23:23 +00:00
#
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 ' ) ) ''' )
2008-12-30 13:08:29 +00:00
cr . commit ( )
2008-12-16 10:23:23 +00:00
if not cr . rowcount :
break
else :
logger . notifyChannel ( ' init ' , netsvc . LOG_INFO , ' removed %d unused menus ' % ( cr . rowcount , ) )
2009-01-29 09:59:37 +00:00
cr . execute ( " update ir_module_module set state= %s where state= %s " , ( ' uninstalled ' , ' to remove ' , ) )
2008-12-16 10:23:23 +00:00
cr . commit ( )
finally :
cr . close ( )
2006-12-07 13:41:40 +00:00
2008-07-23 15:01:27 +00:00
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: