2006-12-07 13:41:40 +00:00
##############################################################################
#
# Copyright (c) 2005 TINY SPRL. (http://tiny.be) All Rights Reserved.
#
# 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 contract 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 tarfile
import re
import urllib
import os
import tools
from osv import fields , osv
class module_repository ( osv . osv ) :
_name = " ir.module.repository "
_description = " Module Repository "
_columns = {
' name ' : fields . char ( ' Name ' , size = 128 ) ,
' url ' : fields . char ( ' Url ' , size = 256 , required = True ) ,
}
module_repository ( )
def get_module_info ( name ) :
try :
f = file ( os . path . join ( tools . config [ ' addons_path ' ] , name , ' __terp__.py ' ) , ' r ' )
data = f . read ( )
info = eval ( data )
f . close ( )
except :
return { }
return info
class module_category ( osv . osv ) :
_name = " ir.module.category "
_description = " Module Category "
def _module_nbr ( self , cr , uid , ids , prop , unknow_none , context ) :
cr . execute ( ' select category_id,count(*) from ir_module_module where category_id in ( ' + ' , ' . join ( map ( str , ids ) ) + ' ) or category_id in (select id from ir_module_category where parent_id in ( ' + ' , ' . join ( map ( str , ids ) ) + ' )) group by category_id ' )
result = dict ( cr . fetchall ( ) )
for id in ids :
cr . execute ( ' select id from ir_module_category where parent_id= %d ' , ( id , ) )
childs = [ c for c , in cr . fetchall ( ) ]
result [ id ] = reduce ( lambda x , y : x + y , [ result . get ( c , 0 ) for c in childs ] , result . get ( id , 0 ) )
return result
_columns = {
' name ' : fields . char ( " Name " , size = 128 , required = True ) ,
' parent_id ' : fields . many2one ( ' ir.module.category ' , ' Parent Category ' , select = True ) ,
' child_ids ' : fields . one2many ( ' ir.module.category ' , ' parent_id ' , ' Parent Category ' ) ,
' module_nr ' : fields . function ( _module_nbr , method = True , string = ' # of Modules ' , type = ' integer ' )
}
_order = ' name '
module_category ( )
class module ( osv . osv ) :
_name = " ir.module.module "
_description = " Module "
def _get_installed_version ( self , cr , uid , ids , field_name = None , arg = None , context = { } ) :
res = { }
for m in self . browse ( cr , uid , ids ) :
res [ m . id ] = get_module_info ( m . name ) . get ( ' version ' , False )
return res
_columns = {
' name ' : fields . char ( " Name " , size = 128 , readonly = True , required = True ) ,
' category_id ' : fields . many2one ( ' ir.module.category ' , ' Category ' , readonly = True ) ,
' shortdesc ' : fields . char ( ' Short description ' , size = 256 , readonly = True ) ,
' description ' : fields . text ( " Description " , readonly = True ) ,
' author ' : fields . char ( " Author " , size = 128 , readonly = True ) ,
' website ' : fields . char ( " Website " , size = 256 , readonly = True ) ,
' installed_version ' : fields . function ( _get_installed_version , method = True , string = ' Installed version ' , type = ' char ' ) ,
' latest_version ' : fields . char ( ' Latest version ' , size = 64 , readonly = True ) ,
' url ' : fields . char ( ' URL ' , size = 128 , readonly = True ) ,
' dependencies_id ' : fields . one2many ( ' ir.module.module.dependency ' , ' module_id ' , ' Dependencies ' ) ,
' state ' : fields . selection ( [
( ' uninstallable ' , ' Uninstallable ' ) ,
( ' uninstalled ' , ' Not Installed ' ) ,
( ' installed ' , ' Installed ' ) ,
( ' to upgrade ' , ' To be upgraded ' ) ,
( ' to remove ' , ' To be removed ' ) ,
( ' to install ' , ' To be installed ' )
] , string = ' State ' , readonly = True ) ,
' demo ' : fields . boolean ( ' Demo data ' ) ,
}
_defaults = {
' state ' : lambda * a : ' uninstalled ' ,
' demo ' : lambda * a : False ,
}
_order = ' name '
def state_change ( self , cr , uid , ids , newstate , context = { } , level = 50 ) :
if level < 1 :
raise ' Recursion error in modules dependencies ! '
2006-12-14 15:16:54 +00:00
demo = True
2006-12-07 13:41:40 +00:00
for module in self . browse ( cr , uid , ids ) :
2006-12-14 15:16:54 +00:00
mdemo = True
2006-12-07 13:41:40 +00:00
for dep in module . dependencies_id :
ids2 = self . search ( cr , uid , [ ( ' name ' , ' = ' , dep . name ) ] )
2006-12-14 15:16:54 +00:00
mdemo = mdemo and self . state_change ( cr , uid , ids2 , newstate , context , level - 1 )
if not module . dependencies_id :
mdemo = module . demo
2006-12-14 16:11:18 +00:00
if module . state == ' uninstalled ' :
self . write ( cr , uid , [ module . id ] , { ' state ' : newstate , ' demo ' : mdemo } )
2006-12-14 15:16:54 +00:00
demo = demo and mdemo
return demo
2006-12-07 13:41:40 +00:00
def button_install ( self , cr , uid , ids , context = { } ) :
return self . state_change ( cr , uid , ids , ' to install ' , context )
def button_install_cancel ( self , cr , uid , ids , context = { } ) :
2006-12-14 15:16:54 +00:00
self . write ( cr , uid , ids , { ' state ' : ' uninstalled ' , ' demo ' : False } )
2006-12-07 13:41:40 +00:00
return True
def button_uninstall ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' to remove ' } )
return True
def button_remove_cancel ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' installed ' } )
return True
def button_upgrade ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' to upgrade ' } )
return True
def button_upgrade_cancel ( self , cr , uid , ids , context = { } ) :
self . write ( cr , uid , ids , { ' state ' : ' installed ' } )
return True
def button_update_translations ( self , cr , uid , ids , context = { } ) :
cr . execute ( ' select code from res_lang where translatable=TRUE ' )
langs = [ l [ 0 ] for l in cr . fetchall ( ) ]
modules = self . read ( cr , uid , ids , [ ' name ' ] )
for module in modules :
files = get_module_info ( module [ ' name ' ] ) . get ( ' translations ' , { } )
for lang in langs :
if files . has_key ( lang ) :
filepath = files [ lang ]
# if filepath does not contain :// we prepend the path of the module
if filepath . find ( ' :// ' ) == - 1 :
filepath = os . path . join ( tools . config [ ' addons_path ' ] , module [ ' name ' ] , filepath )
tools . trans_load ( filepath , lang )
return True
# update the list of available packages
def update_list ( self , cr , uid , context = { } ) :
robj = self . pool . get ( ' ir.module.repository ' )
adp = tools . config [ ' addons_path ' ]
# iterate through installed modules and mark them as being so
for name in os . listdir ( adp ) :
if os . path . isdir ( os . path . join ( adp , name ) ) :
version = get_module_info ( name ) . get ( ' version ' , False )
if version :
ids = self . search ( cr , uid , [ ( ' name ' , ' = ' , name ) ] )
if not ids :
id = self . create ( cr , uid , {
' name ' : name ,
' latest_version ' : version ,
' state ' : ' installed ' ,
} )
else :
self . write ( cr , uid , ids , { ' state ' : ' installed ' } )
# make the list of all installable modules
for repository in robj . browse ( cr , uid , robj . search ( cr , uid , [ ] ) ) :
index_page = urllib . urlopen ( repository . url ) . read ( )
modules = re . findall ( ' .*<a href= " ([a-zA-Z0-9. \ -]+)_([a-zA-Z0-9. \ -]+) \ .tar \ .gz " >.* ' , index_page )
for name , version in modules :
# TODO: change this using urllib
url = os . path . join ( repository . url , name + ' _ ' + version + " .tar.gz " )
ids = self . search ( cr , uid , [ ( ' name ' , ' = ' , name ) ] )
if not ids :
self . create ( cr , uid , {
' name ' : name ,
' latest_version ' : version ,
' url ' : url ,
' state ' : ' uninstalled ' ,
} )
else :
for r in self . read ( cr , uid , ids , [ ' latest_version ' ] ) :
if r [ ' latest_version ' ] < version :
self . write ( cr , uid , [ r [ ' id ' ] ] , { ' latest_version ' : version , ' url ' : url } )
return True
#
# TODO: update dependencies
#
def info_get ( self , cr , uid , ids , context = { } ) :
categ_obj = self . pool . get ( ' ir.module.category ' )
for module in self . browse ( cr , uid , ids , context ) :
url = module . url
adp = tools . config [ ' addons_path ' ]
info = False
if url :
tar = tarfile . open ( mode = " r|gz " , fileobj = urllib . urlopen ( url ) )
for tarinfo in tar :
if tarinfo . name . endswith ( ' __terp__.py ' ) :
info = eval ( tar . extractfile ( tarinfo ) . read ( ) )
elif os . path . isdir ( os . path . join ( adp , module . name ) ) :
info = get_module_info ( module . name )
if info :
categ = info . get ( ' category ' , ' Unclassified ' )
parent = False
for c in categ . split ( ' / ' ) :
ids = categ_obj . search ( cr , uid , [ ( ' name ' , ' = ' , c ) , ( ' parent_id ' , ' = ' , parent ) ] )
if not ids :
parent = categ_obj . create ( cr , uid , { ' name ' : c , ' parent_id ' : parent } )
else :
parent = ids [ 0 ]
self . write ( cr , uid , [ module . id ] , {
' author ' : info . get ( ' author ' , False ) ,
' website ' : info . get ( ' website ' , False ) ,
' shortdesc ' : info . get ( ' name ' , False ) ,
' description ' : info . get ( ' description ' , False ) ,
' category_id ' : parent
} )
return True
module ( )
class module_dependency ( osv . osv ) :
_name = " ir.module.module.dependency "
_description = " Module dependency "
_columns = {
' name ' : fields . char ( ' Name ' , size = 128 ) ,
' module_id ' : fields . many2one ( ' ir.module.module ' , ' Module ' , select = True ) ,
#'module_dest_id': fields.many2one('ir.module.module', 'Module'),
' version_pattern ' : fields . char ( ' Required Version ' , size = 128 ) ,
}
# returns the ids of module version records which match all dependencies
# [version_id, ...]
def resolve ( self , cr , uid , ids ) :
vobj = self . pool . get ( ' ir.module.module.version ' )
objs = self . browse ( cr , uid , ids )
res = { }
for o in objs :
pattern = o . version_pattern and eval ( o . version_pattern ) or [ ]
print " pattern " , pattern
res [ o . id ] = vobj . search ( cr , uid , [ ( ' module ' , ' = ' , o . module . id ) ] + pattern )
#TODO: add smart dependencies resolver here
# it should compute the best version for each module
return [ r [ 0 ] for r in res . itervalues ( ) ]
module_dependency ( )