[FIX] Use the copy version from Python 2.5
[odoo/odoo.git] / bin / wizard / __init__.py
1 # -*- encoding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution   
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
6 #    $Id$
7 #
8 #    This program is free software: you can redistribute it and/or modify
9 #    it under the terms of the GNU General Public License as published by
10 #    the Free Software Foundation, either version 3 of the License, or
11 #    (at your option) any later version.
12 #
13 #    This program is distributed in the hope that it will be useful,
14 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
15 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 #    GNU General Public License for more details.
17 #
18 #    You should have received a copy of the GNU General Public License
19 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 #
21 ##############################################################################
22
23 import netsvc
24 from tools import copy
25 from tools.misc import UpdateableStr, UpdateableDict
26 from tools.translate import translate
27 from xml import dom
28
29 import ir
30 import pooler
31
32 from osv.osv import except_osv
33 from osv.orm import except_orm
34 from netsvc import Logger, LOG_ERROR
35 import sys
36
37 class except_wizard(Exception):
38     def __init__(self, name, value):
39         self.name = name
40         self.value = value
41         self.args = (name, value)
42
43 class interface(netsvc.Service):
44     states = {}
45     
46     def __init__(self, name):
47         assert not netsvc.service_exist('wizard.'+name), 'The wizard "%s" already exists!'%name
48         super(interface, self).__init__('wizard.'+name)
49         self.exportMethod(self.execute)
50         self.wiz_name = name
51         
52     def translate_view(self, cr, node, state, lang):
53         if node.nodeType == node.ELEMENT_NODE:
54             if node.hasAttribute('string') and node.getAttribute('string'):
55                 trans = translate(cr, self.wiz_name+','+state, 'wizard_view', lang, node.getAttribute('string').encode('utf8'))
56                 if trans:
57                     node.setAttribute('string', trans)
58         for n in node.childNodes:
59             self.translate_view(cr, n, state, lang)
60
61     def execute_cr(self, cr, uid, data, state='init', context=None):
62         if not context:
63             context={}
64         res = {}
65         try:
66             state_def = self.states[state]
67
68             result_def = state_def.get('result', {})
69             
70             actions_res = {}
71             # iterate through the list of actions defined for this state
72             for action in state_def.get('actions', []):
73                 # execute them
74                 action_res = action(self, cr, uid, data, context)
75                 assert isinstance(action_res, dict), 'The return value of wizard actions should be a dictionary'
76                 actions_res.update(action_res)
77                 
78             res = copy.copy(result_def)
79             res['datas'] = actions_res
80             
81             lang = context.get('lang', False)
82             if result_def['type'] == 'action':
83                 res['action'] = result_def['action'](self, cr, uid, data, context)
84             elif result_def['type'] == 'form':
85                 fields = copy.deepcopy(result_def['fields'])
86                 arch = copy.copy(result_def['arch'])
87                 button_list = copy.copy(result_def['state'])
88
89                 if isinstance(fields, UpdateableDict):
90                     fields = fields.dict
91                 if isinstance(arch, UpdateableStr):
92                     arch = arch.string
93
94                 # fetch user-set defaut values for the field... shouldn't we pass it the uid?
95                 defaults = ir.ir_get(cr, uid, 'default', False, [('wizard.'+self.wiz_name, False)])
96                 default_values = dict([(x[1], x[2]) for x in defaults])
97                 for val in fields.keys():
98                     if 'default' in fields[val]:
99                         # execute default method for this field
100                         if callable(fields[val]['default']):
101                             fields[val]['value'] = fields[val]['default'](uid, data, state)
102                         else:
103                             fields[val]['value'] = fields[val]['default']
104                         del fields[val]['default']
105                     else:
106                         # if user has set a default value for the field, use it
107                         if val in default_values:
108                             fields[val]['value'] = default_values[val]
109                     if 'selection' in fields[val]:
110                         if not isinstance(fields[val]['selection'], (tuple, list)):
111                             fields[val] = copy.copy(fields[val])
112                             fields[val]['selection'] = fields[val]['selection'](self, cr, uid, context)
113
114                 if lang:
115                     # translate fields
116                     for field in fields:
117                         res_name = "%s,%s,%s" % (self.wiz_name, state, field)
118
119                         trans = translate(cr, res_name, 'wizard_field', lang)
120                         if trans:
121                             fields[field]['string'] = trans
122                         
123                         if 'selection' in fields[field]:
124                             trans = lambda x: translate(cr, res_name, 'selection', lang, x) or x
125                             for idx, (key, val) in enumerate(fields[field]['selection']):
126                                 fields[field]['selection'][idx] = (key, trans(val))
127
128                         if 'help' in fields[field]:
129                             t = translate(cr, res_name, 'help', lang, fields[field]['help']) 
130                             if t:
131                                 fields[field]['help'] = t
132
133                     # translate arch
134                     if not isinstance(arch, UpdateableStr):
135                         doc = dom.minidom.parseString(arch)
136                         self.translate_view(cr, doc, state, lang)
137                         arch = doc.toxml()
138
139                     # translate buttons
140                     button_list = list(button_list)
141                     for i, aa  in enumerate(button_list):
142                         button_name = aa[0]
143                         trans = translate(cr, self.wiz_name+','+state+','+button_name, 'wizard_button', lang)
144                         if trans:
145                             aa = list(aa)
146                             aa[1] = trans
147                             button_list[i] = aa
148                     
149                 res['fields'] = fields
150                 res['arch'] = arch
151                 res['state'] = button_list
152
153         except Exception, e:
154             if isinstance(e, except_wizard) \
155                 or isinstance(e, except_osv) \
156                 or isinstance(e, except_orm):
157                 self.abortResponse(2, e.name, 'warning', e.value)
158             else:
159                 import traceback
160                 tb_s = reduce(lambda x, y: x+y, traceback.format_exception(
161                     sys.exc_type, sys.exc_value, sys.exc_traceback))
162                 logger = Logger()
163                 logger.notifyChannel("web-services", LOG_ERROR,
164                         'Exception in call: ' + tb_s)
165                 raise
166
167         if result_def['type'] == 'choice':
168             next_state = result_def['next_state'](self, cr, uid, data, context)
169             return self.execute_cr(cr, uid, data, next_state, context)
170         return res
171
172     def execute(self, db, uid, data, state='init', context=None):
173         if not context:
174             context={}
175         cr = pooler.get_db(db).cursor()
176         try:
177             try:
178                 res = self.execute_cr(cr, uid, data, state, context)
179                 cr.commit()
180             except Exception:
181                 cr.rollback()
182                 raise
183         finally:
184             cr.close()
185         return res
186
187 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
188