summaryrefslogtreecommitdiff
path: root/bin/addons/base/module/module.py
diff options
context:
space:
mode:
Diffstat (limited to 'bin/addons/base/module/module.py')
-rw-r--r--bin/addons/base/module/module.py274
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()
+