base,kernel,base_module_publish: improve publication of module, add repository, etc...
- add repository for module - improve version number - add check to remove module - can not upload module with the same version number - add version compare from portage (gentoo) - add release in context of eval - add active = True to base module - add wizard to update all installed modules - add check to not upload module that is not installed - add posibility to include source - fix license - now load zip module in preference bzr revid: ced-e1b031d238126e8060b0e2cb6f87bb93cca5a343
This commit is contained in:
@@ -152,6 +152,9 @@ def create_graph(module_list, force=None):
|
||||
|
||||
# if all dependencies of 'package' are already in the graph, add 'package' in the graph
|
||||
if reduce(lambda x,y: x and y in graph, deps, True):
|
||||
if not package in current:
|
||||
packages.pop(0)
|
||||
continue
|
||||
later.clear()
|
||||
current.remove(package)
|
||||
graph.addNode(package, deps)
|
||||
@@ -163,7 +166,7 @@ def create_graph(module_list, force=None):
|
||||
else:
|
||||
later.add(package)
|
||||
packages.append((package, deps, datas))
|
||||
packages = packages[1:]
|
||||
packages.pop(0)
|
||||
|
||||
for package in later:
|
||||
logger.notifyChannel('init', netsvc.LOG_ERROR, 'addon:%s:Unmet dependency' % package)
|
||||
@@ -241,10 +244,11 @@ def register_classes():
|
||||
m = package.name
|
||||
logger.notifyChannel('init', netsvc.LOG_INFO, 'addon:%s:registering classes' % m)
|
||||
sys.stdout.flush()
|
||||
try:
|
||||
|
||||
if not os.path.isfile(opj(ad, m+'.zip')):
|
||||
# XXX must restrict to only addons paths
|
||||
imp.load_module(m, *imp.find_module(m))
|
||||
except ImportError:
|
||||
else:
|
||||
import zipimport
|
||||
mod_path = opj(ad, m+'.zip')
|
||||
try:
|
||||
|
||||
@@ -33,5 +33,6 @@
|
||||
"res/partner/partner_data.xml",
|
||||
"res/ir_property_view.xml",
|
||||
],
|
||||
"installable": True
|
||||
"active": True,
|
||||
"installable": True,
|
||||
}
|
||||
|
||||
@@ -32,6 +32,114 @@ import os
|
||||
import tools
|
||||
from osv import fields, osv, orm
|
||||
import zipfile
|
||||
import release
|
||||
|
||||
ver_regexp = re.compile("^(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
|
||||
suffix_regexp = re.compile("^(alpha|beta|rc|pre|p)(\\d*)$")
|
||||
|
||||
def vercmp(ver1, ver2):
|
||||
"""
|
||||
Compare two versions
|
||||
Take from portage_versions.py
|
||||
@param ver1: version to compare with
|
||||
@type ver1: string (example "1.2-r3")
|
||||
@param ver2: version to compare again
|
||||
@type ver2: string (example "2.1-r1")
|
||||
@rtype: None or float
|
||||
@return:
|
||||
1. position if ver1 is greater than ver2
|
||||
2. negative if ver1 is less than ver2
|
||||
3. 0 if ver1 equals ver2
|
||||
4. None if ver1 or ver2 are invalid
|
||||
"""
|
||||
|
||||
match1 = ver_regexp.match(ver1)
|
||||
match2 = ver_regexp.match(ver2)
|
||||
|
||||
if not match1 or not match1.groups():
|
||||
return None
|
||||
if not match2 or not match2.groups():
|
||||
return None
|
||||
|
||||
list1 = [int(match1.group(1))]
|
||||
list2 = [int(match2.group(1))]
|
||||
|
||||
if len(match1.group(2)) or len(match2.group(2)):
|
||||
vlist1 = match1.group(2)[1:].split(".")
|
||||
vlist2 = match2.group(2)[1:].split(".")
|
||||
for i in range(0, max(len(vlist1), len(vlist2))):
|
||||
# Implicit .0 is given -1, so 1.0.0 > 1.0
|
||||
# would be ambiguous if two versions that aren't literally equal
|
||||
# are given the same value (in sorting, for example).
|
||||
if len(vlist1) <= i or len(vlist1[i]) == 0:
|
||||
list1.append(-1)
|
||||
list2.append(int(vlist2[i]))
|
||||
elif len(vlist2) <= i or len(vlist2[i]) == 0:
|
||||
list1.append(int(vlist1[i]))
|
||||
list2.append(-1)
|
||||
# Let's make life easy and use integers unless we're forced to use floats
|
||||
elif (vlist1[i][0] != "0" and vlist2[i][0] != "0"):
|
||||
list1.append(int(vlist1[i]))
|
||||
list2.append(int(vlist2[i]))
|
||||
# now we have to use floats so 1.02 compares correctly against 1.1
|
||||
else:
|
||||
list1.append(float("0."+vlist1[i]))
|
||||
list2.append(float("0."+vlist2[i]))
|
||||
# and now the final letter
|
||||
if len(match1.group(4)):
|
||||
list1.append(ord(match1.group(4)))
|
||||
if len(match2.group(4)):
|
||||
list2.append(ord(match2.group(4)))
|
||||
|
||||
for i in range(0, max(len(list1), len(list2))):
|
||||
if len(list1) <= i:
|
||||
return -1
|
||||
elif len(list2) <= i:
|
||||
return 1
|
||||
elif list1[i] != list2[i]:
|
||||
return list1[i] - list2[i]
|
||||
|
||||
# main version is equal, so now compare the _suffix part
|
||||
list1 = match1.group(5).split("_")[1:]
|
||||
list2 = match2.group(5).split("_")[1:]
|
||||
|
||||
for i in range(0, max(len(list1), len(list2))):
|
||||
# Implicit _p0 is given a value of -1, so that 1 < 1_p0
|
||||
if len(list1) <= i:
|
||||
s1 = ("p","-1")
|
||||
else:
|
||||
s1 = suffix_regexp.match(list1[i]).groups()
|
||||
if len(list2) <= i:
|
||||
s2 = ("p","-1")
|
||||
else:
|
||||
s2 = suffix_regexp.match(list2[i]).groups()
|
||||
if s1[0] != s2[0]:
|
||||
return suffix_value[s1[0]] - suffix_value[s2[0]]
|
||||
if s1[1] != s2[1]:
|
||||
# it's possible that the s(1|2)[1] == ''
|
||||
# in such a case, fudge it.
|
||||
try:
|
||||
r1 = int(s1[1])
|
||||
except ValueError:
|
||||
r1 = 0
|
||||
try:
|
||||
r2 = int(s2[1])
|
||||
except ValueError:
|
||||
r2 = 0
|
||||
if r1 - r2:
|
||||
return r1 - r2
|
||||
|
||||
# the suffix part is equal to, so finally check the revision
|
||||
if match1.group(9):
|
||||
r1 = int(match1.group(9))
|
||||
else:
|
||||
r1 = 0
|
||||
if match2.group(9):
|
||||
r2 = int(match2.group(9))
|
||||
else:
|
||||
r2 = 0
|
||||
return r1 - r2
|
||||
|
||||
|
||||
class module_repository(osv.osv):
|
||||
_name = "ir.module.repository"
|
||||
@@ -39,7 +147,18 @@ class module_repository(osv.osv):
|
||||
_columns = {
|
||||
'name': fields.char('Name', size=128),
|
||||
'url': fields.char('Url', size=256, required=True),
|
||||
'sequence': fields.integer('Sequence', required=True),
|
||||
'filter': fields.char('Filter', size=128, required=True,
|
||||
help='Regexp to search module on the repository webpage:\n'
|
||||
'- The first parenthesis must match the name of the module.\n'
|
||||
'- The second parenthesis must match all the version number.\n'
|
||||
'- The last parenthesis must match the extension of the module.'),
|
||||
}
|
||||
_defaults = {
|
||||
'sequence': lambda *a: 5,
|
||||
'filter': lambda *a: 'href="([a-zA-Z0-9_]+)-('+release.version.rsplit('.', 1)[0]+'.(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)"',
|
||||
}
|
||||
_order = "sequence"
|
||||
module_repository()
|
||||
|
||||
class module_category(osv.osv):
|
||||
@@ -73,6 +192,8 @@ class module(osv.osv):
|
||||
f = tools.file_open(os.path.join(tools.config['addons_path'], name, '__terp__.py'))
|
||||
data = f.read()
|
||||
info = eval(data)
|
||||
if name == 'version':
|
||||
info = release.version.rsplit('.', 1)[0] + '.' + info
|
||||
f.close()
|
||||
except:
|
||||
return {}
|
||||
@@ -82,7 +203,8 @@ class module(osv.osv):
|
||||
res = {}
|
||||
for m in self.browse(cr, uid, ids):
|
||||
if m.state in ('installed', 'to upgrade', 'to remove'):
|
||||
res[m.id] = self.get_module_info(m.name).get('version', False)
|
||||
res[m.id] = release.version.rsplit('.', 1)[0] + '.' + \
|
||||
self.get_module_info(m.name).get('version', False)
|
||||
else:
|
||||
res[m.id] = ''
|
||||
return res
|
||||
@@ -94,10 +216,12 @@ class module(osv.osv):
|
||||
'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'),
|
||||
'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'),
|
||||
'url': fields.char('URL', size=128),
|
||||
'dependencies_id': fields.one2many('ir.module.module.dependency',
|
||||
'module_id', 'Dependencies', readonly=True),
|
||||
'state': fields.selection([
|
||||
('uninstallable','Uninstallable'),
|
||||
('uninstalled','Not Installed'),
|
||||
@@ -123,6 +247,17 @@ class module(osv.osv):
|
||||
('name_uniq', 'unique (name)', 'The name of the module must be unique !')
|
||||
]
|
||||
|
||||
def unlink(self, cr, uid, ids, context=None):
|
||||
if not ids:
|
||||
return True
|
||||
if isinstance(ids, (int, long)):
|
||||
ids = [ids]
|
||||
for mod in self.read(cr, uid, ids, ['state'], context):
|
||||
if mod['state'] in ('installed', 'to upgrade', 'to remove', 'to install'):
|
||||
raise orm.except_orm('Error',
|
||||
'You try to remove a module that is installed or will be installed')
|
||||
return super(module, self).unlink(cr, uid, ids, context=context)
|
||||
|
||||
def state_change(self, cr, uid, ids, newstate, context={}, level=50):
|
||||
if level<1:
|
||||
raise 'Recursion error in modules dependencies !'
|
||||
@@ -131,14 +266,31 @@ class module(osv.osv):
|
||||
mdemo = True
|
||||
for dep in module.dependencies_id:
|
||||
ids2 = self.search(cr, uid, [('name','=',dep.name)])
|
||||
mdemo = self.state_change(cr, uid, ids2, newstate, context, level-1) and mdemo
|
||||
mdemo = self.state_change(cr, uid, ids2, newstate, context, level-1,)\
|
||||
and mdemo
|
||||
if not module.dependencies_id:
|
||||
mdemo = module.demo
|
||||
if module.state=='uninstalled':
|
||||
if module.state == 'uninstalled':
|
||||
self.write(cr, uid, [module.id], {'state': newstate, 'demo':mdemo})
|
||||
demo = demo and mdemo
|
||||
return demo
|
||||
|
||||
def state_upgrade(self, cr, uid, ids, newstate, context=None, level=50):
|
||||
dep_obj = self.pool.get('ir.module.module.dependency')
|
||||
if level<1:
|
||||
raise 'Recursion error in modules dependencies !'
|
||||
for module in self.browse(cr, uid, ids):
|
||||
dep_ids = dep_obj.search(cr, uid, [('name', '=', module.name)])
|
||||
if dep_ids:
|
||||
ids2 = []
|
||||
for dep in dep_obj.browse(cr, uid, dep_ids):
|
||||
if dep.module_id.state != 'to upgrade':
|
||||
ids2.append(dep.module_id.id)
|
||||
self.state_upgrade(cr, uid, ids2, newstate, context, level)
|
||||
if module.state == 'installed':
|
||||
self.write(cr, uid, module.id, {'state': newstate})
|
||||
return True
|
||||
|
||||
def button_install(self, cr, uid, ids, context={}):
|
||||
return self.state_change(cr, uid, ids, 'to install', context)
|
||||
|
||||
@@ -165,9 +317,8 @@ class module(osv.osv):
|
||||
def button_uninstall_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(self, cr, uid, ids, context=None):
|
||||
return self.state_upgrade(cr, uid, ids, 'to upgrade', context)
|
||||
def button_upgrade_cancel(self, cr, uid, ids, context={}):
|
||||
self.write(cr, uid, ids, {'state': 'installed'})
|
||||
return True
|
||||
@@ -190,6 +341,7 @@ class module(osv.osv):
|
||||
def update_list(self, cr, uid, context={}):
|
||||
robj = self.pool.get('ir.module.repository')
|
||||
adp = tools.config['addons_path']
|
||||
res = [0, 0] # [update, add]
|
||||
|
||||
# iterate through installed modules and mark them as being so
|
||||
for name in os.listdir(adp):
|
||||
@@ -198,20 +350,28 @@ class module(osv.osv):
|
||||
mod_name=name[:-4]
|
||||
ids = self.search(cr, uid, [('name','=',mod_name)])
|
||||
if ids:
|
||||
id = ids[0]
|
||||
mod = self.browse(cr, uid, id)
|
||||
terp = self.get_module_info(mod_name)
|
||||
if terp.get('installable', True) and self.read(cr, uid, ids, ['state'])[0]['state'] == 'uninstallable':
|
||||
self.write(cr, uid, ids, {'state': 'uninstalled'})
|
||||
self.write(cr, uid, ids, {
|
||||
if terp.get('installable', True) and mod.state == 'uninstallable':
|
||||
self.write(cr, uid, id, {'state': 'uninstalled'})
|
||||
if vercmp(terp.get('version', ''), mod.latest_version) > 0:
|
||||
self.write(cr, uid, id, {
|
||||
'latest_version': terp.get('version')})
|
||||
res[0] += 1
|
||||
self.write(cr, uid, id, {
|
||||
'description': terp.get('description', ''),
|
||||
'shortdesc': terp.get('name', ''),
|
||||
'author': terp.get('author', 'Unknown'),
|
||||
'website': terp.get('website', ''),
|
||||
'latest_version': terp.get('version', ''),
|
||||
'license': terp.get('license', 'GPL-2'),
|
||||
})
|
||||
cr.execute('DELETE FROM ir_module_module_dependency where module_id = %d', (ids[0],))
|
||||
self._update_dependencies(cr, uid, ids[0], terp.get('depends', []))
|
||||
self._update_category(cr, uid, ids[0], terp.get('category', 'Uncategorized'))
|
||||
cr.execute('DELETE FROM ir_module_module_dependency\
|
||||
WHERE module_id = %d', (id,))
|
||||
self._update_dependencies(cr, uid, ids[0], terp.get('depends',
|
||||
[]))
|
||||
self._update_category(cr, uid, ids[0], terp.get('category',
|
||||
'Uncategorized'))
|
||||
continue
|
||||
terp_file = os.path.join(adp, name, '__terp__.py')
|
||||
mod_path = os.path.join(adp, name)
|
||||
@@ -219,16 +379,15 @@ class module(osv.osv):
|
||||
terp = self.get_module_info(mod_name)
|
||||
if not terp or not terp.get('installable', True):
|
||||
continue
|
||||
try:
|
||||
if not os.path.isfile(os.path.join(adp, mod_name+'.zip')):
|
||||
import imp
|
||||
# XXX must restrict to only addons paths
|
||||
imp.load_module(name, *imp.find_module(name))
|
||||
except ImportError:
|
||||
imp.load_module(name, *imp.find_module(mod_name))
|
||||
else:
|
||||
import zipimport
|
||||
mod_path = os.path.join(adp, name)
|
||||
mod_path = os.path.join(adp, mod_name+'.zip')
|
||||
zimp = zipimport.zipimporter(mod_path)
|
||||
zimp.load_module(mod_name)
|
||||
version = terp.get('version', False)
|
||||
id = self.create(cr, uid, {
|
||||
'name': mod_name,
|
||||
'state': 'uninstalled',
|
||||
@@ -239,16 +398,27 @@ class module(osv.osv):
|
||||
'latest_version': terp.get('version', ''),
|
||||
'license': terp.get('license', 'GPL-2'),
|
||||
})
|
||||
res[1] += 1
|
||||
self._update_dependencies(cr, uid, id, terp.get('depends', []))
|
||||
self._update_category(cr, uid, id, terp.get('category', 'Uncategorized'))
|
||||
|
||||
# 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")
|
||||
modules = re.findall(repository.filter, index_page, re.I+re.M)
|
||||
mod_sort = {}
|
||||
for m in modules:
|
||||
name = m[0]
|
||||
version = m[1]
|
||||
extension = m[-1]
|
||||
if version == 'x': # 'x' version was a mistake
|
||||
version = '0'
|
||||
if name in mod_sort:
|
||||
if vercmp(version, mod_sort[name][0]) <= 0:
|
||||
continue
|
||||
mod_sort[name] = [version, extension]
|
||||
for name in mod_sort.keys():
|
||||
version, extension = mod_sort[name]
|
||||
url = repository.url+'/'+name+'-'+version+extension
|
||||
ids = self.search(cr, uid, [('name','=',name)])
|
||||
if not ids:
|
||||
self.create(cr, uid, {
|
||||
@@ -257,11 +427,42 @@ class module(osv.osv):
|
||||
'url': url,
|
||||
'state': 'uninstalled',
|
||||
})
|
||||
res[1] += 1
|
||||
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
|
||||
id = ids[0]
|
||||
latest_version = self.read(cr, uid, id, ['latest_version'])\
|
||||
['latest_version']
|
||||
if latest_version == 'x': # 'x' version was a mistake
|
||||
latest_version = '0'
|
||||
c = vercmp(version, latest_version)
|
||||
if c > 0:
|
||||
self.write(cr, uid, id,
|
||||
{'latest_version': version, 'url': url})
|
||||
res[0] += 1
|
||||
elif c == 0:
|
||||
self.write(cr, uid, id, {'url': url})
|
||||
return res
|
||||
|
||||
def download(self, cr, uid, ids, context=None):
|
||||
adp = tools.config['addons_path']
|
||||
for mod in self.browse(cr, uid, ids, context=context):
|
||||
if not mod.url:
|
||||
continue
|
||||
match = re.search('-([a-zA-Z0-9\._-]+)(\.zip)', mod.url, re.I)
|
||||
version = '0'
|
||||
if match:
|
||||
version = match.group(1)
|
||||
if vercmp(mod.installed_version or '0', version) >= 0:
|
||||
continue
|
||||
zipfile = urllib.urlopen(mod.url).read()
|
||||
fname = os.path.join(adp, mod.name+'.zip')
|
||||
try:
|
||||
fp = file(fname, 'wb')
|
||||
fp.write(zipfile)
|
||||
fp.close()
|
||||
except IOError, e:
|
||||
raise orm.except_orm('Error', 'Can not create the module file:\n %s'
|
||||
% (fname,))
|
||||
|
||||
def _update_dependencies(self, cr, uid, id, depends=[]):
|
||||
for d in depends:
|
||||
@@ -305,8 +506,6 @@ class module_dependency(osv.osv):
|
||||
_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),
|
||||
'state': fields.function(_state, method=True, type='selection', selection=[
|
||||
('uninstallable','Uninstallable'),
|
||||
('uninstalled','Not Installed'),
|
||||
@@ -317,17 +516,5 @@ class module_dependency(osv.osv):
|
||||
('unknown', 'Unknown'),
|
||||
], string='State', readonly=True),
|
||||
}
|
||||
# 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 []
|
||||
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()
|
||||
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
<?xml version="1.0"?>
|
||||
<terp>
|
||||
<data>
|
||||
<!--
|
||||
<record model="ir.module.repository" id="module_repository_tiny">
|
||||
<field name="name">Tiny ERP Base Repository</field>
|
||||
<field name="url">http://tinyerp.org/download/modules/</field>
|
||||
<field name="name">Tiny ERP</field>
|
||||
<field name="url">http://www.tinyerp.org/download/modules/</field>
|
||||
<field name="filter" eval="'href="([a-zA-Z0-9_]+)-('+version+'.(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)"'"/>
|
||||
</record>
|
||||
-->
|
||||
</data>
|
||||
</terp>
|
||||
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
<group col="6" colspan="2">
|
||||
<button string="Install" name="button_install" type="object" states="uninstalled"/>
|
||||
<button string="Cancel Install" name="button_install_cancel" type="object" states="to install"/>
|
||||
<button string="Uninstall" name="button_uninstall" type="object" states="installed"/>
|
||||
<button string="Uninstall (beta)" name="button_uninstall" type="object" states="installed"/>
|
||||
<button string="Cancel Uninstall" name="button_uninstall_cancel" type="object" states="to remove"/>
|
||||
<button string="Upgrade" name="button_upgrade" type="object" states="installed"/>
|
||||
<button string="Cancel Upgrade" name="button_upgrade_cancel" type="object" states="to upgrade"/>
|
||||
@@ -102,7 +102,6 @@
|
||||
<field name="dependencies_id" colspan="4" nolabel="1">
|
||||
<tree string="Dependencies">
|
||||
<field name="name"/>
|
||||
<field name="version_pattern"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
@@ -140,7 +139,7 @@
|
||||
<field name="res_model">ir.module.module</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state','=','installed')]</field>
|
||||
<field name="domain">[('state', 'in', ['installed', 'to upgrade', 'to remove'])]</field>
|
||||
</record>
|
||||
<menuitem name="Administration/Modules Management/Modules/Installed Modules" action="open_module_tree_install" id="menu_module_tree_install"/>
|
||||
<record model="ir.actions.act_window" id="open_module_tree_uninstall">
|
||||
@@ -148,7 +147,7 @@
|
||||
<field name="res_model">ir.module.module</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state','=','uninstalled')]</field>
|
||||
<field name="domain">[('state', 'in', ['uninstalled', 'uninstallable'])]</field>
|
||||
</record>
|
||||
<menuitem name="Administration/Modules Management/Modules/Uninstalled Modules" action="open_module_tree_uninstall" id="menu_module_tree_uninstall"/>
|
||||
<record model="ir.actions.act_window" id="open_module_tree_upgrade">
|
||||
@@ -156,7 +155,7 @@
|
||||
<field name="res_model">ir.module.module</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">[('state','<>','uninstalled'),('state','<>','installed'),('state','<>','uninstallable'),]</field>
|
||||
<field name="domain">[('state','in', ['to upgrade', 'to remove', 'to install'])]</field>
|
||||
</record>
|
||||
<menuitem name="Administration/Modules Management/Modules/Modules to be installed, upgraded or removed" action="open_module_tree_upgrade" id="menu_module_tree_upgrade"/>
|
||||
|
||||
@@ -166,8 +165,10 @@
|
||||
<field name="type">form</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Repository">
|
||||
<field name="name" colspan="4" select="1"/>
|
||||
<field name="url" colspan="4" select="1"/>
|
||||
<field name="name" select="1"/>
|
||||
<field name="sequence"/>
|
||||
<field name="url" colspan="4" select="1" widget="url"/>
|
||||
<field name="filter" colspan="4"/>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
@@ -177,6 +178,7 @@
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Repository list">
|
||||
<field name="sequence"/>
|
||||
<field name="name"/>
|
||||
<field name="url"/>
|
||||
</tree>
|
||||
@@ -187,13 +189,11 @@
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<!--
|
||||
<menuitem
|
||||
name="Administration/Modules Management/Repositories"
|
||||
action="open_repository_tree"
|
||||
id="menu_module_repository_tree"
|
||||
/>
|
||||
-->
|
||||
|
||||
</data>
|
||||
</terp>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<?xml version="1.0"?>
|
||||
<terp>
|
||||
<data>
|
||||
<wizard
|
||||
string="Import module"
|
||||
model="ir.module.module"
|
||||
name="base.module.import"
|
||||
id="wizard_base_module_import"/>
|
||||
<record model="ir.actions.wizard" id="wizard_base_module_import">
|
||||
<field name="name">Import module</field>
|
||||
<field name="wiz_name">base.module.import</field>
|
||||
</record>
|
||||
<menuitem
|
||||
name="Administration/Modules Management/Import a new module"
|
||||
action="wizard_base_module_import"
|
||||
|
||||
@@ -67,9 +67,6 @@ class move_module_wizard(wizard.interface):
|
||||
|
||||
ad = tools.config['addons_path']
|
||||
|
||||
if os.path.isdir(os.path.join(ad,module_name)) or os.path.isfile(os.path.join(ad,module_name+'.zip')):
|
||||
raise wizard.except_wizard('Error !', 'This module already exist in your system !')
|
||||
|
||||
fname = os.path.join(ad,module_name+'.zip')
|
||||
try:
|
||||
fp = file(fname, 'wb')
|
||||
@@ -78,7 +75,19 @@ class move_module_wizard(wizard.interface):
|
||||
except IOError, e:
|
||||
raise wizard.except_wizard('Error !', 'Can not create the module file:\n'+' '+fname+'!')
|
||||
|
||||
return {}
|
||||
pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid)
|
||||
return {'module_name': module_name}
|
||||
|
||||
def _action_module_open(self, cr, uid, data, context):
|
||||
return {
|
||||
'domain': str([('name', '=', data['form']['module_name'])]),
|
||||
'name': 'Module List',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'res_model': 'ir.module.module',
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window'
|
||||
}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
@@ -96,8 +105,12 @@ class move_module_wizard(wizard.interface):
|
||||
'type':'form',
|
||||
'arch':finish_form,
|
||||
'fields':{},
|
||||
'state':[('end','Close')]
|
||||
'state':[('open_window','Close')]
|
||||
}
|
||||
}
|
||||
},
|
||||
'open_window': {
|
||||
'actions': [],
|
||||
'result': {'type': 'action', 'action': _action_module_open, 'state':'end'}
|
||||
},
|
||||
}
|
||||
move_module_wizard('base.module.import')
|
||||
|
||||
@@ -56,14 +56,18 @@ view_field = {
|
||||
class wizard_info_get(wizard.interface):
|
||||
def _get_install(self, cr, uid, data, context):
|
||||
pool=pooler.get_pool(cr.dbname)
|
||||
ids = pool.get('ir.module.module').search(cr, uid, [
|
||||
('state','<>','uninstalled'),
|
||||
('state','<>','installed'),
|
||||
('state','<>','uninstallable')])
|
||||
mod_obj = pool.get('ir.module.module')
|
||||
ids = mod_obj.search(cr, uid, [
|
||||
('state', 'in', ['to upgrade', 'to remove', 'to install'])])
|
||||
res = pool.get('ir.module.module').read(cr, uid, ids, ['name','state'], context)
|
||||
return {'module_info':'\n'.join(map(lambda x: x['name']+' : '+x['state'], res))}
|
||||
|
||||
def _upgrade_module(self, cr, uid, data, context):
|
||||
pool=pooler.get_pool(cr.dbname)
|
||||
mod_obj = pool.get('ir.module.module')
|
||||
ids = mod_obj.search(cr, uid, [
|
||||
('state', 'in', ['to upgrade', 'to remove', 'to install'])])
|
||||
mod_obj.download(cr, uid, ids, context=context)
|
||||
(db, pool)=pooler.restart_pool(cr.dbname, update_module=True)
|
||||
return {}
|
||||
|
||||
|
||||
@@ -34,35 +34,58 @@ import pooler
|
||||
class wizard_update_module(wizard.interface):
|
||||
|
||||
arch = '''<?xml version="1.0"?>
|
||||
<form string="Scan for new modules">
|
||||
<label string="This function will check for new modules in the 'addons' path and on module repository." colspan="4" />
|
||||
</form>
|
||||
'''
|
||||
fields = {}
|
||||
<form string="Scan for new modules">
|
||||
<label string="This function will check for new modules in the 'addons' path and on module repositories:" colspan="4" align="0.0"/>
|
||||
<field name="repositories" colspan="4" nolabel="1"/>
|
||||
</form>'''
|
||||
fields = {
|
||||
'repositories': {'type': 'text', 'string': 'Repositories', 'readonly': True},
|
||||
}
|
||||
|
||||
arch_module = '''<?xml version="1.0"?>
|
||||
<form string="New modules">
|
||||
<field name="update" colspan="4"/>
|
||||
<field name="add" colspan="4"/>
|
||||
</form>'''
|
||||
|
||||
fields_module = {
|
||||
'update': {'type': 'integer', 'string': 'Number of modules updated', 'readonly': True},
|
||||
'add': {'type': 'integer', 'string': 'Number of modules added', 'readonly': True},
|
||||
}
|
||||
|
||||
def _update_module(self, cr, uid, data, context):
|
||||
pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid)
|
||||
return {}
|
||||
update, add = pooler.get_pool(cr.dbname).get('ir.module.module').update_list(cr, uid)
|
||||
return {'update': update, 'add': add}
|
||||
|
||||
def _action_module_open(self, cr, uid, data, context):
|
||||
return {
|
||||
'domain': "[]",
|
||||
'name': 'Open Module List',
|
||||
'view_type': 'tree',
|
||||
'res_model': 'ir.module.category',
|
||||
'domain': "[('parent_id', '=', False)]",
|
||||
'domain': str([]),
|
||||
'name': 'Module List',
|
||||
'view_type': 'form',
|
||||
'view_mode': 'tree,form',
|
||||
'res_model': 'ir.module.module',
|
||||
'view_id': False,
|
||||
'type': 'ir.actions.act_window'
|
||||
}
|
||||
|
||||
def _get_repositories(self, cr, uid, data, context):
|
||||
pool = pooler.get_pool(cr.dbname)
|
||||
repository_obj = pool.get('ir.module.repository')
|
||||
ids = repository_obj.search(cr, uid, [])
|
||||
res = repository_obj.read(cr, uid, ids, ['name', 'url'], context)
|
||||
return {'repositories': '\n'.join(map(lambda x: x['name']+': '+x['url'], res))}
|
||||
|
||||
states = {
|
||||
'init': {
|
||||
'actions': [],
|
||||
'result': {'type': 'form', 'arch': arch, 'fields': fields, 'state': [('end', 'Cancel', 'gtk-cancel'), ('update', 'Check new modules', 'gtk-ok')]}
|
||||
'actions': [_get_repositories],
|
||||
'result': {'type': 'form', 'arch': arch, 'fields': fields, 'state':
|
||||
[('end', 'Cancel', 'gtk-cancel'),
|
||||
('update', 'Check new modules', 'gtk-ok')]}
|
||||
},
|
||||
'update': {
|
||||
'actions': [_update_module],
|
||||
'result': {'type':'state', 'state':'open_window'}
|
||||
'actions': [_update_module],
|
||||
'result': {'type': 'form', 'arch': arch_module, 'fields': fields_module,
|
||||
'state': [('open_window', 'Ok', 'gtk-ok')]}
|
||||
},
|
||||
'open_window': {
|
||||
'actions': [],
|
||||
|
||||
@@ -55,6 +55,8 @@ def _eval_xml(self,node, pool, cr, uid, idref):
|
||||
if len(a_eval):
|
||||
import time
|
||||
idref['time'] = time
|
||||
import release
|
||||
idref['version'] = release.version.rsplit('.', 1)[0]
|
||||
idref['ref'] = lambda x: self.id_get(cr, False, x)
|
||||
try:
|
||||
import pytz
|
||||
|
||||
+10
-8
@@ -41,6 +41,7 @@ from config import config
|
||||
#import tools
|
||||
|
||||
import zipfile
|
||||
import release
|
||||
|
||||
if sys.version_info[:2] < (2, 4):
|
||||
from threadinglocal import local
|
||||
@@ -100,7 +101,7 @@ def init_db(cr):
|
||||
cr.execute('insert into ir_module_module (id, author, latest_version, website, name, shortdesc, description, category_id, state) values (%d, %s, %s, %s, %s, %s, %s, %d, %s)', (
|
||||
id,
|
||||
info.get('author', ''),
|
||||
info.get('version', ''),
|
||||
release.version.rsplit('.', 1)[0] + '.' + info.get('version', ''),
|
||||
info.get('website', ''),
|
||||
i,
|
||||
info.get('name', False),
|
||||
@@ -165,24 +166,25 @@ def file_open(name, mode="r", subdir='addons'):
|
||||
name = os.path.join(config['root_path'], subdir, name)
|
||||
else:
|
||||
name = os.path.join(config['root_path'], name)
|
||||
if os.path.isfile(name):
|
||||
return file(name, mode)
|
||||
|
||||
# Check for a zipfile in the path
|
||||
head = name
|
||||
name = False
|
||||
zipname = False
|
||||
while True:
|
||||
head, tail = os.path.split(head)
|
||||
if not tail:
|
||||
break
|
||||
if name:
|
||||
name = os.path.join(tail, name)
|
||||
if zipname:
|
||||
zipname = os.path.join(tail, zipname)
|
||||
else:
|
||||
name = tail
|
||||
zipname = tail
|
||||
if zipfile.is_zipfile(head+'.zip'):
|
||||
import StringIO
|
||||
zfile = zipfile.ZipFile(head+'.zip')
|
||||
return StringIO.StringIO(zfile.read(os.path.join(os.path.basename(head), name)))
|
||||
return StringIO.StringIO(zfile.read(os.path.join(os.path.basename(head), zipname)))
|
||||
if os.path.isfile(name):
|
||||
return file(name, mode)
|
||||
|
||||
raise IOError, 'File not found : '+str(name)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user