[IMP] add new handler for addons configuration, corresponding menu item
[odoo/odoo.git] / bin / addons / base / ir / ir_actions.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Management Solution
5 #    Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 from osv import fields,osv
23 import tools
24 import time
25 from tools.config import config
26 from tools.translate import _
27 import netsvc
28 import re
29 import copy
30 import sys
31 from xml import dom
32
33 class actions(osv.osv):
34     _name = 'ir.actions.actions'
35     _table = 'ir_actions'
36     _columns = {
37         'name': fields.char('Action Name', required=True, size=64),
38         'type': fields.char('Action Type', required=True, size=32),
39         'usage': fields.char('Action Usage', size=32),
40     }
41     _defaults = {
42         'usage': lambda *a: False,
43     }
44 actions()
45
46 class report_custom(osv.osv):
47     _name = 'ir.actions.report.custom'
48     _table = 'ir_act_report_custom'
49     _sequence = 'ir_actions_id_seq'
50     _columns = {
51         'name': fields.char('Report Name', size=64, required=True, translate=True),
52         'type': fields.char('Report Type', size=32, required=True),
53         'model':fields.char('Object', size=64, required=True),
54         'report_id': fields.integer('Report Ref.', required=True),
55         'usage': fields.char('Action Usage', size=32),
56         'multi': fields.boolean('On multiple doc.', help="If set to true, the action will not be displayed on the right toolbar of a form view.")
57     }
58     _defaults = {
59         'multi': lambda *a: False,
60         'type': lambda *a: 'ir.actions.report.custom',
61     }
62 report_custom()
63
64 class report_xml(osv.osv):
65
66     def _report_content(self, cursor, user, ids, name, arg, context=None):
67         res = {}
68         for report in self.browse(cursor, user, ids, context=context):
69             data = report[name + '_data']
70             if not data and report[name[:-8]]:
71                 try:
72                     fp = tools.file_open(report[name[:-8]], mode='rb')
73                     data = fp.read()
74                 except:
75                     data = False
76             res[report.id] = data
77         return res
78
79     def _report_content_inv(self, cursor, user, id, name, value, arg, context=None):
80         self.write(cursor, user, id, {name+'_data': value}, context=context)
81
82     def _report_sxw(self, cursor, user, ids, name, arg, context=None):
83         res = {}
84         for report in self.browse(cursor, user, ids, context=context):
85             if report.report_rml:
86                 res[report.id] = report.report_rml.replace('.rml', '.sxw')
87             else:
88                 res[report.id] = False
89         return res
90
91     _name = 'ir.actions.report.xml'
92     _table = 'ir_act_report_xml'
93     _sequence = 'ir_actions_id_seq'
94     _columns = {
95         'name': fields.char('Name', size=64, required=True, translate=True),
96         'type': fields.char('Report Type', size=32, required=True),
97         'model': fields.char('Object', size=64, required=True),
98         'report_name': fields.char('Internal Name', size=64, required=True),
99         'report_xsl': fields.char('XSL path', size=256),
100         'report_xml': fields.char('XML path', size=256),
101         'report_rml': fields.char('RML path', size=256,
102             help="The .rml path of the file or NULL if the content is in report_rml_content"),
103         'report_sxw': fields.function(_report_sxw, method=True, type='char',
104             string='SXW path'),
105         'report_sxw_content_data': fields.binary('SXW content'),
106         'report_rml_content_data': fields.binary('RML content'),
107         'report_sxw_content': fields.function(_report_content,
108             fnct_inv=_report_content_inv, method=True,
109             type='binary', string='SXW content',),
110         'report_rml_content': fields.function(_report_content,
111             fnct_inv=_report_content_inv, method=True,
112             type='binary', string='RML content'),
113         'auto': fields.boolean('Automatic XSL:RML', required=True),
114         'usage': fields.char('Action Usage', size=32),
115         'header': fields.boolean('Add RML header',
116             help="Add or not the coporate RML header"),
117         'multi': fields.boolean('On multiple doc.',
118             help="If set to true, the action will not be displayed on the right toolbar of a form view."),
119         'report_type': fields.selection([
120             ('pdf', 'pdf'),
121             ('html', 'html'),
122             ('raw', 'raw'),
123             ('sxw', 'sxw'),
124             ('txt', 'txt'),
125             ('odt', 'odt'),
126             ('html2html','HTML from HTML'),
127             ('mako2html','HTML from HTML(Mako)'),
128             ], string='Type', required=True),
129         'groups_id': fields.many2many('res.groups', 'res_groups_report_rel', 'uid', 'gid', 'Groups'),
130         'attachment': fields.char('Save As Attachment Prefix', size=128, help='This is the filename of the attachment used to store the printing result. Keep empty to not save the printed reports. You can use a python expression with the object and time variables.'),
131         'attachment_use': fields.boolean('Reload from Attachment', help='If you check this, then the second time the user prints with same attachment name, it returns the previous report.')
132     }
133     _defaults = {
134         'type': lambda *a: 'ir.actions.report.xml',
135         'multi': lambda *a: False,
136         'auto': lambda *a: True,
137         'header': lambda *a: True,
138         'report_sxw_content': lambda *a: False,
139         'report_type': lambda *a: 'pdf',
140         'attachment': lambda *a: False,
141     }
142
143 report_xml()
144
145 class act_window(osv.osv):
146     _name = 'ir.actions.act_window'
147     _table = 'ir_act_window'
148     _sequence = 'ir_actions_id_seq'
149
150     def _check_model(self, cr, uid, ids, context={}):
151         for action in self.browse(cr, uid, ids, context):
152             if not self.pool.get(action.res_model):
153                 return False
154             if action.src_model and not self.pool.get(action.src_model):
155                 return False
156         return True
157     _constraints = [
158         (_check_model, 'Invalid model name in the action definition.', ['res_model','src_model'])
159     ]
160
161     def get_filters(self, cr, uid, model):
162         cr.execute('select id from ir_act_window a where a.id not in (select act_id from ir_act_window_user_rel) and a.res_model=\''+model+'\' and a.filter=\'1\';')
163         all_ids = cr.fetchall()
164         filter_ids =  map(lambda x:x[0],all_ids)
165         act_ids = self.search(cr,uid,[('res_model','=',model),('filter','=',1),('default_user_ids','in',(','.join(map(str,[uid,])),))])
166         act_ids += filter_ids
167         act_ids = list(set(act_ids))
168         my_acts = self.read(cr, uid, act_ids, ['name', 'domain'])
169         return my_acts
170
171     def _views_get_fnc(self, cr, uid, ids, name, arg, context={}):
172         res={}
173         for act in self.browse(cr, uid, ids):
174             res[act.id]=[(view.view_id.id, view.view_mode) for view in act.view_ids]
175             modes = act.view_mode.split(',')
176             if len(modes)>len(act.view_ids):
177                 find = False
178                 if act.view_id:
179                     res[act.id].append((act.view_id.id, act.view_id.type))
180                 for t in modes[len(act.view_ids):]:
181                     if act.view_id and (t == act.view_id.type) and not find:
182                         find = True
183                         continue
184                     res[act.id].append((False, t))
185         return res
186
187     def _search_view(self, cr, uid, ids, name, arg, context={}):
188         res = {}
189         def encode(s):
190             if isinstance(s, unicode):
191                 return s.encode('utf8')
192             return s
193         for act in self.browse(cr, uid, ids):
194             fields_from_fields_get = self.pool.get(act.res_model).fields_get(cr, uid)
195             if act.search_view_id:
196                 field_get = self.pool.get(act.res_model).fields_view_get(cr, uid, act.search_view_id.id, 'search', context)
197                 fields_from_fields_get.update(field_get['fields'])
198                 field_get['fields'] = fields_from_fields_get
199                 res[act.id] = str(field_get)
200             else:
201                 def process_child(node, new_node, doc):
202                     for child in node.childNodes:
203                         if child.localName=='field' and child.hasAttribute('select') and child.getAttribute('select')=='1':
204                             if child.childNodes:
205                                 fld = doc.createElement('field')
206                                 for attr in child.attributes.keys():
207                                     fld.setAttribute(attr, child.getAttribute(attr))
208                                 new_node.appendChild(fld)
209                             else:
210                                 new_node.appendChild(child)
211                         elif child.localName in ('page','group','notebook'):
212                             process_child(child, new_node, doc)
213
214                 form_arch = self.pool.get(act.res_model).fields_view_get(cr, uid, False, 'form', context)
215                 dom_arc = dom.minidom.parseString(encode(form_arch['arch']))
216                 new_node = copy.deepcopy(dom_arc)
217                 for child_node in new_node.childNodes[0].childNodes:
218                     if child_node.nodeType == child_node.ELEMENT_NODE:
219                         new_node.childNodes[0].removeChild(child_node)
220                 process_child(dom_arc.childNodes[0],new_node.childNodes[0],dom_arc)
221
222                 form_arch['arch'] = new_node.toxml()
223                 form_arch['fields'].update(fields_from_fields_get)
224                 res[act.id] = str(form_arch)
225         return res
226
227     _columns = {
228         'name': fields.char('Action Name', size=64, translate=True),
229         'type': fields.char('Action Type', size=32, required=True),
230         'view_id': fields.many2one('ir.ui.view', 'View Ref.', ondelete='cascade'),
231         'domain': fields.char('Domain Value', size=250),
232         'context': fields.char('Context Value', size=250),
233         'res_model': fields.char('Object', size=64),
234         'src_model': fields.char('Source Object', size=64),
235         'target': fields.selection([('current','Current Window'),('new','New Window')], 'Target Window'),
236         'view_type': fields.selection((('tree','Tree'),('form','Form')),string='View Type'),
237         'view_mode': fields.char('View Mode', size=250),
238         'usage': fields.char('Action Usage', size=32),
239         'view_ids': fields.one2many('ir.actions.act_window.view', 'act_window_id', 'Views'),
240         'views': fields.function(_views_get_fnc, method=True, type='binary', string='Views'),
241         'limit': fields.integer('Limit', help='Default limit for the list view'),
242         'auto_refresh': fields.integer('Auto-Refresh',
243             help='Add an auto-refresh on the view'),
244         'groups_id': fields.many2many('res.groups', 'ir_act_window_group_rel',
245             'act_id', 'gid', 'Groups'),
246         'search_view_id': fields.many2one('ir.ui.view', 'Search View Ref.'),
247         'filter': fields.boolean('Filter'),
248         'default_user_ids': fields.many2many('res.users', 'ir_act_window_user_rel', 'act_id', 'uid', 'Users'),
249         'search_view' : fields.function(_search_view, type='text', method=True, string='Search View'),
250         'menus': fields.char('Menus', size=4096)
251     }
252     _defaults = {
253         'type': lambda *a: 'ir.actions.act_window',
254         'view_type': lambda *a: 'form',
255         'view_mode': lambda *a: 'tree,form',
256         'context': lambda *a: '{}',
257         'limit': lambda *a: 80,
258         'target': lambda *a: 'current',
259         'auto_refresh': lambda *a: 0,
260     }
261 act_window()
262
263 class act_window_view(osv.osv):
264     _name = 'ir.actions.act_window.view'
265     _table = 'ir_act_window_view'
266     _rec_name = 'view_id'
267     _columns = {
268         'sequence': fields.integer('Sequence'),
269         'view_id': fields.many2one('ir.ui.view', 'View'),
270         'view_mode': fields.selection((
271             ('tree', 'Tree'),
272             ('form', 'Form'),
273             ('graph', 'Graph'),
274             ('calendar', 'Calendar'),
275             ('gantt', 'Gantt')), string='View Type', required=True),
276         'act_window_id': fields.many2one('ir.actions.act_window', 'Action', ondelete='cascade'),
277         'multi': fields.boolean('On Multiple Doc.',
278             help="If set to true, the action will not be displayed on the right toolbar of a form view."),
279     }
280     _defaults = {
281         'multi': lambda *a: False,
282     }
283     _order = 'sequence'
284 act_window_view()
285
286 class act_wizard(osv.osv):
287     _name = 'ir.actions.wizard'
288     _inherit = 'ir.actions.actions'
289     _table = 'ir_act_wizard'
290     _sequence = 'ir_actions_id_seq'
291     _columns = {
292         'name': fields.char('Wizard Info', size=64, required=True, translate=True),
293         'type': fields.char('Action Type', size=32, required=True),
294         'wiz_name': fields.char('Wizard Name', size=64, required=True),
295         'multi': fields.boolean('Action on Multiple Doc.', help="If set to true, the wizard will not be displayed on the right toolbar of a form view."),
296         'groups_id': fields.many2many('res.groups', 'res_groups_wizard_rel', 'uid', 'gid', 'Groups'),
297         'model': fields.char('Object', size=64),
298     }
299     _defaults = {
300         'type': lambda *a: 'ir.actions.wizard',
301         'multi': lambda *a: False,
302     }
303 act_wizard()
304
305 class act_url(osv.osv):
306     _name = 'ir.actions.url'
307     _table = 'ir_act_url'
308     _sequence = 'ir_actions_id_seq'
309     _columns = {
310         'name': fields.char('Action Name', size=64, translate=True),
311         'type': fields.char('Action Type', size=32, required=True),
312         'url': fields.text('Action URL',required=True),
313         'target': fields.selection((
314             ('new', 'New Window'),
315             ('self', 'This Window')),
316             'Action Target', required=True
317         )
318     }
319     _defaults = {
320         'type': lambda *a: 'ir.actions.act_url',
321         'target': lambda *a: 'new'
322     }
323 act_url()
324
325 def model_get(self, cr, uid, context={}):
326     wkf_pool = self.pool.get('workflow')
327     ids = wkf_pool.search(cr, uid, [])
328     osvs = wkf_pool.read(cr, uid, ids, ['osv'])
329
330     res = []
331     mpool = self.pool.get('ir.model')
332     for osv in osvs:
333         model = osv.get('osv')
334         id = mpool.search(cr, uid, [('model','=',model)])
335         name = mpool.read(cr, uid, id)[0]['name']
336         res.append((model, name))
337
338     return res
339
340 class ir_model_fields(osv.osv):
341     _inherit = 'ir.model.fields'
342     _rec_name = 'field_description'
343     _columns = {
344         'complete_name': fields.char('Complete Name', size=64, select=1),
345     }
346 ir_model_fields()
347
348 class server_object_lines(osv.osv):
349     _name = 'ir.server.object.lines'
350     _sequence = 'ir_actions_id_seq'
351     _columns = {
352         'server_id': fields.many2one('ir.actions.server', 'Object Mapping'),
353         'col1': fields.many2one('ir.model.fields', 'Destination', required=True),
354         'value': fields.text('Value', required=True),
355         'type': fields.selection([
356             ('value','Value'),
357             ('equation','Formula')
358         ], 'Type', required=True, size=32, change_default=True),
359     }
360     _defaults = {
361         'type': lambda *a: 'equation',
362     }
363 server_object_lines()
364
365 ##
366 # Actions that are run on the server side
367 #
368 class actions_server(osv.osv):
369
370     def _select_signals(self, cr, uid, context={}):
371         cr.execute("select distinct t.signal as key, t.signal || ' - [ ' || w.osv || ' ] ' as val from wkf w, wkf_activity a, wkf_transition t "\
372                         " where w.id = a.wkf_id " \
373                         " and t.act_from = a.id " \
374                         " or t.act_to = a.id and t.signal not in (null, NULL)")
375         result = cr.fetchall() or []
376         res = []
377         for rs in result:
378             if rs[0] is not None and rs[1] is not None:
379                 res.append(rs)
380         return res
381
382     _name = 'ir.actions.server'
383     _table = 'ir_act_server'
384     _sequence = 'ir_actions_id_seq'
385     _order = 'sequence'
386     _columns = {
387         'name': fields.char('Action Name', required=True, size=64, help="Easy to Refer action by name e.g. One Sales Order -> Many Invoices", translate=True),
388         'condition' : fields.char('Condition', size=256, required=True, help="Condition that is to be tested before action is executed, e.g. object.list_price > object.cost_price"),
389         'state': fields.selection([
390             ('client_action','Client Action'),
391             ('dummy','Dummy'),
392             ('loop','Iteration'),
393             ('code','Python Code'),
394             ('trigger','Trigger'),
395             ('email','Email'),
396             ('sms','SMS'),
397             ('object_create','Create Object'),
398             ('object_write','Write Object'),
399             ('other','Multi Actions'),
400         ], 'Action Type', required=True, size=32, help="Type of the Action that is to be executed"),
401         'code':fields.text('Python Code', help="Python code to be executed"),
402         'sequence': fields.integer('Sequence', help="Important when you deal with multiple actions, the execution order will be decided based on this, low number is higher priority."),
403         'model_id': fields.many2one('ir.model', 'Object', required=True, help="Select the object on which the action will work (read, write, create)."),
404         'action_id': fields.many2one('ir.actions.actions', 'Client Action', help="Select the Action Window, Report, Wizard to be executed."),
405         'trigger_name': fields.selection(_select_signals, string='Trigger Name', size=128, help="Select the Signal name that is to be used as the trigger."),
406         'wkf_model_id': fields.many2one('ir.model', 'Workflow On', help="Workflow to be executed on this model."),
407         'trigger_obj_id': fields.many2one('ir.model.fields','Trigger On', help="Select the object from the model on which the workflow will executed."),
408         'email': fields.char('Email Address', size=512, help="Provides the fields that will be used to fetch the email address, e.g. when you select the invoice, then `object.invoice_address_id.email` is the field which gives the correct address"),
409         'subject': fields.char('Subject', size=1024, translate=True, help="Specify the subject. You can use fields from the object, e.g. `Hello [[ object.partner_id.name ]]`"),
410         'message': fields.text('Message', translate=True, help="Specify the message. You can use the fields from the object. e.g. `Dear [[ object.partner_id.name ]]`"),
411         'mobile': fields.char('Mobile No', size=512, help="Provides fields that be used to fetch the mobile number, e.g. you select the invoice, then `object.invoice_address_id.mobile` is the field which gives the correct mobile number"),
412         'sms': fields.char('SMS', size=160, translate=True),
413         'child_ids': fields.many2many('ir.actions.server', 'rel_server_actions', 'server_id', 'action_id', 'Other Actions'),
414         'usage': fields.char('Action Usage', size=32),
415         'type': fields.char('Action Type', size=32, required=True),
416         'srcmodel_id': fields.many2one('ir.model', 'Model', help="Object in which you want to create / write the object. If it is empty then refer to the Object field."),
417         'fields_lines': fields.one2many('ir.server.object.lines', 'server_id', 'Field Mappings.'),
418         'record_id':fields.many2one('ir.model.fields', 'Create Id', help="Provide the field name where the record id is stored after the create operations. If it is empty, you can not track the new record."),
419         'write_id':fields.char('Write Id', size=256, help="Provide the field name that the record id refers to for the write operation. If it is empty it will refer to the active id of the object."),
420         'loop_action':fields.many2one('ir.actions.server', 'Loop Action', help="Select the action that will be executed. Loop action will not be avaliable inside loop."),
421         'expression':fields.char('Loop Expression', size=512, help="Enter the field/expression that will return the list. E.g. select the sale order in Object, and you can have loop on the sales order line. Expression = `object.order_line`."),
422     }
423     _defaults = {
424         'state': lambda *a: 'dummy',
425         'condition': lambda *a: 'True',
426         'type': lambda *a: 'ir.actions.server',
427         'sequence': lambda *a: 5,
428         'code': lambda *a: """# You can use the following variables
429 #    - object
430 #    - object2
431 #    - time
432 #    - cr
433 #    - uid
434 #    - ids
435 # If you plan to return an action, assign: action = {...}
436 """,
437     }
438
439     def get_email(self, cr, uid, action, context):
440         logger = netsvc.Logger()
441         obj_pool = self.pool.get(action.model_id.model)
442         id = context.get('active_id')
443         obj = obj_pool.browse(cr, uid, id)
444
445         fields = None
446
447         if '/' in action.email.complete_name:
448             fields = action.email.complete_name.split('/')
449         elif '.' in action.email.complete_name:
450             fields = action.email.complete_name.split('.')
451
452         for field in fields:
453             try:
454                 obj = getattr(obj, field)
455             except Exception,e :
456                 logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
457
458         return obj
459
460     def get_mobile(self, cr, uid, action, context):
461         logger = netsvc.Logger()
462         obj_pool = self.pool.get(action.model_id.model)
463         id = context.get('active_id')
464         obj = obj_pool.browse(cr, uid, id)
465
466         fields = None
467
468         if '/' in action.mobile.complete_name:
469             fields = action.mobile.complete_name.split('/')
470         elif '.' in action.mobile.complete_name:
471             fields = action.mobile.complete_name.split('.')
472
473         for field in fields:
474             try:
475                 obj = getattr(obj, field)
476             except Exception,e :
477                 logger.notifyChannel('Workflow', netsvc.LOG_ERROR, 'Failed to parse : %s' % (field))
478
479         return obj
480
481     def merge_message(self, cr, uid, keystr, action, context):
482         logger = netsvc.Logger()
483         def merge(match):
484             obj_pool = self.pool.get(action.model_id.model)
485             id = context.get('active_id')
486             obj = obj_pool.browse(cr, uid, id)
487             exp = str(match.group()[2:-2]).strip()
488             result = eval(exp, {'object':obj, 'context': context,'time':time})
489             if result in (None, False):
490                 return str("--------")
491             return str(result)
492
493         com = re.compile('(\[\[.+?\]\])')
494         message = com.sub(merge, keystr)
495
496         return message
497
498     # Context should contains:
499     #   ids : original ids
500     #   id  : current id of the object
501     # OUT:
502     #   False : Finnished correctly
503     #   ACTION_ID : Action to launch
504
505     def run(self, cr, uid, ids, context={}):
506         logger = netsvc.Logger()
507
508         for action in self.browse(cr, uid, ids, context):
509             obj_pool = self.pool.get(action.model_id.model)
510             obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
511             cxt = {
512                 'context':context,
513                 'object': obj,
514                 'time':time,
515                 'cr': cr,
516                 'pool' : self.pool,
517                 'uid' : uid
518             }
519             expr = eval(str(action.condition), cxt)
520             if not expr:
521                 continue
522
523             if action.state=='client_action':
524                 if not action.action_id:
525                     raise osv.except_osv(_('Error'), _("Please specify an action to launch !"))
526                 return self.pool.get(action.action_id.type)\
527                     .read(cr, uid, action.action_id.id, context=context)
528
529             if action.state == 'code':
530                 localdict = {
531                     'self': self.pool.get(action.model_id.model),
532                     'context': context,
533                     'time': time,
534                     'ids': ids,
535                     'cr': cr,
536                     'uid': uid,
537                     'obj':obj
538                 }
539                 exec action.code in localdict
540                 if 'action' in localdict:
541                     return localdict['action']
542
543             if action.state == 'email':
544                 user = config['email_from']
545                 address = str(action.email)
546                 try:
547                     address =  eval(str(action.email), cxt)
548                 except:
549                     pass
550
551                 if not address:
552                     logger.notifyChannel('email', netsvc.LOG_INFO, 'Partner Email address not Specified!')
553                     continue
554                 if not user:
555                     raise osv.except_osv(_('Error'), _("Please specify server option --smtp-from !"))
556
557                 subject = self.merge_message(cr, uid, str(action.subject), action, context)
558                 body = self.merge_message(cr, uid, str(action.message), action, context)
559
560                 if tools.email_send(user, [address], subject, body, debug=False, subtype='html') == True:
561                     logger.notifyChannel('email', netsvc.LOG_INFO, 'Email successfully send to : %s' % (address))
562                 else:
563                     logger.notifyChannel('email', netsvc.LOG_ERROR, 'Failed to send email to : %s' % (address))
564
565             if action.state == 'trigger':
566                 wf_service = netsvc.LocalService("workflow")
567                 model = action.wkf_model_id.model
568                 obj_pool = self.pool.get(action.model_id.model)
569                 res_id = self.pool.get(action.model_id.model).read(cr, uid, [context.get('active_id')], [action.trigger_obj_id.name])
570                 id = res_id [0][action.trigger_obj_id.name]
571                 wf_service.trg_validate(uid, model, int(id), action.trigger_name, cr)
572
573             if action.state == 'sms':
574                 #TODO: set the user and password from the system
575                 # for the sms gateway user / password
576                 api_id = ''
577                 text = action.sms
578                 to = self.get_mobile(cr, uid, action, context)
579                 #TODO: Apply message mearge with the field
580                 if tools.sms_send(user, password, api_id, text, to) == True:
581                     logger.notifyChannel('sms', netsvc.LOG_INFO, 'SMS successfully send to : %s' % (action.address))
582                 else:
583                     logger.notifyChannel('sms', netsvc.LOG_ERROR, 'Failed to send SMS to : %s' % (action.address))
584
585             if action.state == 'other':
586                 res = []
587                 for act in action.child_ids:
588                     context['active_id'] = context['active_ids'][0]
589                     result = self.run(cr, uid, [act.id], context)
590                     if result:
591                         res.append(result)
592
593                 return res
594
595             if action.state == 'loop':
596                 obj_pool = self.pool.get(action.model_id.model)
597                 obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
598                 cxt = {
599                     'context':context,
600                     'object': obj,
601                     'time':time,
602                     'cr': cr,
603                     'pool' : self.pool,
604                     'uid' : uid
605                 }
606                 expr = eval(str(action.expression), cxt)
607                 context['object'] = obj
608                 for i in expr:
609                     context['active_id'] = i.id
610                     result = self.run(cr, uid, [action.loop_action.id], context)
611
612             if action.state == 'object_write':
613                 res = {}
614                 for exp in action.fields_lines:
615                     euq = exp.value
616                     if exp.type == 'equation':
617                         obj_pool = self.pool.get(action.model_id.model)
618                         obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
619                         cxt = {'context':context, 'object': obj, 'time':time}
620                         expr = eval(euq, cxt)
621                     else:
622                         expr = exp.value
623                     res[exp.col1.name] = expr
624
625                 if not action.write_id:
626                     if not action.srcmodel_id:
627                         obj_pool = self.pool.get(action.model_id.model)
628                         obj_pool.write(cr, uid, [context.get('active_id')], res)
629                     else:
630                         write_id = context.get('active_id')
631                         obj_pool = self.pool.get(action.srcmodel_id.model)
632                         obj_pool.write(cr, uid, [write_id], res)
633
634                 elif action.write_id:
635                     obj_pool = self.pool.get(action.srcmodel_id.model)
636                     rec = self.pool.get(action.model_id.model).browse(cr, uid, context.get('active_id'))
637                     id = eval(action.write_id, {'object': rec})
638                     try:
639                         id = int(id)
640                     except:
641                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
642
643                     if type(id) != type(1):
644                         raise osv.except_osv(_('Error'), _("Problem in configuration `Record Id` in Server Action!"))
645                     write_id = id
646                     obj_pool.write(cr, uid, [write_id], res)
647
648             if action.state == 'object_create':
649                 res = {}
650                 for exp in action.fields_lines:
651                     euq = exp.value
652                     if exp.type == 'equation':
653                         obj_pool = self.pool.get(action.model_id.model)
654                         obj = obj_pool.browse(cr, uid, context['active_id'], context=context)
655                         expr = eval(euq, {'context':context, 'object': obj, 'time':time})
656                     else:
657                         expr = exp.value
658                     res[exp.col1.name] = expr
659
660                 obj_pool = None
661                 res_id = False
662                 obj_pool = self.pool.get(action.srcmodel_id.model)
663                 res_id = obj_pool.create(cr, uid, res)
664                 cr.commit()
665                 if action.record_id:
666                     self.pool.get(action.model_id.model).write(cr, uid, [context.get('active_id')], {action.record_id.name:res_id})
667
668         return False
669
670 actions_server()
671
672 class act_window_close(osv.osv):
673     _name = 'ir.actions.act_window_close'
674     _inherit = 'ir.actions.actions'
675     _table = 'ir_actions'
676     _defaults = {
677         'type': lambda *a: 'ir.actions.act_window_close',
678     }
679 act_window_close()
680
681 # This model use to register action services.
682 # if action type is 'configure', it will be start on configuration wizard.
683 # if action type is 'service',
684 #                - if start_type= 'at once', it will be start at one time on start date
685 #                - if start_type='auto', it will be start on auto starting from start date, and stop on stop date
686 #                - if start_type="manual", it will start and stop on manually
687 class ir_actions_todo(osv.osv):
688     _name = 'ir.actions.todo'
689     _columns={
690         'name':fields.char('Name',size=64,required=True, select=True),
691         'note':fields.text('Text', translate=True),
692         'start_date': fields.datetime('Start Date'),
693         'end_date': fields.datetime('End Date'),
694         'action_id':fields.many2one('ir.actions.act_window', 'Action', select=True,required=True, ondelete='cascade'),
695         'sequence':fields.integer('Sequence'),
696         'active': fields.boolean('Active'),
697         'type':fields.selection([('configure', 'Configure'),('service', 'Service'),('other','Other')], string='Type', required=True),
698         'start_on':fields.selection([('at_once', 'At Once'),('auto', 'Auto'),('manual','Manual')], string='Start On'),
699         'groups_id': fields.many2many('res.groups', 'res_groups_act_todo_rel', 'act_todo_id', 'group_id', 'Groups'),
700         'users_id': fields.many2many('res.users', 'res_users_act_todo_rel', 'act_todo_id', 'user_id', 'Users'),
701         'state':fields.selection([('open', 'Not Started'),('done', 'Done'),('skip','Skipped'),('cancel','Cancel')], string='State', required=True)
702     }
703     _defaults={
704         'state': lambda *a: 'open',
705         'sequence': lambda *a: 10,
706         'active':lambda *a:True,
707         'type':lambda *a:'configure'
708     }
709     _order="sequence"
710 ir_actions_todo()
711
712 # This model to use run all configuration actions
713 class ir_actions_configuration_wizard(osv.osv_memory):
714     _name='ir.actions.configuration.wizard'
715     def next_configuration_action(self,cr,uid,context={}):
716         item_obj = self.pool.get('ir.actions.todo')
717         item_ids = item_obj.search(cr, uid,
718                                    [('type','=','configure'),
719                                     ('state', '=', 'open'),
720                                     ('active','=',True)],
721                                    limit=1, context=context)
722         if item_ids:
723             return item_obj.browse(cr, uid, item_ids[0], context=context)
724         return False
725     def _get_action_name(self, cr, uid, context={}):
726         next_action = self.next_configuration_action(cr,uid,context=context)
727         if next_action:
728             return next_action.note
729         else:
730             return "Your database is now fully configured.\n\nClick 'Continue' and enjoy your OpenERP experience..."
731
732     def _get_action(self, cr, uid, context={}):
733         next_action = self.next_configuration_action(cr, uid, context=context)
734         if next_action:
735             return next_action.id
736         return
737
738     def _progress_get(self, cr, uid, context=None):
739         total = self.pool.get('ir.actions.todo')\
740             .search_count(cr, uid, [], context)
741         todo = self.pool.get('ir.actions.todo')\
742             .search_count(cr, uid,[('type','=','configure'),
743                                    ('active','=',True),
744                                    ('state','<>','open')],
745                           context)
746         if total > 0.0:
747             return max(5.0, round(todo*100/total))
748         else:
749             return 100.0
750
751     _columns = {
752         'name': fields.text('Next Wizard', readonly=True),
753         'progress': fields.float('Configuration Progress', readonly=True),
754         'item_id': fields.many2one('ir.actions.todo',
755                                   'Next Configuration Wizard',
756                                   invisible=True, readonly=True),
757     }
758     _defaults={
759         'progress': _progress_get,
760         'item_id': _get_action,
761         'name': _get_action_name,
762     }
763     def button_next(self,cr,uid,ids,context=None):
764         user_action = self.pool.get('res.users').browse(cr,uid,uid)
765         act_obj = self.pool.get(user_action.menu_id.type)
766         action_ids = act_obj.search(cr,uid,[
767                 ('name', '=', user_action.menu_id.name)])
768         action_open = act_obj.browse(cr, uid, action_ids)[0]
769         if context and 'menu' in context:
770             return {
771                 'view_type': action_open.view_type,
772                 'view_id': action_open.view_id and [action_open.view_id.id] or False,
773                 'res_model': action_open.res_model,
774                 'type': action_open.type,
775                 'domain': action_open.domain
776             }
777         return {'type': 'ir.actions.act_window_close'}
778
779     def button_skip(self,cr,uid,ids,context=None):
780         item_obj = self.pool.get('ir.actions.todo')
781         item_id = self.read(cr,uid,ids)[0]['item_id']
782         if item_id:
783             item = item_obj.browse(cr, uid, item_id, context=context)
784             item_obj.write(cr, uid, item.id, {
785                 'state': 'skip',
786                 }, context=context)
787             return {
788                 'view_type': 'form',
789                 "view_mode": 'form',
790                 'res_model': 'ir.actions.configuration.wizard',
791                 'type': 'ir.actions.act_window',
792                 'target':'new',
793             }
794         return self.button_next(cr, uid, ids, context)
795
796     def button_continue(self, cr, uid, ids, context=None):
797         item_obj = self.pool.get('ir.actions.todo')
798         item_id = self.read(cr,uid,ids)[0]['item_id']
799         if item_id:
800             item = item_obj.browse(cr, uid, item_id, context=context)
801             item_obj.write(cr, uid, item.id, {
802                 'state': 'done',
803                 }, context=context)
804             return {
805                   'view_mode': item.action_id.view_mode,
806                   'view_type': item.action_id.view_type,
807                   'view_id':item.action_id.view_id and [item.action_id.view_id.id] or False,
808                   'res_model': item.action_id.res_model,
809                   'type': item.action_id.type,
810                   'target':item.action_id.target,
811             }
812         return self.button_next(cr, uid, ids, context)
813 ir_actions_configuration_wizard()
814
815 class ir_actions_configurator(osv.osv_memory):
816     _name = 'ir.actions.configurator'
817     logger = netsvc.Logger()
818     def next_action(self, cr, uid, context=None):
819         todos = self.pool.get('ir.actions.todo')
820         self.logger.notifyChannel('actions', netsvc.LOG_INFO,
821                                   'getting next %s' % todos)
822         active_todos = todos.search(cr, uid,
823                                 [('type','=','configure'),
824                                  ('state', '=', 'open'),
825                                  ('active','=',True)],
826                                 limit=1, context=None)
827         if active_todos:
828             return todos.browse(cr, uid, active_todos[0], context=None)
829         return None
830
831     def next(self, cr, uid):
832         self.logger.notifyChannel('actions', netsvc.LOG_INFO,
833                                   'getting next operation')
834         next = self.next_action(cr, uid)
835         self.logger.notifyChannel('actions', netsvc.LOG_INFO,
836                                   'next action is %s' % next)
837         if next:
838             self.pool.get('ir.actions.todo').write(cr, uid, next.id, {
839                     'state':'done',
840                     }, context=None)
841             action = next.action_id
842             return {
843                 'view_mode': action.view_mode,
844                 'view_type': action.view_type,
845                 'view_id': action.view_id and [action.view_id.id] or False,
846                 'res_model': action.res_model,
847                 'type': action.type,
848                 'target': action.target,
849                 }
850         return {'type': 'ir.actions.act_window_close'}
851 ir_actions_configurator()
852 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
853