[FIX] Fixed context support for action loaded by menu
[odoo/odoo.git] / addons / base / controllers / main.py
1 # -*- coding: utf-8 -*-
2 import base64
3 import glob, os
4 import pprint
5 from xml.etree import ElementTree
6 from cStringIO import StringIO
7
8 import simplejson
9
10 import openerpweb
11 import openerpweb.ast
12 import openerpweb.nonliterals
13
14 import cherrypy
15
16 # Should move to openerpweb.Xml2Json
17 class Xml2Json:
18     # xml2json-direct
19     # Simple and straightforward XML-to-JSON converter in Python
20     # New BSD Licensed
21     #
22     # URL: http://code.google.com/p/xml2json-direct/
23     @staticmethod
24     def convert_to_json(s):
25         return simplejson.dumps(
26             Xml2Json.convert_to_structure(s), sort_keys=True, indent=4)
27
28     @staticmethod
29     def convert_to_structure(s):
30         root = ElementTree.fromstring(s)
31         return Xml2Json.convert_element(root)
32
33     @staticmethod
34     def convert_element(el, skip_whitespaces=True):
35         res = {}
36         if el.tag[0] == "{":
37             ns, name = el.tag.rsplit("}", 1)
38             res["tag"] = name
39             res["namespace"] = ns[1:]
40         else:
41             res["tag"] = el.tag
42         res["attrs"] = {}
43         for k, v in el.items():
44             res["attrs"][k] = v
45         kids = []
46         if el.text and (not skip_whitespaces or el.text.strip() != ''):
47             kids.append(el.text)
48         for kid in el:
49             kids.append(Xml2Json.convert_element(kid))
50             if kid.tail and (not skip_whitespaces or kid.tail.strip() != ''):
51                 kids.append(kid.tail)
52         res["children"] = kids
53         return res
54
55 #----------------------------------------------------------
56 # OpenERP Web base Controllers
57 #----------------------------------------------------------
58
59 class Session(openerpweb.Controller):
60     _cp_path = "/base/session"
61
62     def manifest_glob(self, addons, key):
63         files = []
64         for addon in addons:
65             globlist = openerpweb.addons_manifest.get(addon, {}).get(key, [])
66
67             files.extend([
68                 resource_path[len(openerpweb.path_addons):]
69                 for pattern in globlist
70                 for resource_path in glob.glob(os.path.join(
71                     openerpweb.path_addons, addon, pattern))
72             ])
73         return files
74
75     def concat_files(self, file_list):
76         """ Concatenate file content
77         return (concat,timestamp)
78         concat: concatenation of file content
79         timestamp: max(os.path.getmtime of file_list)
80         """
81         root = openerpweb.path_root
82         files_content = []
83         files_timestamp = 0
84         for i in file_list:
85             fname = os.path.join(root, i)
86             ftime = os.path.getmtime(fname)
87             if ftime > files_timestamp:
88                 files_timestamp = ftime
89             files_content = open(fname).read()
90         files_concat = "".join(files_content)
91         return files_concat
92
93     @openerpweb.jsonrequest
94     def login(self, req, db, login, password):
95         req.session.login(db, login, password)
96
97         return {
98             "session_id": req.session_id,
99             "uid": req.session._uid,
100         }
101
102     @openerpweb.jsonrequest
103     def sc_list(self, req):
104         return req.session.model('ir.ui.view_sc').get_sc(req.session._uid, "ir.ui.menu",
105                                                          req.session.eval_context(req.context))
106
107     @openerpweb.jsonrequest
108     def get_databases_list(self, req):
109         proxy = req.session.proxy("db")
110         dbs = proxy.list()
111         
112         return {"db_list": dbs}
113
114     @openerpweb.jsonrequest
115     def modules(self, req):
116         return {"modules": [name
117             for name, manifest in openerpweb.addons_manifest.iteritems()
118             if manifest.get('active', True)]}
119
120     @openerpweb.jsonrequest
121     def csslist(self, req, mods='base'):
122         return {'files': self.manifest_glob(mods.split(','), 'css')}
123
124     @openerpweb.jsonrequest
125     def jslist(self, req, mods='base'):
126         return {'files': self.manifest_glob(mods.split(','), 'js')}
127
128     def css(self, req, mods='base,base_hello'):
129         files = self.manifest_glob(mods.split(','), 'css')
130         concat = self.concat_files(files)[0]
131         # TODO request set the Date of last modif and Etag
132         return concat
133     css.exposed = True
134
135     def js(self, req, mods='base,base_hello'):
136         files = self.manifest_glob(mods.split(','), 'js')
137         concat = self.concat_files(files)[0]
138         # TODO request set the Date of last modif and Etag
139         return concat
140     js.exposed = True
141
142     @openerpweb.jsonrequest
143     def eval_domain_and_context(self, req, contexts, domains,
144                                 group_by_seq=None):
145         """ Evaluates sequences of domains and contexts, composing them into
146         a single context, domain or group_by sequence.
147
148         :param list contexts: list of contexts to merge together. Contexts are
149                               evaluated in sequence, all previous contexts
150                               are part of their own evaluation context
151                               (starting at the session context).
152         :param list domains: list of domains to merge together. Domains are
153                              evaluated in sequence and appended to one another
154                              (implicit AND), their evaluation domain is the
155                              result of merging all contexts.
156         :param list group_by_seq: list of domains (which may be in a different
157                                   order than the ``contexts`` parameter),
158                                   evaluated in sequence, their ``'group_by'``
159                                   key is extracted if they have one.
160         :returns:
161             a 3-dict of:
162
163             context (``dict``)
164                 the global context created by merging all of
165                 ``contexts``
166
167             domain (``list``)
168                 the concatenation of all domains
169
170             group_by (``list``)
171                 a list of fields to group by, potentially empty (in which case
172                 no group by should be performed)
173         """
174         context = req.session.eval_context(openerpweb.nonliterals.CompoundContext(*contexts))
175         domain = req.session.eval_domain(openerpweb.nonliterals.CompoundDomain(*(domains or [])), context)
176         
177         group_by_sequence = []
178         for candidate in (group_by_seq or []):
179             ctx = req.session.eval_context(candidate, context)
180             group_by = ctx.get('group_by')
181             if not group_by:
182                 continue
183             elif isinstance(group_by, basestring):
184                 group_by_sequence.append(group_by)
185             else:
186                 group_by_sequence.extend(group_by)
187         
188         return {
189             'context': context,
190             'domain': domain,
191             'group_by': group_by_sequence
192         }
193
194     @openerpweb.jsonrequest
195     def save_session_action(self, req, the_action):
196         """
197         This method store an action object in the session object and returns an integer
198         identifying that action. The method get_session_action() can be used to get
199         back the action.
200         
201         :param the_action: The action to save in the session.
202         :type the_action: anything
203         :return: A key identifying the saved action.
204         :rtype: integer
205         """
206         saved_actions = cherrypy.session.get('saved_actions')
207         if not saved_actions:
208             saved_actions = {"next":0, "actions":{}}
209             cherrypy.session['saved_actions'] = saved_actions
210         # we don't allow more than 10 stored actions
211         if len(saved_actions["actions"]) >= 10:
212             del saved_actions["actions"][min(saved_actions["actions"].keys())]
213         key = saved_actions["next"]
214         saved_actions["actions"][key] = the_action
215         saved_actions["next"] = key + 1
216         return key
217
218     @openerpweb.jsonrequest
219     def get_session_action(self, req, key):
220         """
221         Gets back a previously saved action. This method can return None if the action
222         was saved since too much time (this case should be handled in a smart way).
223         
224         :param key: The key given by save_session_action()
225         :type key: integer
226         :return: The saved action or None.
227         :rtype: anything
228         """
229         saved_actions = cherrypy.session.get('saved_actions')
230         if not saved_actions:
231             return None
232         return saved_actions["actions"].get(key)
233         
234 def eval_context_and_domain(session, context, domain=None):
235     e_context = session.eval_context(context)
236     e_domain = session.eval_domain(domain or [], e_context)
237
238     return (e_context, e_domain)
239         
240 def load_actions_from_ir_values(req, key, key2, models, meta, context):
241     context['bin_size'] = False # Possible upstream bug. Antony says not to loose time on this.
242     Values = req.session.model('ir.values')
243     actions = Values.get(key, key2, models, meta, context)
244
245     for _, _, action in actions:
246         clean_action(action, req.session)
247
248     return actions
249
250 def clean_action(action, session):
251     # values come from the server, we can just eval them
252     if isinstance(action['context'], basestring):
253         action['context'] = eval(
254             action['context'],
255             session.evaluation_context()) or {}
256
257     if isinstance(action['domain'], basestring):
258         action['domain'] = eval(
259             action['domain'],
260             session.evaluation_context(
261                 action['context'])) or []
262     if not action.has_key('flags'):
263         # Set empty flags dictionary for web client.
264         action['flags'] = dict()
265     return fix_view_modes(action)
266
267 def fix_view_modes(action):
268     """ For historical reasons, OpenERP has weird dealings in relation to
269     view_mode and the view_type attribute (on window actions):
270
271     * one of the view modes is ``tree``, which stands for both list views
272       and tree views
273     * the choice is made by checking ``view_type``, which is either
274       ``form`` for a list view or ``tree`` for an actual tree view
275
276     This methods simply folds the view_type into view_mode by adding a
277     new view mode ``list`` which is the result of the ``tree`` view_mode
278     in conjunction with the ``form`` view_type.
279
280     TODO: this should go into the doc, some kind of "peculiarities" section
281
282     :param dict action: an action descriptor
283     :returns: nothing, the action is modified in place
284     """
285     if action.pop('view_type') != 'form':
286         return
287
288     action['view_mode'] = ','.join(
289         mode if mode != 'tree' else 'list'
290         for mode in action['view_mode'].split(','))
291     action['views'] = [
292         [id, mode if mode != 'tree' else 'list']
293         for id, mode in action['views']
294     ]
295     return action
296
297 class Menu(openerpweb.Controller):
298     _cp_path = "/base/menu"
299
300     @openerpweb.jsonrequest
301     def load(self, req):
302         return {'data': self.do_load(req)}
303
304     def do_load(self, req):
305         """ Loads all menu items (all applications and their sub-menus).
306
307         :param req: A request object, with an OpenERP session attribute
308         :type req: < session -> OpenERPSession >
309         :return: the menu root
310         :rtype: dict('children': menu_nodes)
311         """
312         Menus = req.session.model('ir.ui.menu')
313         # menus are loaded fully unlike a regular tree view, cause there are
314         # less than 512 items
315         context = req.session.eval_context(req.context)
316         menu_ids = Menus.search([], 0, False, False, context)
317         menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context)
318         menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']}
319         menu_items.append(menu_root)
320         
321         # make a tree using parent_id
322         menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items)
323         for menu_item in menu_items:
324             if menu_item['parent_id']:
325                 parent = menu_item['parent_id'][0]
326             else:
327                 parent = False
328             if parent in menu_items_map:
329                 menu_items_map[parent].setdefault(
330                     'children', []).append(menu_item)
331
332         # sort by sequence a tree using parent_id
333         for menu_item in menu_items:
334             menu_item.setdefault('children', []).sort(
335                 key=lambda x:x["sequence"])
336
337         return menu_root
338
339     @openerpweb.jsonrequest
340     def action(self, req, menu_id):
341         actions = load_actions_from_ir_values(req,'action', 'tree_but_open',
342                                              [('ir.ui.menu', menu_id)], False,
343                                              req.session.eval_context(req.context))
344         return {"action": actions}
345
346 class DataSet(openerpweb.Controller):
347     _cp_path = "/base/dataset"
348
349     @openerpweb.jsonrequest
350     def fields(self, req, model):
351         return {'fields': req.session.model(model).fields_get(False,
352                                                               req.session.eval_context(req.context))}
353
354     @openerpweb.jsonrequest
355     def search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, context=None, sort=None):
356         return self.do_search_read(request, model, fields, offset, limit, domain, context, sort)
357     def do_search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, context=None, sort=None):
358         """ Performs a search() followed by a read() (if needed) using the
359         provided search criteria
360
361         :param request: a JSON-RPC request object
362         :type request: openerpweb.JsonRequest
363         :param str model: the name of the model to search on
364         :param fields: a list of the fields to return in the result records
365         :type fields: [str]
366         :param int offset: from which index should the results start being returned
367         :param int limit: the maximum number of records to return
368         :param list domain: the search domain for the query
369         :param list sort: sorting directives
370         :returns: a list of result records
371         :rtype: list
372         """
373         Model = request.session.model(model)
374         context, domain = eval_context_and_domain(request.session, request.context, domain)
375         
376         ids = Model.search(domain, offset or 0, limit or False,
377                            sort or False, context)
378
379         if fields and fields == ['id']:
380             # shortcut read if we only want the ids
381             return map(lambda id: {'id': id}, ids)
382
383         reads = Model.read(ids, fields or False, context)
384         reads.sort(key=lambda obj: ids.index(obj['id']))
385         return reads
386     
387     @openerpweb.jsonrequest
388     def read(self, request, model, ids, fields=False):
389         return self.do_search_read(request, model, ids, fields,
390                                    request.session.eval_context(request.context))
391
392     @openerpweb.jsonrequest
393     def get(self, request, model, ids, fields=False):
394         return self.do_get(request, model, ids, fields)
395     def do_get(self, request, model, ids, fields=False):
396         """ Fetches and returns the records of the model ``model`` whose ids
397         are in ``ids``.
398
399         The results are in the same order as the inputs, but elements may be
400         missing (if there is no record left for the id)
401
402         :param request: the JSON-RPC2 request object
403         :type request: openerpweb.JsonRequest
404         :param model: the model to read from
405         :type model: str
406         :param ids: a list of identifiers
407         :type ids: list
408         :param fields: a list of fields to fetch, ``False`` or empty to fetch
409                        all fields in the model
410         :type fields: list | False
411         :returns: a list of records, in the same order as the list of ids
412         :rtype: list
413         """
414         Model = request.session.model(model)
415         records = Model.read(ids, fields, request.session.eval_context(request.context))
416
417         record_map = dict((record['id'], record) for record in records)
418
419         return [record_map[id] for id in ids if record_map.get(id)]
420     
421     @openerpweb.jsonrequest
422     def load(self, req, model, id, fields):
423         m = req.session.model(model)
424         value = {}
425         r = m.read([id], False, req.session.eval_context(req.context))
426         if r:
427             value = r[0]
428         return {'value': value}
429
430     @openerpweb.jsonrequest
431     def create(self, req, model, data):
432         m = req.session.model(model)
433         r = m.create(data, req.session.eval_context(req.context))
434         return {'result': r}
435
436     @openerpweb.jsonrequest
437     def save(self, req, model, id, data):
438         m = req.session.model(model)
439         r = m.write([id], data, req.session.eval_context(req.context))
440         return {'result': r}
441
442     @openerpweb.jsonrequest
443     def unlink(self, request, model, ids=[]):
444         Model = request.session.model(model)
445         return Model.unlink(ids, request.session.eval_context(request.context))
446
447     @openerpweb.jsonrequest
448     def call(self, req, model, method, args, domain_id=None, context_id=None):
449         domain = args[domain_id] if domain_id and len(args) - 1 >= domain_id  else []
450         context = args[context_id] if context_id and len(args) - 1 >= context_id  else {}
451         c, d = eval_context_and_domain(req.session, context, domain);
452         if(domain_id and len(args) - 1 >= domain_id):
453             args[domain_id] = d
454         if(context_id and len(args) - 1 >= context_id):
455             args[context_id] = c
456         
457         m = req.session.model(model)
458         r = getattr(m, method)(*args)
459         return {'result': r}
460
461     @openerpweb.jsonrequest
462     def exec_workflow(self, req, model, id, signal):
463         r = req.session.exec_workflow(model, id, signal)
464         return {'result': r}
465
466     @openerpweb.jsonrequest
467     def default_get(self, req, model, fields):
468         m = req.session.model(model)
469         r = m.default_get(fields, req.session.eval_context(req.context))
470         return {'result': r}
471
472 class DataGroup(openerpweb.Controller):
473     _cp_path = "/base/group"
474     @openerpweb.jsonrequest
475     def read(self, request, model, group_by_fields, domain=None):
476         Model = request.session.model(model)
477         context, domain = eval_context_and_domain(request.session, request.context, domain)
478
479         return Model.read_group(
480             domain or [], False, group_by_fields, 0, False,
481             dict(context, group_by=group_by_fields))
482
483 class View(openerpweb.Controller):
484     _cp_path = "/base/view"
485
486     def fields_view_get(self, request, model, view_id, view_type,
487                         transform=True, toolbar=False, submenu=False):
488         Model = request.session.model(model)
489         context = request.session.eval_context(request.context)
490         fvg = Model.fields_view_get(view_id, view_type, context, toolbar, submenu)
491         # todo fme?: check that we should pass the evaluated context here
492         self.process_view(request.session, fvg, context, transform)
493         return fvg
494     
495     def process_view(self, session, fvg, context, transform):
496         if transform:
497             evaluation_context = session.evaluation_context(context or {})
498             xml = self.transform_view(fvg['arch'], session, evaluation_context)
499         else:
500             xml = ElementTree.fromstring(fvg['arch'])
501         fvg['arch'] = Xml2Json.convert_element(xml)
502         for field in fvg['fields'].values():
503             if field.has_key('views') and field['views']:
504                 for view in field["views"].values():
505                     self.process_view(session, view, None, transform)
506
507     @openerpweb.jsonrequest
508     def add_custom(self, request, view_id, arch):
509         CustomView = request.session.model('ir.ui.view.custom')
510         CustomView.create({
511             'user_id': request.session._uid,
512             'ref_id': view_id,
513             'arch': arch
514         }, request.session.eval_context(request.context))
515         return {'result': True}
516
517     @openerpweb.jsonrequest
518     def undo_custom(self, request, view_id, reset=False):
519         CustomView = request.session.model('ir.ui.view.custom')
520         context = request.session.eval_context(request.context)
521         vcustom = CustomView.search([('user_id', '=', request.session._uid), ('ref_id' ,'=', view_id)],
522                                     0, False, False, context)
523         if vcustom:
524             if reset:
525                 CustomView.unlink(vcustom, context)
526             else:
527                 CustomView.unlink([vcustom[0]], context)
528             return {'result': True}
529         return {'result': False}
530
531     def normalize_attrs(self, elem, context):
532         """ Normalize @attrs, @invisible, @required, @readonly and @states, so
533         the client only has to deal with @attrs.
534
535         See `the discoveries pad <http://pad.openerp.com/discoveries>`_ for
536         the rationale.
537
538         :param elem: the current view node (Python object)
539         :type elem: xml.etree.ElementTree.Element
540         :param dict context: evaluation context
541         """
542         # If @attrs is normalized in json by server, the eval should be replaced by simplejson.loads
543         attrs = openerpweb.ast.literal_eval(elem.get('attrs', '{}'))
544         if 'states' in elem.attrib:
545             attrs.setdefault('invisible', [])\
546                 .append(('state', 'not in', elem.attrib.pop('states').split(',')))
547         if attrs:
548             elem.set('attrs', simplejson.dumps(attrs))
549         for a in ['invisible', 'readonly', 'required']:
550             if a in elem.attrib:
551                 # In the XML we trust
552                 avalue = bool(eval(elem.get(a, 'False'),
553                                    {'context': context or {}}))
554                 if not avalue:
555                     del elem.attrib[a]
556                 else:
557                     elem.attrib[a] = '1'
558                     if a == 'invisible' and 'attrs' in elem.attrib:
559                         del elem.attrib['attrs']
560
561     def transform_view(self, view_string, session, context=None):
562         # transform nodes on the fly via iterparse, instead of
563         # doing it statically on the parsing result
564         parser = ElementTree.iterparse(StringIO(view_string), events=("start",))
565         root = None
566         for event, elem in parser:
567             if event == "start":
568                 if root is None:
569                     root = elem
570                 self.normalize_attrs(elem, context)
571                 self.parse_domains_and_contexts(elem, session)
572         return root
573
574     def parse_domain(self, elem, attr_name, session):
575         """ Parses an attribute of the provided name as a domain, transforms it
576         to either a literal domain or a :class:`openerpweb.nonliterals.Domain`
577
578         :param elem: the node being parsed
579         :type param: xml.etree.ElementTree.Element
580         :param str attr_name: the name of the attribute which should be parsed
581         :param session: Current OpenERP session
582         :type session: openerpweb.openerpweb.OpenERPSession
583         """
584         domain = elem.get(attr_name, '').strip()
585         if domain:
586             try:
587                 elem.set(
588                     attr_name,
589                     openerpweb.ast.literal_eval(
590                         domain))
591             except ValueError:
592                 # not a literal
593                 elem.set(attr_name,
594                          openerpweb.nonliterals.Domain(session, domain))
595
596     def parse_domains_and_contexts(self, elem, session):
597         """ Converts domains and contexts from the view into Python objects,
598         either literals if they can be parsed by literal_eval or a special
599         placeholder object if the domain or context refers to free variables.
600
601         :param elem: the current node being parsed
602         :type param: xml.etree.ElementTree.Element
603         :param session: OpenERP session object, used to store and retrieve
604                         non-literal objects
605         :type session: openerpweb.openerpweb.OpenERPSession
606         """
607         self.parse_domain(elem, 'domain', session)
608         self.parse_domain(elem, 'filter_domain', session)
609         context_string = elem.get('context', '').strip()
610         if context_string:
611             try:
612                 elem.set('context',
613                          openerpweb.ast.literal_eval(context_string))
614             except ValueError:
615                 elem.set('context',
616                          openerpweb.nonliterals.Context(
617                              session, context_string))
618
619 class FormView(View):
620     _cp_path = "/base/formview"
621
622     @openerpweb.jsonrequest
623     def load(self, req, model, view_id, toolbar=False):
624         fields_view = self.fields_view_get(req, model, view_id, 'form', toolbar=toolbar)
625         return {'fields_view': fields_view}
626
627 class ListView(View):
628     _cp_path = "/base/listview"
629
630     @openerpweb.jsonrequest
631     def load(self, req, model, view_id, toolbar=False):
632         fields_view = self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar)
633         return {'fields_view': fields_view}
634
635     def process_colors(self, view, row, context):
636         colors = view['arch']['attrs'].get('colors')
637
638         if not colors:
639             return None
640
641         color = [
642             pair.split(':')[0]
643             for pair in colors.split(';')
644             if eval(pair.split(':')[1], dict(context, **row))
645         ]
646
647         if not color:
648             return None
649         elif len(color) == 1:
650             return color[0]
651         return 'maroon'
652
653 class SearchView(View):
654     _cp_path = "/base/searchview"
655
656     @openerpweb.jsonrequest
657     def load(self, req, model, view_id):
658         fields_view = self.fields_view_get(req, model, view_id, 'search')
659         return {'fields_view': fields_view}
660
661     @openerpweb.jsonrequest
662     def fields_get(self, req, model):
663         Model = req.session.model(model)
664         fields = Model.fields_get(False, req.session.eval_context(req.context))
665         return {'fields': fields}
666
667 class Binary(openerpweb.Controller):
668     _cp_path = "/base/binary"
669
670     @openerpweb.httprequest
671     def image(self, request, session_id, model, id, field, **kw):
672         cherrypy.response.headers['Content-Type'] = 'image/png'
673         Model = request.session.model(model)
674         context = request.session.eval_context(request.context)
675         try:
676             if not id:
677                 res = Model.default_get([field], context).get(field, '')
678             else:
679                 res = Model.read([int(id)], [field], context)[0].get(field, '')
680             return base64.decodestring(res)
681         except: # TODO: what's the exception here?
682             return self.placeholder()
683     def placeholder(self):
684         return open(os.path.join(openerpweb.path_addons, 'base', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
685
686     @openerpweb.httprequest
687     def saveas(self, request, session_id, model, id, field, fieldname, **kw):
688         Model = request.session.model(model)
689         context = request.session.eval_context(request.context)
690         res = Model.read([int(id)], [field, fieldname], context)[0]
691         filecontent = res.get(field, '')
692         if not filecontent:
693             raise cherrypy.NotFound
694         else:
695             cherrypy.response.headers['Content-Type'] = 'application/octet-stream'
696             filename = '%s_%s' % (model.replace('.', '_'), id)
697             if fieldname:
698                 filename = res.get(fieldname, '') or filename
699             cherrypy.response.headers['Content-Disposition'] = 'attachment; filename=' +  filename
700             return base64.decodestring(filecontent)
701
702     @openerpweb.httprequest
703     def upload(self, request, session_id, callback, ufile=None):
704         cherrypy.response.timeout = 500
705         headers = {}
706         for key, val in cherrypy.request.headers.iteritems():
707             headers[key.lower()] = val
708         size = int(headers.get('content-length', 0))
709         # TODO: might be usefull to have a configuration flag for max-lenght file uploads
710         try:
711             out = """<script language="javascript" type="text/javascript">
712                         var win = window.top.window,
713                             callback = win[%s];
714                         if (typeof(callback) === 'function') {
715                             callback.apply(this, %s);
716                         } else {
717                             win.jQuery('#oe_notification', win.document).notify('create', {
718                                 title: "Ajax File Upload",
719                                 text: "Could not find callback"
720                             });
721                         }
722                     </script>"""
723             data = ufile.file.read()
724             args = [size, ufile.filename, ufile.headers.getheader('Content-Type'), base64.encodestring(data)]
725         except Exception, e:
726             args = [False, e.message]
727         return out % (simplejson.dumps(callback), simplejson.dumps(args))
728
729     @openerpweb.httprequest
730     def upload_attachment(self, request, session_id, callback, model, id, ufile=None):
731         cherrypy.response.timeout = 500
732         context = request.session.eval_context(request.context)
733         Model = request.session.model('ir.attachment')
734         try:
735             out = """<script language="javascript" type="text/javascript">
736                         var win = window.top.window,
737                             callback = win[%s];
738                         if (typeof(callback) === 'function') {
739                             callback.call(this, %s);
740                         }
741                     </script>"""
742             attachment_id = Model.create({
743                 'name': ufile.filename,
744                 'datas': base64.encodestring(ufile.file.read()),
745                 'res_model': model,
746                 'res_id': int(id)
747             }, context)
748             args = {
749                 'filename': ufile.filename,
750                 'id':  attachment_id
751             }
752         except Exception, e:
753             args = { 'error': e.message }
754         return out % (simplejson.dumps(callback), simplejson.dumps(args))
755
756 class Action(openerpweb.Controller):
757     _cp_path = "/base/action"
758
759     @openerpweb.jsonrequest
760     def load(self, req, action_id):
761         Actions = req.session.model('ir.actions.actions')
762         value = False
763         context = req.session.eval_context(req.context)
764         action_type = Actions.read([action_id], ['type'], context)
765         if action_type:
766             action = req.session.model(action_type[0]['type']).read([action_id], False,
767                                                                     #TODO fme: check why does not work with context
768                                                                     #context)
769                                                                     {})
770             if action:
771                 value = clean_action(action[0], req.session)
772         return {'result': value}