merging from upper branch

This commit is contained in:
Bertrand Chenal committed 2007-12-29 01:38:57 +01:00
commit b6aeba1508
18 files changed
+1302 -245

No files matched your search

+1
View File
@@ -9,3 +9,4 @@ from translation import *
from exports import *
from rule import *
from property import *
import module
+3
View File
@@ -16,5 +16,8 @@
'exports.xml',
'rule.xml',
'property.xml',
'module/module.xml',
'module/repository.xml',
'module/category.xml',
],
}
+1 -2
View File
@@ -42,8 +42,7 @@ class ActionKeyword(OSV):
('form_action', 'Action form'),
('form_relate', 'Form relate'),
], string='Keyword', required=True),
'model': fields.Reference('Model', selection=_models_get, size=128,
required=True),
'model': fields.Reference('Model', selection=_models_get, size=128),
'action': fields.many2one('ir.actions.actions', 'Action',
ondelete='CASCADE'),
}
+3
View File
@@ -0,0 +1,3 @@
from category import *
from repository import *
from module import *
+18
View File
@@ -0,0 +1,18 @@
"Category"
from trytond.osv import fields, OSV
class Category(OSV):
"Module Category"
_name = "ir.module.category"
_description = __doc__
_columns = {
'name': fields.Char("Name", size=128, required=True),
'parent_id': fields.Many2One('ir.module.category',
'Parent Category', select=1),
'child_ids': fields.One2Many('ir.module.category',
'parent_id', 'Parent Category'),
}
_order = 'name'
Category()
+43
View File
@@ -0,0 +1,43 @@
<?xml version="1.0"?>
<terp>
<data>
<record model="ir.ui.view" id="category_view_form">
<field name="name">ir.module.category.form</field>
<field name="model">ir.module.category</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Module Category">
<field name="name" colspan="4"/>
<field name="parent_id" colspan="4"/>
</form>
</field>
</record>
<record model="ir.ui.view" id="category_view_tree">
<field name="name">ir.module.category.tree</field>
<field name="model">ir.module.category</field>
<field name="type">tree</field>
<field name="field_parent">child_ids</field>
<field name="arch" type="xml">
<tree string="Module Categories">
<field name="name"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="act_category_tree">
<field name="name">Categories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.category</field>
<field name="view_type">tree</field>
<field name="domain">[('parent_id', '=', False)]</field>
</record>
<record model="ir.actions.act_window.view"
id="act_category_tree_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="category_view_tree"/>
<field name="act_window_id" ref="act_category_tree"/>
</record>
<menuitem name="Administration/IR/Modules/"
action="act_category_tree" id="menu_category_tree"/>
</data>
</terp>
+625
View File
@@ -0,0 +1,625 @@
"Module"
import os
import re
import zipfile
import urllib
from trytond.osv import fields, OSV
import trytond.tools as tools
from trytond.module import ADDONS_PATH
from trytond.osv.orm import ExceptORM
from trytond.wizard import Wizard
VER_REGEXP = re.compile(
"^(\\d+)((\\.\\d+)*)([a-z]?)((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?$")
SUFFIX_VALUE = {"pre": -2, "p": 0, "alpha": -4, "beta": -3, "rc": -1}
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-revision1")
@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:
suffix1 = ("p","-1")
else:
suffix1 = SUFFIX_REGEXP.match(list1[i]).groups()
if len(list2) <= i:
suffix2 = ("p","-1")
else:
suffix2 = SUFFIX_REGEXP.match(list2[i]).groups()
if suffix1[0] != suffix2[0]:
return SUFFIX_VALUE[suffix1[0]] - SUFFIX_VALUE[suffix2[0]]
if suffix1[1] != suffix2[1]:
# it's possible that the s(1|2)[1] == ''
# in such a case, fudge it.
try:
revision1 = int(suffix1[1])
except ValueError:
revision1 = 0
try:
revision2 = int(suffix2[1])
except ValueError:
revision2 = 0
if revision1 - revision2:
return revision1 - revision2
# the suffix part is equal to, so finally check the revision
if match1.group(9):
revision1 = int(match1.group(9))
else:
revision1 = 0
if match2.group(9):
revision2 = int(match2.group(9))
else:
revision2 = 0
return revision1 - revision2
class Module(OSV):
"Module"
_name = "ir.module.module"
_description = __doc__
@staticmethod
def get_module_info(name):
"Return the content of the __terp__.py"
try:
file_p = tools.file_open(os.path.join(ADDONS_PATH, name,
'__terp__.py'))
data = file_p.read()
info = eval(data)
file_p.close()
except:
return {}
return info
def _get_installed_version(self, cursor, user, ids, name, arg,
context=None):
res = {}
for module in self.browse(cursor, user, ids, context=context):
if module.state in ('installed', 'to upgrade', 'to remove'):
res[module.id] = Module.get_module_info(
module.name).get('version', '')
else:
res[module.id] = ''
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),
'dependencies_id': fields.one2many('ir.module.module.dependency',
'module_id', 'Dependencies', readonly=True),
'state': fields.selection([
('uninstallable','Not Installable'),
('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'),
'license': fields.selection([('GPL-2', 'GPL-2'),
('Other proprietary', 'Other proprietary')], string='License',
readonly=True),
}
_defaults = {
'state': lambda *a: 'uninstalled',
'demo': lambda *a: False,
'license': lambda *a: 'GPL-2',
}
_order = 'name'
_sql_constraints = [
('name_uniq', 'unique (name)', 'The name of the module must be unique!')
]
def unlink(self, cursor, user, ids, context=None):
if not ids:
return True
if isinstance(ids, (int, long)):
ids = [ids]
for module in self.browse(cursor, user, ids, context=context):
if module.state in (
'installed',
'to upgrade',
'to remove',
'to install',
):
raise ExceptORM('Error',
'You try to remove a module that is installed ' \
'or will be installed')
return super(Module, self).unlink(cursor, user, ids, context=context)
#TODO: remove the level check recursion
def state_change(self, cursor, user, ids, newstate, context=None, level=50):
if level < 1:
raise Exception, 'Recursion error in modules dependencies!'
demo = True
for module in self.browse(cursor, user, ids, context=context):
mdemo = True
for dep in module.dependencies_id:
ids2 = self.search(cursor, user, [('name', '=', dep.name)],
context=context)
mdemo = self.state_change(cursor, user, ids2, newstate,
context, level-1,) \
and mdemo
if not module.dependencies_id:
mdemo = module.demo
if module.state == 'uninstalled':
self.write(cursor, user, [module.id], {
'state': newstate,
'demo': mdemo,
}, context=context)
demo = demo and mdemo
return demo
def state_upgrade(self, cursor, user, ids, newstate, context=None,
level=50):
dependency_obj = self.pool.get('ir.module.module.dependency')
if level < 1:
raise Exception, 'Recursion error in modules dependencies!'
for module in self.browse(cursor, user, ids):
dep_ids = dependency_obj.search(cursor, user, [
('name', '=', module.name),
], context=context)
if dep_ids:
ids2 = []
for dep in dependency_obj.browse(cursor, user, dep_ids,
context=context):
if dep.module_id.state != 'to upgrade':
ids2.append(dep.module_id.id)
self.state_upgrade(cursor, user, ids2, newstate, context, level)
if module.state == 'installed':
self.write(cursor, user, module.id, {
'state': newstate,
}, context=context)
return True
def button_install(self, cursor, user, ids, context=None):
return self.state_change(cursor, user, ids, 'to install',
context=context)
def button_install_cancel(self, cursor, user, ids, context=None):
self.write(cursor, user, ids, {
'state': 'uninstalled',
'demo': False,
}, context=context)
return True
def button_uninstall(self, cursor, user, ids, context=None):
for module in self.browse(cursor, user, ids, context=context):
cursor.execute('SELECT m.state, m.name ' \
'FROM ir_module_module_dependency d ' \
'JOIN ir_module_module m on (d.module_id=m.id) ' \
'WHERE d.name = %s ' \
'AND m.state not in ' \
'(\'uninstalled\',\'uninstallable\',\'to remove\')',
(module.name,))
res = cursor.fetchall()
if res:
raise ExceptORM('Error',
'The modules you are trying to remove ' \
'depends on installed modules :\n' + \
'\n'.join(['\t%s: %s' % (x[0], x[1]) for x in res]))
self.write(cursor, user, ids, {'state': 'to remove'})
return True
def button_uninstall_cancel(self, cursor, user, ids, context=None):
self.write(cursor, user, ids, {'state': 'installed'}, context=context)
return True
def button_upgrade(self, cursor, user, ids, context=None):
return self.state_upgrade(cursor, user, ids, 'to upgrade', context)
def button_upgrade_cancel(self, cursor, user, ids, context=None):
self.write(cursor, user, ids, {'state': 'installed'}, context=context)
return True
def button_update_translations(self, cursor, user, ids, context=None):
lang_obj = self.pool.get('res.lang')
lang_ids = lang_obj.search(cursor, user, [('translatable', '=', True)],
context=context)
langs = lang_obj.browse(cursor, user, lang_ids, context=context)
for module in self.browse(cursor, user, ids, context=context):
files = Module.get_module_info(module.name).get('translations', {})
for lang in langs:
if files.has_key(lang.code):
filepath = os.path.join(ADDONS_PATH, module.name,
files[lang.code])
tools.trans_load(filepath, lang.code)
return True
# update the list of available packages
def update_list(self, cursor, user, context=None):
robj = self.pool.get('ir.module.repository')
res = [0, 0] # [update, add]
# iterate through installed modules and mark them as being so
for name in os.listdir(ADDONS_PATH):
mod_name = name
if name[-4:] == '.zip':
mod_name = name[:-4]
ids = self.search(cursor, user, [('name', '=', mod_name)],
context=context)
if ids:
module_id = ids[0]
mod = self.browse(cursor, user, module_id)
terp = Module.get_module_info(mod_name)
if terp.get('installable', True) \
and mod.state == 'uninstallable':
self.write(cursor, user, module_id, {
'state': 'uninstalled'}, context=context)
if vercmp(terp.get('version', ''),
mod.latest_version or '0') > 0:
self.write(cursor, user, module_id, {
'latest_version': terp.get('version'),
'url': '',
}, context=context)
res[0] += 1
self.write(cursor, user, module_id, {
'description': terp.get('description', ''),
'shortdesc': terp.get('name', ''),
'author': terp.get('author', ''),
'website': terp.get('website', ''),
'license': terp.get('license', 'GPL-2'),
})
self._update_dependencies(cursor, user, module_id,
terp.get('depends', []))
self._update_category(cursor, user, module_id,
terp.get('category', 'None'))
continue
mod_path = os.path.join(ADDONS_PATH, name)
if os.path.isdir(mod_path) \
or os.path.islink(mod_path) \
or zipfile.is_zipfile(mod_path):
terp = Module.get_module_info(mod_name)
if not terp or not terp.get('installable', True):
continue
if not os.path.isfile(
os.path.join(ADDONS_PATH, mod_name+'.zip')):
import imp
# XXX must restrict to only addons paths
imp.load_module(name, *imp.find_module(mod_name))
else:
import zipimport
mod_path = os.path.join(ADDONS_PATH, mod_name+'.zip')
zimp = zipimport.zipimporter(mod_path)
zimp.load_module(mod_name)
new_id = self.create(cursor, user, {
'name': mod_name,
'state': 'uninstalled',
'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'),
})
res[1] += 1
self._update_dependencies(cursor, user, new_id,
terp.get('depends', []))
self._update_category(cursor, user, new_id,
terp.get('category', 'None'))
import socket
socket.setdefaulttimeout(10)
for repository in robj.browse(cursor, user,
robj.search(cursor, user, []), context=context):
try:
index_page = urllib.urlopen(repository.url).read()
except IOError, exception:
if exception.errno == 21:
raise ExceptORM('Error',
'This url \'%s\' must provide an html file '
'with links to zip modules' % (repository.url))
else:
raise
modules = re.findall(repository.filter, index_page, re.I+re.M)
mod_sort = {}
for module in modules:
name = module[0]
version = module[1]
extension = module[-1]
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(cursor, user, [('name', '=', name)],
context=context)
if not ids:
self.create(cursor, user, {
'name': name,
'latest_version': version,
'url': url,
'state': 'uninstalled',
}, context=context)
res[1] += 1
else:
mod_id = ids[0]
latest_version = self.browse(cursor, user, mod_id,
context=context).latest_version
if vercmp(version, latest_version) > 0:
self.write(cursor, user, mod_id, {
'latest_version': version,
'url': url,
}, context=context)
res[0] += 1
return res
def download(self, cursor, user, ids, download=True, context=None):
res = []
for mod in self.browse(cursor, user, 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
res.append(mod.url)
if not download:
continue
zip_file = urllib.urlopen(mod.url).read()
fname = os.path.join(ADDONS_PATH, mod.name+'.zip')
try:
file_p = file(fname, 'wb')
file_p.write(zip_file)
file_p.close()
except IOError:
raise ExceptORM('Error', 'Can not create the module file: %s'
% (fname,))
terp = Module.get_module_info(mod.name)
self.write(cursor, user, mod.id, {
'description': terp.get('description', ''),
'shortdesc': terp.get('name', ''),
'author': terp.get('author', 'Unknown'),
'website': terp.get('website', ''),
'license': terp.get('license', 'GPL-2'),
})
self._update_dependencies(cursor, user, mod.id, terp.get('depends',
[]))
self._update_category(cursor, user, mod.id, terp.get('category',
'Uncategorized'))
return res
def _update_dependencies(self, cursor, user, module_id, depends=None):
dependency_obj = self.pool.get('ir.module.module.dependency')
dependency_ids = dependency_obj.search(cursor, user, [
('module_id', '=', module_id),
])
dependency_obj.unlink(cursor, user, dependency_ids)
if depends is None:
depends = []
for depend in depends:
dependency_obj.create(cursor, user, {
'module_id': module_id,
'name': depend,
})
def _update_category(self, cursor, user, module_id, category='None'):
category_obj = self.pool.get('ir.module.category')
categs = category.split('/')
parent_id = None
while categs:
if parent_id is not None:
category_ids = category_obj.search(cursor, user, [
('name', '=', categs[0]),
('parent_id', '=', parent_id),
])
else:
category_ids = category_obj.search(cursor, user, [
('name', '=', categs[0]),
('parent_id', '=', False),
])
if not category_ids:
parent_id = category_obj.create(cursor, user, {
'name': categs[0],
'parent_id': parent_id,
})
else:
parent_id = category_ids[0]
categs = categs[1:]
self.write(cursor, user, module_id, {'category_id': parent_id})
Module()
class ModuleDependency(OSV):
"Module dependency"
_name = "ir.module.module.dependency"
_description = __doc__
def _state(self, cursor, user, ids, name, args, context=None):
result = {}
module_obj = self.pool.get('ir.module.module')
for dependency in self.browse(cursor, user, ids):
ids = module_obj.search(cursor, user, [
('name', '=', dependency.name),
], context=context)
if ids:
result[dependency.id] = module_obj.browse(cursor, user, ids[0],
context=context).state
else:
result[dependency.id] = 'unknown'
return result
_columns = {
'name': fields.char('Name', size=128),
'module_id': fields.many2one('ir.module.module', 'Module', select=1),
'state': fields.function(_state, method=True, type='selection',
selection=[
('uninstallable','Uninstallable'),
('uninstalled','Not Installed'),
('installed','Installed'),
('to upgrade','To be upgraded'),
('to remove','To be removed'),
('to install','To be installed'),
('unknown', 'Unknown'),
], string='State', readonly=True),
}
ModuleDependency()
class ModuleUpdateList(Wizard):
"Update module list"
_name = 'ir.module.module.update_list'
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 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):
module_obj = self.pool.get('ir.module.module')
update, add = module_obj.update_list(cr, uid)
return {'update': update, 'add': add}
def _action_module_open(self, cr, uid, data, context):
return {
'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):
repository_obj = self.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([x['name']+': '+x['url'] \
for x in res]),
}
states = {
'init': {
'actions': [_get_repositories],
'result': {
'type': 'form',
'arch': arch,
'fields': fields,
'state': [
('end', 'Cancel', 'gtk-cancel'),
('update', 'Check new modules', 'gtk-ok', True),
]
}
},
'update': {
'actions': [_update_module],
'result': {
'type': 'form',
'arch': arch_module,
'fields': fields_module,
'state': [
('open_window', 'Ok', 'gtk-ok', True),
]
}
},
'open_window': {
'actions': [],
'result': {
'type': 'action',
'action': _action_module_open,
'state':'end',
}
},
}
ModuleUpdateList()
+107
View File
@@ -0,0 +1,107 @@
<?xml version="1.0"?>
<terp>
<data>
<menuitem name="Administration/IR/Modules" id="menu_modules"/>
<record model="ir.ui.view" id="module_view_form">
<field name="name">ir.module.module.form</field>
<field name="model">ir.module.module</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Module">
<notebook>
<page string="Module">
<field name="name" select="1"/>
<field name="shortdesc" colspan="4" select="2"/>
<field name="description" colspan="4" select="2"/>
<field name="installed_version"/>
<field name="latest_version"/>
<field name="author" select="2"/>
<field name="website" select="2" widget="url"/>
<field name="url" widget="url"/>
<field name="license"/>
<field name="demo" readonly="1"/>
<newline/>
<field name="state" readonly="1" select="1"/>
<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 (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"/>
<!--
<button string="Update translations" name="button_update_translations" type="object" states="installed"/>
-->
</group>
</page>
<page string="Dependencies">
<field name="dependencies_id" colspan="4" nolabel="1">
<tree string="Dependencies">
<field name="name"/>
<field name="state"/>
</tree>
</field>
</page>
</notebook>
</form>
</field>
</record>
<record model="ir.ui.view" id="module_view_tree">
<field name="name">ir.module.module.tree</field>
<field name="model">ir.module.module</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Modules"
colors="blue:state=='to upgrade' or state=='to install';grey:state=='uninstalled' or state=='uninstallable'">
<field name="name"/>
<field name="shortdesc"/>
<field name="author"/>
<field name="installed_version"/>
<field name="latest_version"/>
<field name="state"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="act_module_form">
<field name="name">Modules</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.module</field>
<field name="view_type">form</field>
</record>
<record model="ir.actions.act_window.view"
id="act_module_form_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="module_view_tree"/>
<field name="act_window_id" ref="act_module_form"/>
</record>
<record model="ir.actions.act_window.view"
id="act_module_form_view2">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="module_view_form"/>
<field name="act_window_id" ref="act_module_form"/>
</record>
<record model="ir.action.keyword" id="act_category_keyword1">
<field name="action" ref="act_module_form"/>
<field name="keyword">tree_open</field>
<field name="model">ir.module.category,0</field>
</record>
<menuitem name="Administration/IR/Modules/"
action="act_module_form" id="menu_module_form"/>
<record model="ir.actions.wizard" id="act_module_update_list">
<field name="name">Update Module List</field>
<field name="wiz_name">ir.module.module.update_list</field>
</record>
<menuitem name="Administration/IR/Modules/Update Module List"
action="act_module_update_list"
type="wizard"
id="menu_module_update_list"/>
</data>
</terp>
+29
View File
@@ -0,0 +1,29 @@
"Repository"
from trytond.osv import OSV, fields
class Repository(OSV):
"Module repository"
_name = "ir.module.repository"
_description = __doc__
_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.'),
'active': fields.boolean('Active'),
}
_defaults = {
'sequence': lambda *a: 5,
'filter': lambda *a: 'href="([a-zA-Z0-9_]+)-' \
'((\\d+)((\\.\\d+)*)([a-z]?)' \
'((_(pre|p|beta|alpha|rc)\\d*)*)(-r(\\d+))?)(\.zip)"',
'active': lambda *a: 1,
}
_order = "sequence"
Repository()
+54
View File
@@ -0,0 +1,54 @@
<?xml version="1.0"?>
<terp>
<data>
<record model="ir.ui.view" id="repository_view_form">
<field name="name">ir.module.repository.form</field>
<field name="model">ir.module.repository</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Repository">
<field name="name" select="1"/>
<field name="sequence"/>
<field name="url" select="1" widget="url"/>
<field name="active" select="1"/>
<field name="filter" colspan="4"/>
</form>
</field>
</record>
<record model="ir.ui.view" id="repository_view_tree">
<field name="name">ir.module.repository.tree</field>
<field name="model">ir.module.repository</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Repositories">
<field name="sequence"/>
<field name="active"/>
<field name="name"/>
<field name="url"/>
</tree>
</field>
</record>
<record model="ir.actions.act_window" id="act_repository_form">
<field name="name">Repositories</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">ir.module.repository</field>
<field name="view_type">form</field>
</record>
<record model="ir.actions.act_window.view"
id="act_repository_form_view1">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="repository_view_tree"/>
<field name="act_window_id" ref="act_repository_form"/>
</record>
<record model="ir.actions.act_window.view"
id="act_repository_form_view2">
<field name="sequence" eval="2"/>
<field name="view_mode">form</field>
<field name="view_id" ref="repository_view_form"/>
<field name="act_window_id" ref="act_repository_form"/>
</record>
<menuitem name="Administration/IR/Modules/"
action="act_repository_form" id="menu_repository_form"/>
</data>
</terp>
+40
View File
@@ -42,6 +42,46 @@ class View(OSV):
(_check_xml, 'Invalid XML for View Architecture!', ['arch'])
]
def unlink(self, cursor, user, ids, context=None):
views = self.browse(cursor, user, ids, context=context)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get()
except:
pass
res = super(View, self).unlink(cursor, user, ids, context=context)
return res
def create(self, cursor, user, vals, context=None):
res = super(View, self).create(cursor, user, vals, context=context)
if 'model' in vals:
model = vals['model']
# Restart the cache
try:
self.pool.get(model).fields_view_get()
except:
pass
return res
def write(self, cursor, user, ids, vals, context=None):
views = self.browse(cursor, user, ids)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get()
except:
pass
res = super(View, self).write(cursor, user, ids, vals, context=context)
views = self.browse(cursor, user, ids)
for view in views:
# Restart the cache
try:
self.pool.get(view.model).fields_view_get()
except:
pass
return res
View()
+2
View File
@@ -168,6 +168,8 @@ def load_module_graph(cursor, graph, **kwargs):
sys.stdout.flush()
pool = pooler.get_pool(cursor.dbname)
modules = pool.instanciate(module)
pool_wizard = pooler.get_pool_wizard(cursor.dbname)
wizards = pool_wizard.instanciate(module, pool)
cursor.execute('SELECT state, demo FROM ir_module_module WHERE name = %s',
(module,))
(package_state, package_demo) = (cursor.rowcount and cursor.fetchone()) \
+1 -3
View File
@@ -479,8 +479,7 @@ class Function(_column):
_properties = True
def __init__(self, fnct, arg=None, fnct_inv=None, fnct_inv_arg=None,
type='float', fnct_search=None, obj=None, method=False,
store=False, **args):
type='float', fnct_search=None, obj=None, method=False, **args):
_column.__init__(self, **args)
self._obj = obj
self._method = method
@@ -494,7 +493,6 @@ class Function(_column):
self.readonly = 1
self._type = type
self._fnct_search = fnct_search
self.store = store
if type == 'float':
self._symbol_c = '%f'
self._symbol_f = lambda x: __builtin__.float(x or 0.0)
+166 -131
View File
@@ -1,8 +1,13 @@
# -*- coding: utf-8 -*-
from xml import dom
from xml.dom import minidom
from xml import xpath
from trytond.netsvc import Logger, LOG_ERROR, LOG_WARNING, LocalService
import fields
from trytond.tools import Cache
import md5
ID_MAX = 1000
def intersect(i, j):
return [x for x in j if x in i]
@@ -427,11 +432,10 @@ class ORM(object):
"AND a.attname = %s " \
"AND c.oid = a.attrelid " \
"AND a.atttypid = t.oid",
(self._table, k.lower()))
(self._table, k))
res = cursor.dictfetchall()
if not res:
if not isinstance(field, fields.function) \
or field.store:
if not isinstance(field, fields.function):
# add the missing field
cursor.execute("ALTER TABLE \"%s\" " \
"ADD COLUMN \"%s\" %s" % \
@@ -489,8 +493,7 @@ class ORM(object):
f_pg_type = f_pg_def['typname']
f_pg_size = f_pg_def['size']
f_pg_notnull = f_pg_def['attnotnull']
if isinstance(field, fields.function) \
and not field.store:
if isinstance(field, fields.function):
logger.notify_channel('init', LOG_WARNING,
'column %s (%s) in table %s was converted '\
'to a function !\n' \
@@ -954,29 +957,36 @@ class ORM(object):
and getattr(self._columns[x], '_classic_write')] + \
self._inherits.values()
res = []
if len(fields_pre) :
fields_pre2 = map(lambda x: (x in ('create_date', 'write_date')) \
fields_pre2 = [(x in ('create_date', 'write_date')) \
and ('date_trunc(\'second\', ' + x + ') as ' + x) \
or '"' + x + '"', fields_pre)
if domain1:
cursor.execute(('SELECT ' + ','.join(fields_pre2 + ['id']) + \
' FROM \"' + self._table +'\" ' \
'WHERE id in (' + ','.join([str(x) for x in ids]) + ')'\
' AND ' + domain1 + ' order by ' + self._order),
domain2)
if not cursor.rowcount == len({}.fromkeys(ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % self._description)
else:
cursor.execute('SELECT ' + ','.join(fields_pre2 + ['id']) + \
' FROM \"' + self._table + '\" ' \
'WHERE id in (' + ','.join([str(x) for x in ids]) + ')'\
' ORDER BY ' + self._order)
res = cursor.dictfetchall()
or '"' + x + '"' for x in fields_pre]
for i in range((len(ids) / ID_MAX) + ((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
if domain1:
cursor.execute(('SELECT ' + \
','.join(fields_pre2 + ['id']) + \
' FROM \"' + self._table +'\" ' \
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids]) + ')'\
' AND ' + domain1 + ' ORDER BY ' + self._order),
domain2)
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
else:
cursor.execute('SELECT ' + \
','.join(fields_pre2 + ['id']) + \
' FROM \"' + self._table + '\" ' \
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids]) + ')'\
' ORDER BY ' + self._order)
res.extend(cursor.dictfetchall())
else:
res = [{'id':x} for x in ids]
res = [{'id': x} for x in ids]
for field in fields_pre:
if self._columns[field].translate:
@@ -1111,14 +1121,18 @@ class ORM(object):
ids = [ids]
delta = context.get('read_delta', False)
if delta and self._log_access:
cursor.execute(
"SELECT (now() - min(write_date)) <= '%s'::interval " \
"FROM \"%s\" WHERE id in (%s)" % \
(delta, self._table, ",".join([str(x) for x in ids])))
res = cursor.fetchone()
if res and res[0]:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute(
"SELECT (now() - min(write_date)) <= '%s'::interval " \
"FROM \"%s\" WHERE id in (%s)" % \
(delta, self._table,
",".join([str(x) for x in sub_ids])))
res = cursor.fetchone()
if res and res[0]:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
self.pool.get('ir.model.access').check(cursor, user, self._name,
'unlink')
@@ -1126,7 +1140,6 @@ class ORM(object):
wf_service = LocalService("workflow")
for obj_id in ids:
wf_service.trg_delete(user, self._name, obj_id, cursor)
str_d = ','.join(('%d',) * len(ids))
#cursor.execute('select * from ' + self._table + \
# ' where id in ('+str_d+')', ids)
@@ -1139,19 +1152,31 @@ class ORM(object):
self._name)
if domain1:
domain1 = ' AND ' + domain1
cursor.execute('SELECT id FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1, ids + domain2)
if not cursor.rowcount == len({}.fromkeys(ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % self._description)
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
str_d = string.join(('%d',) * len(sub_ids), ',')
if domain1:
cursor.execute('SELECT id FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1,
sub_ids + domain2)
if not cursor.rowcount == len({}.fromkeys(ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % self._description)
cursor.execute('DELETE FROM inherit ' \
'WHERE (obj_type = %s AND obj_id IN ('+str_d+')) ' \
'OR (inst_type = %s AND inst_id IN ('+str_d+'))',
((self._name,) + tuple(ids) + (self._name,) + tuple(ids)))
cursor.execute('DELETE FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1, ids + domain2)
cursor.execute('DELETE FROM inherit ' \
'WHERE (obj_type = %s AND obj_id IN ('+str_d+')) ' \
'OR (inst_type = %s AND inst_id IN ('+str_d+'))',
((self._name,) + tuple(sub_ids) + \
(self._name,) + tuple(sub_ids)))
if domain:
cursor.execute('DELETE FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ') ' + domain1,
sub_ids + domain2)
else:
cursor.execute('DELETE FROM "'+self._table+'" ' \
'WHERE id IN (' + str_d + ')', sub_ids)
return True
# TODO: Validate
@@ -1164,23 +1189,27 @@ class ORM(object):
ids = [ids]
delta = context.get('read_delta', False)
if delta and self._log_access:
cursor.execute("select (now() - min(write_date)) <= '%s'::interval"\
" FROM %s WHERE id IN (%s)" % \
(delta, self._table, ",".join([str(x) for x in ids])))
res = cursor.fetchone()
if res and res[0]:
for field in vals:
if field in self._columns \
and self._columns[field]._classic_write:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute("SELECT " \
"(now() - min(write_date)) <= '%s'::interval"\
" FROM %s WHERE id IN (%s)" % \
(delta, self._table,
",".join([str(x) for x in sub_ids])))
res = cursor.fetchone()
if res and res[0]:
for field in vals:
if field in self._columns \
and self._columns[field]._classic_write:
raise ExceptORM('ConcurrencyException',
'This record was modified in the meanwhile')
self.pool.get('ir.model.access').check(cursor, user, self._name,
'write')
#for v in self._inherits.values():
# assert v not in vals, (v, vals)
ids_str = ','.join([str(x) for x in ids])
upd0 = []
upd1 = []
upd_todo = []
@@ -1232,23 +1261,35 @@ class ORM(object):
user, self._name)
if domain1:
domain1 = ' and ' + domain1
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ') ' + domain1, domain2)
if not cursor.rowcount == len({}.fromkeys(ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % self._description)
else:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ')')
if not cursor.rowcount == len({}.fromkeys(ids)):
raise ExceptORM('AccessError',
'You try to write on an record ' \
'that doesn\'t exist (Document type: %s).' % \
self._description)
cursor.execute('UPDATE "' + self._table + '" ' \
'SET ' + ','.join(upd0) + ' ' \
'WHERE id IN (' + ids_str + ') ' + domain1, upd1 + domain2)
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i + 1)]
ids_str = ','.join([str(x) for x in sub_ids])
if domain1:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ') ' + domain1, domain2)
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
else:
cursor.execute('SELECT id FROM "' + self._table + '" ' \
'WHERE id IN (' + ids_str + ')')
if not cursor.rowcount == len({}.fromkeys(sub_ids)):
raise ExceptORM('AccessError',
'You try to bypass an access rule ' \
'(Document type: %s).' % \
self._description)
if domain1:
cursor.execute('UPDATE "' + self._table + '" ' \
'SET ' + ','.join(upd0) + ' ' \
'WHERE id IN (' + ids_str + ') ' + domain1,
upd1 + domain2)
else:
cursor.execute('UPDATE "' + self._table + '" ' \
'SET ' + ','.join(upd0) + ' ' \
'WHERE id IN (' + ids_str + ') ', upd1)
if totranslate:
for field in direct:
@@ -1267,10 +1308,15 @@ class ORM(object):
for table in self._inherits:
col = self._inherits[table]
cursor.execute('SELECT DISTINCT "' + col + '" ' \
'FROM "' + self._table + '" WHERE id IN (' + ids_str + ')',
upd1)
nids = [x[0] for x in cursor.fetchall()]
nids = []
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids = ids[ID_MAX * i:ID_MAX * (i +1)]
ids_str = ','.join([str(x) for x in sub_ids])
cursor.execute('SELECT DISTINCT "' + col + '" ' \
'FROM "' + self._table + '" WHERE id IN (' + ids_str + ')',
upd1)
nids.extend([x[0] for x in cursor.fetchall()])
vals2 = {}
for val in updend:
@@ -1287,7 +1333,6 @@ class ORM(object):
wf_service = LocalService("workflow")
for obj_id in ids:
wf_service.trg_write(user, self._name, obj_id, cursor)
self._update_function_stored(cursor, user, ids, context=context)
return True
def create(self, cursor, user, vals, context=None):
@@ -1385,32 +1430,8 @@ class ORM(object):
wf_service = LocalService("workflow")
wf_service.trg_create(user, self._name, id_new, cursor)
self._update_function_stored(cursor, user, [id_new], context=context)
return id_new
def _update_function_stored(self, cursor, user, ids, context=None):
ffields = [x for x in self._columns if isinstance(self._columns[x],
fields.function) and self._columns[x].store]
if ffields:
result = self.read(cursor, user, ids, fields_names=ffields,
context=context)
for res in result:
upd0 = []
upd1 = []
for field in res:
if field not in ffields:
continue
value = res[field]
if self._columns[field]._type in ('many2one', 'one2one'):
value = res[field][0]
upd0.append('"' + field + '"=' + \
self._columns[field]._symbol_set[0])
upd1.append(self._columns[field]._symbol_set[1](value))
upd1.append(res['id'])
cursor.execute('update "' + self._table + '" set ' + \
','.join(upd0) + ' where id = %d', upd1)
return True
def fields_get(self, cursor, user, fields_names=None, context=None):
"""
returns the definition of each field in the object
@@ -1599,28 +1620,15 @@ class ORM(object):
return arch, fields2
def fields_view_get(self, cursor, user, view_id=None, view_type='form',
context=None, toolbar=False):
context=None, toolbar=False, hexmd5=None):
def _inherit_apply(src, inherit):
def _find(node, node2):
if node.nodeType == node.ELEMENT_NODE \
and node.localName == node2.localName:
res = True
for attr in node2.attributes.keys():
if attr == 'position':
continue
if node.hasAttribute(attr):
if node.getAttribute(attr) == \
node2.getAttribute(attr):
continue
res = False
if res:
return node
for child in node.childNodes:
res = _find(child, node2)
if res:
return res
if node2.nodeType == node2.ELEMENT_NODE \
and node2.localName == 'xpath':
res = xpath.Evaluate(node2.getAttribute('expr'), node)
return res and res[0] or None
return None
doc_src = dom.minidom.parseString(src)
@@ -1782,8 +1790,13 @@ class ORM(object):
'action': resaction,
'relate': resrelate,
}
result['md5'] = md5.new(str(result)).hexdigest()
if hexmd5 == result['md5']:
return True
return result
fields_view_get = Cache()(fields_view_get)
_view_look_dom_arch = __view_look_dom_arch
def _where_calc(self, cursor, user, args, active_test=True, context=None):
@@ -1817,6 +1830,16 @@ class ORM(object):
fargs = args[i][0].split('.', 1)
field = table._columns.get(fargs[0], False)
if not field:
if args[i][0] == 'id' and args[i][1] == 'child_of':
ids2 = args[i][2]
def _rec_get(ids, table, parent):
if not ids:
return []
ids2 = table.search(cursor, user,
[(parent, 'in', ids)], context=context)
return ids2 + _rec_get(ids2, table, parent)
args[i] = (args[i][0], 'in', ids2 + \
_rec_get(ids2, table, table._parent_name), table)
i += 1
continue
if len(fargs) > 1:
@@ -1853,11 +1876,15 @@ class ORM(object):
if not ids2:
args[i] = ('id', '=', '0')
else:
cursor.execute('SELECT "' + field._fields_id + \
'" FROM "' + field_obj._table + '" ' \
'WHERE id IN (' + \
','.join([str(x) for x in ids2]) + ')')
ids3 = [x[0] for x in cursor.fetchall()]
ids3 = []
for i in range((len(ids2) / ID_MAX) + \
(len(ids2) % ID_MAX)):
sub_ids = ids2[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute('SELECT "' + field._fields_id + \
'" FROM "' + field_obj._table + '" ' \
'WHERE id IN (' + \
','.join([str(x) for x in sub_ids2]) + ')')
ids3.extend([x[0] for x in cursor.fetchall()])
args[i] = ('id', 'in', ids3)
i += 1
@@ -2027,6 +2054,7 @@ class ORM(object):
todel.append(xitem)
for xitem in todel[::-1]:
del arg[2][xitem]
#TODO fix max_stack_depth
if arg[0] == 'id':
qu1.append('(%s.id in (%s))' % \
(table._table,
@@ -2201,10 +2229,17 @@ class ORM(object):
parent = self._parent_name
ids_parent = ids[:]
while len(ids_parent):
cursor.execute('SELECT distinct "' + parent + '" ' +
'FROM "' + self._table + '" ' +
'WHERE id IN (' + ','.join([str(x) for x in ids_parent]) + ')')
ids_parent = [x[0] for x in cursor.fetchall()]
ids_parent2 = []
for i in range((len(ids) / ID_MAX) + \
((len(ids) % ID_MAX) and 1 or 0)):
sub_ids_parent = ids_parent[ID_MAX * i:ID_MAX * (i + 1)]
cursor.execute('SELECT distinct "' + parent + '" ' +
'FROM "' + self._table + '" ' +
'WHERE id IN ' \
'(' + ','.join([str(x) for x in sub_ids_parent]) + ')')
ids_parent2.extend(filter(None,
[x[0] for x in cursor.fetchall()]))
ids_parent = ids_parent2
for i in ids_parent:
if i in ids:
return False
+15 -1
View File
@@ -3,8 +3,10 @@ from netsvc import Logger, LOG_INFO
_DB = {}
_POOL = {}
_POOL_WIZARD = {}
def get_db_and_pool(db_name, force_demo=False, update_module=False):
def get_db_and_pool(db_name, force_demo=False, update_module=False,
wizard=False):
if db_name in _DB:
database = _DB[db_name]
else:
@@ -15,12 +17,18 @@ def get_db_and_pool(db_name, force_demo=False, update_module=False):
if db_name in _POOL:
pool = _POOL[db_name]
pool_wizard = _POOL_WIZARD[db_name]
else:
Logger().notify_channel('pooler', LOG_INFO,
'Instanciate pooler for %s' % (db_name))
from osv.osv import OSVService
pool = OSVService()
_POOL[db_name] = pool
from wizard import WizardService
pool_wizard = WizardService()
_POOL_WIZARD[db_name] = pool_wizard
from module import load_modules
load_modules(database, force_demo, update_module)
@@ -28,6 +36,8 @@ def get_db_and_pool(db_name, force_demo=False, update_module=False):
import report
#report.interface.register_all(database)
pool.get('ir.cron').pool_jobs(database.dbname)
if wizard:
return database, pool_wizard
return database, pool
def restart_pool(db_name, force_demo=False, update_module=False):
@@ -57,3 +67,7 @@ def get_db(db_name):
def get_pool(db_name, force_demo=False, update_module=False):
pool = get_db_and_pool(db_name, force_demo, update_module)[1]
return pool
def get_pool_wizard(db_name, force_demo=False, update_module=False):
pool = get_db_and_pool(db_name, force_demo, update_module, wizard=True)[1]
return pool
+1 -1
View File
@@ -350,7 +350,7 @@ class Cache(object):
kwargs.sort()
# Work out key as a tuple of ('argname', value) pairs
key = (('dbname', cursor.dbname),) + tuple(kwargs)
key = (('dbname', cursor.dbname), str(kwargs))
# Check cache and return cached value if possible
if key in self.cache:
+3 -3
View File
@@ -16,9 +16,9 @@ class Wizard(Service):
def _execute(self, database, user, wiz_id, datas, action, context):
self.wiz_datas[wiz_id].update(datas)
wiz = LocalService('wizard.' + self.wiz_name[wiz_id])
return wiz.execute(database, user, self.wiz_datas[wiz_id], action,
context)
wiz = LocalService('wizard_proxy')
return wiz.execute(database, user, self.wiz_name[wiz_id],
self.wiz_datas[wiz_id], action, context)
def create(self, database, user, passwd, wiz_name, datas=None):
security.check(database, user, passwd)
+190 -104
View File
@@ -6,6 +6,11 @@ from xml import dom
from trytond.osv import ExceptORM, ExceptOSV
import sys
MODULE_LIST = []
MODULE_CLASS_LIST = {}
CLASS_POOL = {}
class ExceptWizard(Exception):
def __init__(self, name, value):
Exception.__init__(self)
@@ -16,107 +21,23 @@ class ExceptWizard(Exception):
except_wizard = ExceptWizard
class WizardService(Service):
states = {}
name = ""
def __init__(self):
self.wiz_name = self.name
assert not service_exist(self.wiz_name), \
'The wizard "%s" already exists!' % self.name
Service.__init__(self, self.wiz_name)
self.object_name_pool = {}
self.module_obj_list = {}
Service.__init__(self, 'wizard_proxy')
Service.join_group(self, 'web-services')
Service.export_method(self, self.execute)
def translate_view(self, cursor, node, state, lang):
translation_obj = pooler.get_pool(cursor.dbname).get('ir.translation')
if node.nodeType == node.ELEMENT_NODE:
if node.hasAttribute('string') \
and node.getAttribute('string'):
res_trans = translation_obj._get_source(cursor,
self.wiz_name + ',' + state, 'wizard_view', lang)
if res_trans:
node.setAttribute('string', res_trans.decode('utf8'))
for child_node in node.childNodes:
self.translate_view(cursor, child_node, state, lang)
def execute_cr(self, cursor, user, data, state='init', context=None):
if context is None:
context = {}
res = {}
pool = pooler.get_pool(cursor.dbname)
values_obj = pool.get('ir.values')
translation_obj = pool.get('ir.translation')
def execute_cr(self, cursor, user, wizard_name, data, state='init',
context=None):
try:
state_def = self.states[state]
result_def = copy.copy(state_def.get('result', {}))
# iterate through the list of actions defined for this state
for action in state_def.get('actions', []):
# execute them
action_res = action(self, cursor, user, data, context)
assert isinstance(action_res, dict), \
'The return value of wizard actions ' \
'should be a dictionary'
result_def.update(action_res)
res['datas'] = result_def.get('datas', {})
lang = context.get('lang', 'en_US')
if result_def['type'] == 'action':
res['action'] = result_def['action'](self, cursor, user, data,
context)
elif result_def['type'] == 'form':
fields = copy.copy(result_def['fields'])
arch = copy.copy(result_def['arch'])
button_list = copy.copy(result_def['state'])
defaults = values_obj.get(cursor, user, 'default',
False, [('wizard.'+self.wiz_name, False)])
default_values = dict([(x[1], x[2]) for x in defaults])
for val in fields.keys():
if 'default' in fields[val]:
# execute default method for this field
if callable(fields[val]['default']):
fields[val]['value'] = fields[val]['default']\
(self, cursor, user, data, state, context)
else:
fields[val]['value'] = fields[val]['default']
del fields[val]['default']
if val in default_values:
fields[val]['value'] = default_values[val]
if 'selection' in fields[val]:
if callable(fields[val]['selection']):
fields[val] = copy.copy(fields[val])
fields[val]['selection'] = fields[val]['selection']\
(self, cursor, user, data, state, context)
# translate fields
for field in fields:
res_trans = translation_obj._get_source(cursor, user,
self.wiz_name + ',' + state + ',' + field,
'wizard_field', lang)
if res_trans:
fields[field]['string'] = res_trans
# translate arch
doc = dom.minidom.parseString(arch)
self.translate_view(cursor, doc, state, lang)
arch = doc.toxml()
# translate buttons
for i, button in enumerate(button_list):
button_name = button[0]
res_trans = translation_obj._get_source(cursor, user,
self.wiz_name + ',' + state + ',' + button_name,
'wizard_button', lang)
if res_trans:
button = list(button)
button[1] = res_trans
button_list[i] = tuple(button)
res['fields'] = fields
res['arch'] = arch
res['state'] = button_list
wizard = pooler.get_pool_wizard(cursor.dbname).get(wizard_name)
if not wizard:
self.abort_response('Wizard Error', 'warning',
'Wizard %s doesn\'t exist' % str(wizard_name))
res = wizard.execute(cursor, user, data, state, context)
return res
except ExceptORM, inst:
self.abort_response(inst.name, 'warning', inst.value)
except ExceptOSV, inst:
@@ -131,17 +52,14 @@ class WizardService(Service):
'Exception in call: ' + tb_s)
raise
if result_def['type'] == 'choice':
next_state = result_def['next_state'](self, cursor, user,
data, context)
return self.execute_cr(cursor, user, data, next_state, context)
return res
def execute(self, dbname, user, data, state='init', context=None):
def execute(self, dbname, user, wizard_name, data, state='init',
context=None):
cursor = pooler.get_db(dbname).cursor()
pool = pooler.get_pool_wizard(dbname)
try:
try:
res = self.execute_cr(cursor, user, data, state, context)
res = pool.execute_cr(cursor, user, wizard_name, data, state,
context)
cursor.commit()
except Exception:
cursor.rollback()
@@ -149,3 +67,171 @@ class WizardService(Service):
finally:
cursor.close()
return res
def add(self, name, object_name_inst):
"""
adds a new obj instance to the obj pool.
if it already existed, the instance is replaced
"""
if self.object_name_pool.has_key(name):
del self.object_name_pool[name]
self.object_name_pool[name] = object_name_inst
module = str(object_name_inst.__class__)[6:]
module = module[:len(module)-1]
module = module.split('.')[0][2:]
self.module_obj_list.setdefault(module, []).append(object_name_inst)
def get(self, name):
return self.object_name_pool.get(name, None)
def instanciate(self, module, pool_obj):
res = []
class_list = MODULE_CLASS_LIST.get(module, [])
for klass in class_list:
res.append(klass.create_instance(self, module, pool_obj))
return res
class Wizard(object):
_name = ""
states = {}
def __new__(cls):
for module in cls.__module__.split('.'):
if module != 'trytond' and module != 'addons':
break
if not hasattr(cls, '_module'):
cls._module = module
MODULE_CLASS_LIST.setdefault(cls._module, []).append(cls)
CLASS_POOL[cls._name] = cls
if module not in MODULE_LIST:
MODULE_LIST.append(cls._module)
return None
def create_instance(cls, pool, module, pool_obj):
"""
try to apply inheritancy at the instanciation level and
put objs in the pool var
"""
if pool.get(cls._name):
parent_class = pool.get(cls._name).__class__
new_states = copy.copy(getattr(pool.get(cls._name), 'states'))
for i in (
'actions',
'result',
):
new = copy.copy(getattr(new_states, i))
if hasattr(new, 'update'):
new.update(cls.__dict__.get(i, {}))
else:
new.extend(cls.__dict__.get(i, []))
new_states[i] = new
cls = type(cls._name, (cls, parent_class), {'states': new_states})
obj = object.__new__(cls)
obj.__init__(pool, pool_obj)
return obj
create_instance = classmethod(create_instance)
def __init__(self, pool, pool_obj):
pool.add(self._name, self)
self.pool = pool_obj
super(Wizard, self).__init__()
def translate_view(self, cursor, node, state, lang):
translation_obj = pooler.get_pool(cursor.dbname).get('ir.translation')
if node.nodeType == node.ELEMENT_NODE:
if node.hasAttribute('string') \
and node.getAttribute('string'):
res_trans = translation_obj._get_source(cursor,
self._name + ',' + state, 'wizard_view', lang)
if res_trans:
node.setAttribute('string', res_trans.decode('utf8'))
for child_node in node.childNodes:
self.translate_view(cursor, child_node, state, lang)
def execute(self, cursor, user, data, state='init', context=None):
if context is None:
context = {}
res = {}
values_obj = self.pool.get('ir.values')
translation_obj = self.pool.get('ir.translation')
state_def = self.states[state]
result_def = copy.copy(state_def.get('result', {}))
# iterate through the list of actions defined for this state
for action in state_def.get('actions', []):
# execute them
action_res = action(self, cursor, user, data, context)
assert isinstance(action_res, dict), \
'The return value of wizard actions ' \
'should be a dictionary'
result_def.update(action_res)
res = copy.copy(result_def)
res['datas'] = result_def.get('datas', {})
lang = context.get('lang', 'en_US')
if result_def['type'] == 'action':
res['action'] = result_def['action'](self, cursor, user, data,
context)
elif result_def['type'] == 'form':
fields = copy.copy(result_def['fields'])
arch = copy.copy(result_def['arch'])
button_list = copy.copy(result_def['state'])
defaults = values_obj.get(cursor, user, 'default',
False, [(self._name, False)])
default_values = dict([(x[1], x[2]) for x in defaults])
for val in fields.keys():
if 'default' in fields[val]:
# execute default method for this field
if callable(fields[val]['default']):
fields[val]['value'] = fields[val]['default']\
(self, cursor, user, data, state, context)
else:
fields[val]['value'] = fields[val]['default']
del fields[val]['default']
if val in default_values:
fields[val]['value'] = default_values[val]
if 'selection' in fields[val]:
if callable(fields[val]['selection']):
fields[val] = copy.copy(fields[val])
fields[val]['selection'] = fields[val]['selection']\
(self, cursor, user, data, state, context)
# translate fields
for field in fields:
res_trans = translation_obj._get_source(cursor, user,
self._name + ',' + state + ',' + field,
'wizard_field', lang)
if res_trans:
fields[field]['string'] = res_trans
# translate arch
doc = dom.minidom.parseString(arch)
self.translate_view(cursor, doc, state, lang)
arch = doc.toxml()
# translate buttons
for i, button in enumerate(button_list):
button_name = button[0]
res_trans = translation_obj._get_source(cursor, user,
self._name + ',' + state + ',' + button_name,
'wizard_button', lang)
if res_trans:
button = list(button)
button[1] = res_trans
button_list[i] = tuple(button)
res['fields'] = fields
res['arch'] = arch
res['state'] = button_list
if result_def['type'] == 'choice':
next_state = result_def['next_state'](self, cursor, user,
data, context)
return self.execute(cursor, user, data, next_state, context)
return res