base,kernel,base_module_publish: improve publication of module, add repository, etc...
authorced <>
Mon, 30 Jul 2007 13:35:27 +0000 (13:35 +0000)
committerced <>
Mon, 30 Jul 2007 13:35:27 +0000 (13:35 +0000)
- 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

bin/addons/__init__.py
bin/addons/base/__terp__.py
bin/addons/base/module/module.py
bin/addons/base/module/module_data.xml
bin/addons/base/module/module_view.xml
bin/addons/base/module/module_wizard.xml
bin/addons/base/module/wizard/wizard_module_import.py
bin/addons/base/module/wizard/wizard_module_upgrade.py
bin/addons/base/module/wizard/wizard_update_module.py
bin/tools/convert.py
bin/tools/misc.py

index 991a1ad..486994c 100644 (file)
@@ -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:
index 26104c1..f1a6ce0 100644 (file)
@@ -33,5 +33,6 @@
                "res/partner/partner_data.xml",
                "res/ir_property_view.xml",
        ],
-       "installable": True
+       "active": True,
+       "installable": True,
 }
index cdbeeba..1d3974c 100644 (file)
@@ -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()
 
index 930ebee..d1559b9 100644 (file)
@@ -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=&quot;([a-zA-Z0-9_]+)-('+version+'.(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)&quot;'"/>
                </record>
-               -->
        </data>
 </terp>
 
index 2ab909a..fa61a14 100644 (file)
@@ -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"/>
                                                        <field name="dependencies_id" colspan="4" nolabel="1">
                                                                <tree string="Dependencies">
                                                                        <field name="name"/>
-                                                                       <field name="version_pattern"/>
                                                                        <field name="state"/>
                                                                </tree>
                                                        </field>
                        <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">
                        <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">
                        <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','&lt;&gt;','uninstalled'),('state','&lt;&gt;','installed'),('state','&lt;&gt;','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"/>
 
                        <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>
                        <field name="type">tree</field>
                        <field name="arch" type="xml">
                                <tree string="Repository list">
+                                       <field name="sequence"/>
                                        <field name="name"/>
                                        <field name="url"/>
                                </tree>
                        <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>
index 735054f..70db979 100644 (file)
@@ -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"
index 0731de4..910f920 100644 (file)
@@ -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')
index 5a52ae2..2fa4186 100644 (file)
@@ -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 {}
 
index 9968155..0cab280 100644 (file)
@@ -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': [],
index 9eb902b..52e49e5 100644 (file)
@@ -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
index 0eaa036..cf9baa8 100644 (file)
@@ -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)