diff options
Diffstat (limited to 'bin/addons/base/module/module.py')
| -rw-r--r-- | bin/addons/base/module/module.py | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/bin/addons/base/module/module.py b/bin/addons/base/module/module.py new file mode 100644 index 00000000000..bc9aefc0a32 --- /dev/null +++ b/bin/addons/base/module/module.py @@ -0,0 +1,274 @@ +############################################################################## +# +# 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 !' + ids_dest = [] + for module in self.browse(cr, uid, ids): + for dep in module.dependencies_id: + ids2 = self.search(cr, uid, [('name','=',dep.name)]) + for id in ids2: + if (id not in ids_dest): + ids_dest.append(id) + if module.state=='uninstalled': + self.write(cr, uid, [module.id], {'state': newstate}) + if ids_dest: + self.state_change(cr, uid, ids_dest, newstate, context, level-1) + + for module in self.browse(cr, uid, ids): + if module.state!='installed': + demo=True + for dep in module.dependencies_id: + ids2 = self.search(cr, uid, [('name', '=', dep.name)]) + for module2 in self.browse(cr, uid, ids2): + demo = demo and module2.demo + self.write(cr, uid, [module.id], {'demo':demo}) + return True + + 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={}): + self.write(cr, uid, ids, {'state': 'uninstalled'}) + 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() + |
