diff --git a/trytond/ir/module/module.py b/trytond/ir/module/module.py
index 9bdc0d9491..6c6ec5946d 100644
--- a/trytond/ir/module/module.py
+++ b/trytond/ir/module/module.py
@@ -7,6 +7,7 @@ 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+))?$")
@@ -536,3 +537,89 @@ class ModuleDependency(OSV):
}
ModuleDependency()
+
+
+class ModuleUpdateList(Wizard):
+ "Update module list"
+ _name = 'ir.module.module.update_list'
+
+ arch = '''
+
'''
+ fields = {
+ 'repositories': {'type': 'text', 'string': 'Repositories', 'readonly': True},
+ }
+
+ arch_module = '''
+ '''
+
+ 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()
diff --git a/trytond/ir/module/module.xml b/trytond/ir/module/module.xml
index a0e876e83a..465f874ec9 100644
--- a/trytond/ir/module/module.xml
+++ b/trytond/ir/module/module.xml
@@ -95,5 +95,13 @@
+
+ Update Module List
+ ir.module.module.update_list
+
+
diff --git a/trytond/module.py b/trytond/module.py
index b799544c42..50f4bf1698 100644
--- a/trytond/module.py
+++ b/trytond/module.py
@@ -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()) \
diff --git a/trytond/pooler.py b/trytond/pooler.py
index 1b4300cd57..d43d6699a6 100644
--- a/trytond/pooler.py
+++ b/trytond/pooler.py
@@ -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
diff --git a/trytond/web_service/wizard.py b/trytond/web_service/wizard.py
index ad21bf9d02..d02532a41d 100644
--- a/trytond/web_service/wizard.py
+++ b/trytond/web_service/wizard.py
@@ -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)
diff --git a/trytond/wizard/wizard.py b/trytond/wizard/wizard.py
index 206ed7faf7..b646e5d8bf 100644
--- a/trytond/wizard/wizard.py
+++ b/trytond/wizard/wizard.py
@@ -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