1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 # Copyright (C) 2010-2011 OpenERP s.a. (<http://openerp.com>).
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
24 from os.path import join as opj
30 import openerp.osv as osv
31 import openerp.tools as tools
32 import openerp.tools.osutil as osutil
33 from openerp.tools.safe_eval import safe_eval as eval
34 from openerp.tools.translate import _
36 import openerp.netsvc as netsvc
39 import openerp.release as release
43 from zipfile import PyZipFile, ZIP_DEFLATED
44 from cStringIO import StringIO
48 import openerp.modules.db
49 import openerp.modules.graph
51 _ad = os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons') # default addons path (base)
54 # Modules already loaded
57 logger = netsvc.Logger()
59 def initialize_sys_path():
60 """ Add all addons paths in sys.path.
62 This ensures something like ``import crm`` works even if the addons are
63 not in the PYTHONPATH.
69 ad_paths = map(lambda m: os.path.abspath(tools.ustr(m.strip())), tools.config['addons_path'].split(','))
70 ad_paths.append(_ad) # for get_module_path
73 def get_module_path(module, downloaded=False, display_warning=True):
74 """Return the path of the given module.
76 Search the addons paths and return the first path where the given
77 module is found. If downloaded is True, return the default addons
78 path if nothing else is found.
83 if os.path.exists(opj(adp, module)) or os.path.exists(opj(adp, '%s.zip' % module)):
84 return opj(adp, module)
87 return opj(_ad, module)
89 logger.notifyChannel('init', netsvc.LOG_WARNING, 'module %s: module not found' % (module,))
93 def get_module_filetree(module, dir='.'):
94 path = get_module_path(module)
98 dir = os.path.normpath(dir)
101 if dir.startswith('..') or (dir and dir[0] == '/'):
102 raise Exception('Cannot access file outside the module')
104 if not os.path.isdir(path):
106 zip = zipfile.ZipFile(path + ".zip")
107 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
109 files = osutil.listdir(path, True)
113 if not f.startswith(dir):
117 f = f[len(dir)+int(not dir.endswith('/')):]
118 lst = f.split(os.sep)
121 current = current.setdefault(lst.pop(0), {})
122 current[lst.pop(0)] = None
126 def zip_directory(directory, b64enc=True, src=True):
127 """Compress a directory
129 @param directory: The directory to compress
130 @param base64enc: if True the function will encode the zip file with base64
131 @param src: Integrate the source files
133 @return: a string containing the zip file
136 RE_exclude = re.compile('(?:^\..+\.swp$)|(?:\.py[oc]$)|(?:\.bak$)|(?:\.~.~$)', re.I)
138 def _zippy(archive, path, src=True):
139 path = os.path.abspath(path)
140 base = os.path.basename(path)
141 for f in osutil.listdir(path, True):
142 bf = os.path.basename(f)
143 if not RE_exclude.search(bf) and (src or bf in ('__openerp__.py', '__terp__.py') or not bf.endswith('.py')):
144 archive.write(os.path.join(path, f), os.path.join(base, f))
146 archname = StringIO()
147 archive = PyZipFile(archname, "w", ZIP_DEFLATED)
149 # for Python 2.5, ZipFile.write() still expects 8-bit strings (2.6 converts to utf-8)
150 directory = tools.ustr(directory).encode('utf-8')
152 archive.writepy(directory)
153 _zippy(archive, directory, src=src)
155 archive_data = archname.getvalue()
159 return base64.encodestring(archive_data)
163 def get_module_as_zip(modulename, b64enc=True, src=True):
164 """Generate a module as zip file with the source or not and can do a base64 encoding
166 @param modulename: The module name
167 @param b64enc: if True the function will encode the zip file with base64
168 @param src: Integrate the source files
170 @return: a stream to store in a file-like object
173 ap = get_module_path(str(modulename))
175 raise Exception('Unable to find path for module %s' % modulename)
177 ap = ap.encode('utf8')
178 if os.path.isfile(ap + '.zip'):
179 val = file(ap + '.zip', 'rb').read()
181 val = base64.encodestring(val)
183 val = zip_directory(ap, b64enc, src)
188 def get_module_resource(module, *args):
189 """Return the full path of a resource of the given module.
191 @param module: the module
192 @param args: the resource path components
194 @return: absolute path to the resource
196 TODO name it get_resource_path
197 TODO make it available inside on osv object (self.get_resource_path)
199 a = get_module_path(module)
200 if not a: return False
201 resource_path = opj(a, *args)
202 if zipfile.is_zipfile( a +'.zip') :
203 zip = zipfile.ZipFile( a + ".zip")
204 files = ['/'.join(f.split('/')[1:]) for f in zip.namelist()]
205 resource_path = '/'.join(args)
206 if resource_path in files:
207 return opj(a, resource_path)
208 elif os.path.exists(resource_path):
212 def get_module_icon(module):
213 iconpath = ['static', 'src', 'img', 'icon.png']
214 if get_module_resource(module, *iconpath):
215 return ('/' + module + '/') + '/'.join(iconpath)
216 return '/base/' + '/'.join(iconpath)
218 def load_information_from_description_file(module):
220 :param module: The name of the module (sale, purchase, ...)
223 terp_file = get_module_resource(module, '__openerp__.py')
225 terp_file = get_module_resource(module, '__terp__.py')
226 mod_path = get_module_path(module)
229 if os.path.isfile(terp_file) or zipfile.is_zipfile(mod_path+'.zip'):
230 # default values for descriptor
233 'application': False,
235 'category': 'Uncategorized',
237 'complexity': 'normal',
240 'icon': get_module_icon(module),
250 info.update(itertools.izip(
251 'depends data demo test init_xml update_xml demo_xml'.split(),
254 with tools.file_open(terp_file) as terp_f:
255 info.update(eval(terp_f.read()))
259 #TODO: refactor the logger in this file to follow the logging guidelines
261 logging.getLogger('modules').debug('module %s: no descriptor file'
262 ' found: __openerp__.py or __terp__.py (deprecated)', module)
266 def init_module_models(cr, module_name, obj_list):
267 """ Initialize a list of models.
269 Call _auto_init and init on each model to create or update the
270 database tables supporting the models.
272 TODO better explanation of _auto_init and init.
275 logger.notifyChannel('init', netsvc.LOG_INFO,
276 'module %s: creating or updating database tables' % module_name)
279 result = obj._auto_init(cr, {'module': module_name})
282 if hasattr(obj, 'init'):
286 obj._auto_end(cr, {'module': module_name})
293 def register_module_classes(m):
294 """ Register module named m, if not already registered.
296 This loads the module and register all of its models, thanks to either
297 the MetaModel metaclass, or the explicit instantiation of the model.
302 mt = isinstance(e, zipimport.ZipImportError) and 'zip ' or ''
303 msg = "Couldn't load %smodule %s" % (mt, m)
304 logger.notifyChannel('init', netsvc.LOG_CRITICAL, msg)
305 logger.notifyChannel('init', netsvc.LOG_CRITICAL, e)
310 logger.notifyChannel('init', netsvc.LOG_INFO, 'module %s: registering objects' % m)
311 mod_path = get_module_path(m)
313 initialize_sys_path()
315 zip_mod_path = mod_path + '.zip'
316 if not os.path.isfile(zip_mod_path):
317 __import__('openerp.modules.' + m)
319 zimp = zipimport.zipimporter(zip_mod_path)
329 """Returns the list of module names
333 name = os.path.basename(name)
334 if name[-4:] == '.zip':
338 def is_really_module(name):
339 name = opj(dir, name)
340 return os.path.isdir(name) or zipfile.is_zipfile(name)
341 return map(clean, filter(is_really_module, os.listdir(dir)))
344 initialize_sys_path()
346 plist.extend(listdir(ad))
347 return list(set(plist))
350 def get_modules_with_version():
351 modules = get_modules()
353 for module in modules:
355 info = load_information_from_description_file(module)
356 res[module] = "%s.%s" % (release.major_version, info['version'])
362 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: