merging from upper branch
This commit is contained in:
commit
b6aeba1508
18 files changed
+1302
-245
No files matched your search
@@ -9,3 +9,4 @@ from translation import *
|
||||
from exports import *
|
||||
from rule import *
|
||||
from property import *
|
||||
import module
|
||||
@@ -16,5 +16,8 @@
|
||||
'exports.xml',
|
||||
'rule.xml',
|
||||
'property.xml',
|
||||
'module/module.xml',
|
||||
'module/repository.xml',
|
||||
'module/category.xml',
|
||||
],
|
||||
}
|
||||
@@ -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'),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from category import *
|
||||
from repository import *
|
||||
from module import *
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
@@ -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>
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
||||
@@ -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()) \
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
@@ -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
|
||||
Reference in new issue
Block a user