2011-03-02 18:56:06 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
2011-07-13 10:26:12 +00:00
|
|
|
import base64, glob, os, re
|
2011-03-10 15:53:45 +00:00
|
|
|
from xml.etree import ElementTree
|
2011-03-24 20:11:25 +00:00
|
|
|
from cStringIO import StringIO
|
2011-03-02 18:56:06 +00:00
|
|
|
|
2011-03-10 15:53:45 +00:00
|
|
|
import simplejson
|
2011-03-02 18:56:06 +00:00
|
|
|
|
|
|
|
import openerpweb
|
2011-03-28 12:27:24 +00:00
|
|
|
import openerpweb.ast
|
|
|
|
import openerpweb.nonliterals
|
2011-03-02 18:56:06 +00:00
|
|
|
|
2011-04-08 15:25:08 +00:00
|
|
|
import cherrypy
|
|
|
|
|
2011-04-06 21:10:37 +00:00
|
|
|
# Should move to openerpweb.Xml2Json
|
2011-03-03 14:55:52 +00:00
|
|
|
class Xml2Json:
|
|
|
|
# xml2json-direct
|
|
|
|
# Simple and straightforward XML-to-JSON converter in Python
|
|
|
|
# New BSD Licensed
|
|
|
|
#
|
|
|
|
# URL: http://code.google.com/p/xml2json-direct/
|
|
|
|
@staticmethod
|
|
|
|
def convert_to_json(s):
|
2011-03-28 14:19:20 +00:00
|
|
|
return simplejson.dumps(
|
|
|
|
Xml2Json.convert_to_structure(s), sort_keys=True, indent=4)
|
2011-03-03 14:55:52 +00:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def convert_to_structure(s):
|
|
|
|
root = ElementTree.fromstring(s)
|
|
|
|
return Xml2Json.convert_element(root)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def convert_element(el, skip_whitespaces=True):
|
|
|
|
res = {}
|
2011-03-21 08:13:31 +00:00
|
|
|
if el.tag[0] == "{":
|
|
|
|
ns, name = el.tag.rsplit("}", 1)
|
2011-03-03 14:55:52 +00:00
|
|
|
res["tag"] = name
|
|
|
|
res["namespace"] = ns[1:]
|
|
|
|
else:
|
|
|
|
res["tag"] = el.tag
|
|
|
|
res["attrs"] = {}
|
2011-03-21 08:13:31 +00:00
|
|
|
for k, v in el.items():
|
2011-03-03 14:55:52 +00:00
|
|
|
res["attrs"][k] = v
|
|
|
|
kids = []
|
|
|
|
if el.text and (not skip_whitespaces or el.text.strip() != ''):
|
|
|
|
kids.append(el.text)
|
|
|
|
for kid in el:
|
|
|
|
kids.append(Xml2Json.convert_element(kid))
|
|
|
|
if kid.tail and (not skip_whitespaces or kid.tail.strip() != ''):
|
2011-03-08 14:55:30 +00:00
|
|
|
kids.append(kid.tail)
|
2011-03-16 17:20:42 +00:00
|
|
|
res["children"] = kids
|
2011-03-03 14:55:52 +00:00
|
|
|
return res
|
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
#----------------------------------------------------------
|
|
|
|
# OpenERP Web base Controllers
|
|
|
|
#----------------------------------------------------------
|
|
|
|
|
2011-07-13 10:26:12 +00:00
|
|
|
class Database(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/database"
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def get_databases_list(self, req):
|
|
|
|
proxy = req.session.proxy("db")
|
|
|
|
dbs = proxy.list()
|
|
|
|
h = req.httprequest.headers['Host'].split(':')[0]
|
2011-07-13 12:04:11 +00:00
|
|
|
d = h.split('.')[0]
|
2011-07-13 10:26:12 +00:00
|
|
|
r = cherrypy.config['openerp.dbfilter'].replace('%h',h).replace('%d',d)
|
|
|
|
print "h,d",h,d,r
|
|
|
|
dbs = [i for i in dbs if re.match(r,i)]
|
|
|
|
return {"db_list": dbs}
|
|
|
|
|
2011-03-10 11:51:23 +00:00
|
|
|
class Session(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/session"
|
|
|
|
|
2011-04-13 12:50:42 +00:00
|
|
|
def manifest_glob(self, addons, key):
|
2011-03-08 11:19:54 +00:00
|
|
|
files = []
|
2011-04-13 12:50:42 +00:00
|
|
|
for addon in addons:
|
|
|
|
globlist = openerpweb.addons_manifest.get(addon, {}).get(key, [])
|
|
|
|
|
|
|
|
files.extend([
|
|
|
|
resource_path[len(openerpweb.path_addons):]
|
|
|
|
for pattern in globlist
|
|
|
|
for resource_path in glob.glob(os.path.join(
|
|
|
|
openerpweb.path_addons, addon, pattern))
|
|
|
|
])
|
2011-03-08 11:19:54 +00:00
|
|
|
return files
|
|
|
|
|
|
|
|
def concat_files(self, file_list):
|
2011-03-02 18:56:06 +00:00
|
|
|
""" Concatenate file content
|
|
|
|
return (concat,timestamp)
|
|
|
|
concat: concatenation of file content
|
|
|
|
timestamp: max(os.path.getmtime of file_list)
|
|
|
|
"""
|
2011-03-07 09:26:32 +00:00
|
|
|
root = openerpweb.path_root
|
2011-03-02 18:56:06 +00:00
|
|
|
files_content = []
|
|
|
|
files_timestamp = 0
|
|
|
|
for i in file_list:
|
2011-03-21 08:13:31 +00:00
|
|
|
fname = os.path.join(root, i)
|
2011-03-02 18:56:06 +00:00
|
|
|
ftime = os.path.getmtime(fname)
|
|
|
|
if ftime > files_timestamp:
|
|
|
|
files_timestamp = ftime
|
|
|
|
files_content = open(fname).read()
|
|
|
|
files_concat = "".join(files_content)
|
|
|
|
return files_concat
|
|
|
|
|
2011-03-11 13:26:22 +00:00
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def login(self, req, db, login, password):
|
2011-03-17 15:01:53 +00:00
|
|
|
req.session.login(db, login, password)
|
2011-04-01 10:45:00 +00:00
|
|
|
|
2011-03-17 17:14:03 +00:00
|
|
|
return {
|
2011-03-21 08:13:31 +00:00
|
|
|
"session_id": req.session_id,
|
2011-03-11 13:26:22 +00:00
|
|
|
"uid": req.session._uid,
|
|
|
|
}
|
2011-06-10 12:50:06 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def sc_list(self, req):
|
2011-06-17 16:08:49 +00:00
|
|
|
return req.session.model('ir.ui.view_sc').get_sc(req.session._uid, "ir.ui.menu",
|
|
|
|
req.session.eval_context(req.context))
|
2011-06-10 12:50:06 +00:00
|
|
|
|
2011-03-11 13:26:22 +00:00
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def modules(self, req):
|
2011-04-13 12:50:42 +00:00
|
|
|
return {"modules": [name
|
|
|
|
for name, manifest in openerpweb.addons_manifest.iteritems()
|
|
|
|
if manifest.get('active', True)]}
|
2011-03-11 13:26:22 +00:00
|
|
|
|
2011-03-10 11:51:23 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-04-13 12:50:42 +00:00
|
|
|
def csslist(self, req, mods='base'):
|
2011-03-10 11:51:23 +00:00
|
|
|
return {'files': self.manifest_glob(mods.split(','), 'css')}
|
2011-03-08 11:19:54 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-04-13 12:50:42 +00:00
|
|
|
def jslist(self, req, mods='base'):
|
2011-03-10 11:51:23 +00:00
|
|
|
return {'files': self.manifest_glob(mods.split(','), 'js')}
|
2011-03-08 11:19:54 +00:00
|
|
|
|
2011-07-07 08:44:15 +00:00
|
|
|
def css(self, req, mods='base'):
|
2011-03-08 11:19:54 +00:00
|
|
|
files = self.manifest_glob(mods.split(','), 'css')
|
2011-03-08 14:57:54 +00:00
|
|
|
concat = self.concat_files(files)[0]
|
2011-03-02 18:56:06 +00:00
|
|
|
# TODO request set the Date of last modif and Etag
|
|
|
|
return concat
|
2011-03-21 08:13:31 +00:00
|
|
|
css.exposed = True
|
2011-03-02 18:56:06 +00:00
|
|
|
|
2011-07-07 08:44:15 +00:00
|
|
|
def js(self, req, mods='base'):
|
2011-03-08 11:19:54 +00:00
|
|
|
files = self.manifest_glob(mods.split(','), 'js')
|
2011-03-08 14:57:54 +00:00
|
|
|
concat = self.concat_files(files)[0]
|
2011-03-02 18:56:06 +00:00
|
|
|
# TODO request set the Date of last modif and Etag
|
|
|
|
return concat
|
2011-03-21 08:13:31 +00:00
|
|
|
js.exposed = True
|
|
|
|
|
2011-03-25 12:32:52 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-03-28 16:39:17 +00:00
|
|
|
def eval_domain_and_context(self, req, contexts, domains,
|
|
|
|
group_by_seq=None):
|
|
|
|
""" Evaluates sequences of domains and contexts, composing them into
|
|
|
|
a single context, domain or group_by sequence.
|
|
|
|
|
|
|
|
:param list contexts: list of contexts to merge together. Contexts are
|
|
|
|
evaluated in sequence, all previous contexts
|
|
|
|
are part of their own evaluation context
|
|
|
|
(starting at the session context).
|
|
|
|
:param list domains: list of domains to merge together. Domains are
|
|
|
|
evaluated in sequence and appended to one another
|
|
|
|
(implicit AND), their evaluation domain is the
|
|
|
|
result of merging all contexts.
|
|
|
|
:param list group_by_seq: list of domains (which may be in a different
|
|
|
|
order than the ``contexts`` parameter),
|
|
|
|
evaluated in sequence, their ``'group_by'``
|
|
|
|
key is extracted if they have one.
|
|
|
|
:returns:
|
|
|
|
a 3-dict of:
|
|
|
|
|
|
|
|
context (``dict``)
|
|
|
|
the global context created by merging all of
|
|
|
|
``contexts``
|
|
|
|
|
|
|
|
domain (``list``)
|
|
|
|
the concatenation of all domains
|
|
|
|
|
|
|
|
group_by (``list``)
|
|
|
|
a list of fields to group by, potentially empty (in which case
|
|
|
|
no group by should be performed)
|
|
|
|
"""
|
2011-06-28 12:17:47 +00:00
|
|
|
context, domain = eval_context_and_domain(req.session,
|
2011-06-28 14:04:18 +00:00
|
|
|
openerpweb.nonliterals.CompoundContext(*(contexts or [])),
|
|
|
|
openerpweb.nonliterals.CompoundDomain(*(domains or [])))
|
2011-06-17 16:08:49 +00:00
|
|
|
|
|
|
|
group_by_sequence = []
|
|
|
|
for candidate in (group_by_seq or []):
|
|
|
|
ctx = req.session.eval_context(candidate, context)
|
|
|
|
group_by = ctx.get('group_by')
|
|
|
|
if not group_by:
|
|
|
|
continue
|
|
|
|
elif isinstance(group_by, basestring):
|
|
|
|
group_by_sequence.append(group_by)
|
|
|
|
else:
|
|
|
|
group_by_sequence.extend(group_by)
|
|
|
|
|
|
|
|
return {
|
|
|
|
'context': context,
|
|
|
|
'domain': domain,
|
|
|
|
'group_by': group_by_sequence
|
|
|
|
}
|
2011-04-08 15:25:08 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def save_session_action(self, req, the_action):
|
|
|
|
"""
|
|
|
|
This method store an action object in the session object and returns an integer
|
|
|
|
identifying that action. The method get_session_action() can be used to get
|
|
|
|
back the action.
|
|
|
|
|
|
|
|
:param the_action: The action to save in the session.
|
|
|
|
:type the_action: anything
|
|
|
|
:return: A key identifying the saved action.
|
|
|
|
:rtype: integer
|
|
|
|
"""
|
|
|
|
saved_actions = cherrypy.session.get('saved_actions')
|
|
|
|
if not saved_actions:
|
|
|
|
saved_actions = {"next":0, "actions":{}}
|
|
|
|
cherrypy.session['saved_actions'] = saved_actions
|
|
|
|
# we don't allow more than 10 stored actions
|
|
|
|
if len(saved_actions["actions"]) >= 10:
|
|
|
|
del saved_actions["actions"][min(saved_actions["actions"].keys())]
|
|
|
|
key = saved_actions["next"]
|
|
|
|
saved_actions["actions"][key] = the_action
|
|
|
|
saved_actions["next"] = key + 1
|
|
|
|
return key
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def get_session_action(self, req, key):
|
|
|
|
"""
|
|
|
|
Gets back a previously saved action. This method can return None if the action
|
|
|
|
was saved since too much time (this case should be handled in a smart way).
|
|
|
|
|
|
|
|
:param key: The key given by save_session_action()
|
|
|
|
:type key: integer
|
|
|
|
:return: The saved action or None.
|
|
|
|
:rtype: anything
|
|
|
|
"""
|
|
|
|
saved_actions = cherrypy.session.get('saved_actions')
|
|
|
|
if not saved_actions:
|
|
|
|
return None
|
|
|
|
return saved_actions["actions"].get(key)
|
2011-07-13 10:26:12 +00:00
|
|
|
|
2011-06-17 16:08:49 +00:00
|
|
|
def eval_context_and_domain(session, context, domain=None):
|
|
|
|
e_context = session.eval_context(context)
|
2011-06-28 14:54:13 +00:00
|
|
|
# should we give the evaluated context as an evaluation context to the domain?
|
|
|
|
e_domain = session.eval_domain(domain or [])
|
2011-06-17 12:08:34 +00:00
|
|
|
|
2011-06-29 13:12:33 +00:00
|
|
|
return e_context, e_domain
|
2011-07-13 10:26:12 +00:00
|
|
|
|
2011-04-04 13:47:05 +00:00
|
|
|
def load_actions_from_ir_values(req, key, key2, models, meta, context):
|
|
|
|
Values = req.session.model('ir.values')
|
|
|
|
actions = Values.get(key, key2, models, meta, context)
|
|
|
|
|
2011-06-29 15:14:00 +00:00
|
|
|
return [(id, name, clean_action(action, req.session))
|
|
|
|
for id, name, action in actions]
|
2011-03-02 18:56:06 +00:00
|
|
|
|
2011-04-07 09:59:12 +00:00
|
|
|
def clean_action(action, session):
|
2011-06-29 15:14:00 +00:00
|
|
|
if action['type'] != 'ir.actions.act_window':
|
|
|
|
return action
|
2011-04-07 09:59:12 +00:00
|
|
|
# values come from the server, we can just eval them
|
2011-06-28 15:59:56 +00:00
|
|
|
if isinstance(action.get('context', None), basestring):
|
2011-04-07 09:59:12 +00:00
|
|
|
action['context'] = eval(
|
|
|
|
action['context'],
|
|
|
|
session.evaluation_context()) or {}
|
|
|
|
|
2011-06-28 15:59:56 +00:00
|
|
|
if isinstance(action.get('domain', None), basestring):
|
2011-04-07 09:59:12 +00:00
|
|
|
action['domain'] = eval(
|
|
|
|
action['domain'],
|
|
|
|
session.evaluation_context(
|
2011-07-18 14:29:23 +00:00
|
|
|
action.get('context', {}))) or []
|
2011-06-29 15:14:00 +00:00
|
|
|
if 'flags' not in action:
|
2011-05-31 19:52:02 +00:00
|
|
|
# Set empty flags dictionary for web client.
|
|
|
|
action['flags'] = dict()
|
|
|
|
return fix_view_modes(action)
|
2011-04-07 09:59:12 +00:00
|
|
|
|
2011-06-30 06:46:51 +00:00
|
|
|
def generate_views(action):
|
|
|
|
"""
|
|
|
|
While the server generates a sequence called "views" computing dependencies
|
|
|
|
between a bunch of stuff for views coming directly from the database
|
|
|
|
(the ``ir.actions.act_window model``), it's also possible for e.g. buttons
|
|
|
|
to return custom view dictionaries generated on the fly.
|
|
|
|
|
|
|
|
In that case, there is no ``views`` key available on the action.
|
|
|
|
|
|
|
|
Since the web client relies on ``action['views']``, generate it here from
|
|
|
|
``view_mode`` and ``view_id``.
|
|
|
|
|
|
|
|
Currently handles two different cases:
|
|
|
|
|
|
|
|
* no view_id, multiple view_mode
|
|
|
|
* single view_id, single view_mode
|
|
|
|
|
|
|
|
:param dict action: action descriptor dictionary to generate a views key for
|
|
|
|
"""
|
|
|
|
view_id = action.get('view_id', False)
|
|
|
|
if isinstance(view_id, (list, tuple)):
|
|
|
|
view_id = view_id[0]
|
|
|
|
|
|
|
|
# providing at least one view mode is a requirement, not an option
|
|
|
|
view_modes = action['view_mode'].split(',')
|
|
|
|
|
|
|
|
if len(view_modes) > 1:
|
|
|
|
if view_id:
|
|
|
|
raise ValueError('Non-db action dictionaries should provide '
|
|
|
|
'either multiple view modes or a single view '
|
|
|
|
'mode and an optional view id.\n\n Got view '
|
|
|
|
'modes %r and view id %r for action %r' % (
|
|
|
|
view_modes, view_id, action))
|
|
|
|
action['views'] = [(False, mode) for mode in view_modes]
|
|
|
|
return
|
|
|
|
action['views'] = [(view_id, view_modes[0])]
|
|
|
|
|
2011-04-04 14:26:40 +00:00
|
|
|
def fix_view_modes(action):
|
|
|
|
""" For historical reasons, OpenERP has weird dealings in relation to
|
|
|
|
view_mode and the view_type attribute (on window actions):
|
|
|
|
|
|
|
|
* one of the view modes is ``tree``, which stands for both list views
|
|
|
|
and tree views
|
|
|
|
* the choice is made by checking ``view_type``, which is either
|
|
|
|
``form`` for a list view or ``tree`` for an actual tree view
|
|
|
|
|
|
|
|
This methods simply folds the view_type into view_mode by adding a
|
|
|
|
new view mode ``list`` which is the result of the ``tree`` view_mode
|
|
|
|
in conjunction with the ``form`` view_type.
|
|
|
|
|
|
|
|
TODO: this should go into the doc, some kind of "peculiarities" section
|
|
|
|
|
|
|
|
:param dict action: an action descriptor
|
|
|
|
:returns: nothing, the action is modified in place
|
|
|
|
"""
|
2011-06-30 06:46:51 +00:00
|
|
|
if 'views' not in action:
|
|
|
|
generate_views(action)
|
|
|
|
|
2011-04-04 14:26:40 +00:00
|
|
|
if action.pop('view_type') != 'form':
|
2011-07-12 13:34:30 +00:00
|
|
|
return action
|
2011-04-04 14:26:40 +00:00
|
|
|
|
2011-06-30 06:24:24 +00:00
|
|
|
action['views'] = [
|
|
|
|
[id, mode if mode != 'tree' else 'list']
|
|
|
|
for id, mode in action['views']
|
|
|
|
]
|
|
|
|
|
2011-05-31 19:52:02 +00:00
|
|
|
return action
|
2011-03-02 18:56:06 +00:00
|
|
|
|
|
|
|
class Menu(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/menu"
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-03-21 08:13:31 +00:00
|
|
|
def load(self, req):
|
2011-03-21 10:47:35 +00:00
|
|
|
return {'data': self.do_load(req)}
|
|
|
|
|
|
|
|
def do_load(self, req):
|
|
|
|
""" Loads all menu items (all applications and their sub-menus).
|
|
|
|
|
|
|
|
:param req: A request object, with an OpenERP session attribute
|
|
|
|
:type req: < session -> OpenERPSession >
|
|
|
|
:return: the menu root
|
|
|
|
:rtype: dict('children': menu_nodes)
|
|
|
|
"""
|
2011-03-21 08:43:24 +00:00
|
|
|
Menus = req.session.model('ir.ui.menu')
|
2011-03-02 18:56:06 +00:00
|
|
|
# menus are loaded fully unlike a regular tree view, cause there are
|
|
|
|
# less than 512 items
|
2011-06-17 16:08:49 +00:00
|
|
|
context = req.session.eval_context(req.context)
|
|
|
|
menu_ids = Menus.search([], 0, False, False, context)
|
|
|
|
menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id'], context)
|
2011-03-21 08:13:31 +00:00
|
|
|
menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, '']}
|
2011-03-02 18:56:06 +00:00
|
|
|
menu_items.append(menu_root)
|
2011-03-21 08:43:24 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
# make a tree using parent_id
|
2011-03-21 08:43:24 +00:00
|
|
|
menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items)
|
|
|
|
for menu_item in menu_items:
|
2011-03-21 15:40:34 +00:00
|
|
|
if menu_item['parent_id']:
|
|
|
|
parent = menu_item['parent_id'][0]
|
|
|
|
else:
|
|
|
|
parent = False
|
2011-03-21 08:43:24 +00:00
|
|
|
if parent in menu_items_map:
|
2011-03-21 10:47:35 +00:00
|
|
|
menu_items_map[parent].setdefault(
|
|
|
|
'children', []).append(menu_item)
|
2011-03-21 08:43:24 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
# sort by sequence a tree using parent_id
|
2011-03-21 08:43:24 +00:00
|
|
|
for menu_item in menu_items:
|
2011-03-21 10:47:35 +00:00
|
|
|
menu_item.setdefault('children', []).sort(
|
2011-03-21 08:43:24 +00:00
|
|
|
key=lambda x:x["sequence"])
|
2011-03-14 09:38:25 +00:00
|
|
|
|
2011-03-21 10:47:35 +00:00
|
|
|
return menu_root
|
2011-03-02 18:56:06 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-03-21 08:13:31 +00:00
|
|
|
def action(self, req, menu_id):
|
2011-04-04 13:47:05 +00:00
|
|
|
actions = load_actions_from_ir_values(req,'action', 'tree_but_open',
|
2011-06-17 16:08:49 +00:00
|
|
|
[('ir.ui.menu', menu_id)], False,
|
2011-06-20 09:55:58 +00:00
|
|
|
req.session.eval_context(req.context))
|
2011-03-25 09:41:19 +00:00
|
|
|
return {"action": actions}
|
2011-03-02 18:56:06 +00:00
|
|
|
|
|
|
|
class DataSet(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/dataset"
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-03-21 08:13:31 +00:00
|
|
|
def fields(self, req, model):
|
2011-06-17 16:08:49 +00:00
|
|
|
return {'fields': req.session.model(model).fields_get(False,
|
|
|
|
req.session.eval_context(req.context))}
|
2011-03-02 18:56:06 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-06-22 14:55:59 +00:00
|
|
|
def search_read(self, request, model, fields=False, offset=0, limit=False, domain=None, sort=None):
|
|
|
|
return self.do_search_read(request, model, fields, offset, limit, domain, sort)
|
|
|
|
def do_search_read(self, request, model, fields=False, offset=0, limit=False, domain=None
|
|
|
|
, sort=None):
|
2011-03-23 12:08:06 +00:00
|
|
|
""" Performs a search() followed by a read() (if needed) using the
|
2011-03-23 12:21:26 +00:00
|
|
|
provided search criteria
|
|
|
|
|
|
|
|
:param request: a JSON-RPC request object
|
|
|
|
:type request: openerpweb.JsonRequest
|
2011-03-28 09:18:09 +00:00
|
|
|
:param str model: the name of the model to search on
|
2011-03-23 12:21:26 +00:00
|
|
|
:param fields: a list of the fields to return in the result records
|
|
|
|
:type fields: [str]
|
2011-03-28 09:18:09 +00:00
|
|
|
:param int offset: from which index should the results start being returned
|
|
|
|
:param int limit: the maximum number of records to return
|
|
|
|
:param list domain: the search domain for the query
|
|
|
|
:param list sort: sorting directives
|
2011-06-22 14:55:59 +00:00
|
|
|
:returns: A structure (dict) with two keys: ids (all the ids matching
|
|
|
|
the (domain, context) pair) and records (paginated records
|
|
|
|
matching fields selection set)
|
2011-03-23 12:22:17 +00:00
|
|
|
:rtype: list
|
2011-03-23 12:08:06 +00:00
|
|
|
"""
|
2011-03-23 08:34:41 +00:00
|
|
|
Model = request.session.model(model)
|
2011-06-22 14:55:59 +00:00
|
|
|
context, domain = eval_context_and_domain(
|
|
|
|
request.session, request.context, domain)
|
2011-05-04 14:05:35 +00:00
|
|
|
|
2011-06-22 14:55:59 +00:00
|
|
|
ids = Model.search(domain, 0, False, sort or False, context)
|
|
|
|
# need to fill the dataset with all ids for the (domain, context) pair,
|
|
|
|
# so search un-paginated and paginate manually before reading
|
|
|
|
paginated_ids = ids[offset:(offset + limit if limit else None)]
|
2011-03-23 08:34:41 +00:00
|
|
|
if fields and fields == ['id']:
|
|
|
|
# shortcut read if we only want the ids
|
2011-06-22 14:55:59 +00:00
|
|
|
return {
|
|
|
|
'ids': ids,
|
|
|
|
'records': map(lambda id: {'id': id}, paginated_ids)
|
|
|
|
}
|
|
|
|
|
|
|
|
records = Model.read(paginated_ids, fields or False, context)
|
|
|
|
records.sort(key=lambda obj: ids.index(obj['id']))
|
|
|
|
return {
|
|
|
|
'ids': ids,
|
|
|
|
'records': records
|
|
|
|
}
|
2011-05-04 14:05:35 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
|
2011-03-23 12:08:06 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-04-04 15:06:19 +00:00
|
|
|
def get(self, request, model, ids, fields=False):
|
|
|
|
return self.do_get(request, model, ids, fields)
|
|
|
|
def do_get(self, request, model, ids, fields=False):
|
2011-03-23 12:08:06 +00:00
|
|
|
""" Fetches and returns the records of the model ``model`` whose ids
|
|
|
|
are in ``ids``.
|
|
|
|
|
|
|
|
The results are in the same order as the inputs, but elements may be
|
|
|
|
missing (if there is no record left for the id)
|
|
|
|
|
|
|
|
:param request: the JSON-RPC2 request object
|
|
|
|
:type request: openerpweb.JsonRequest
|
|
|
|
:param model: the model to read from
|
|
|
|
:type model: str
|
|
|
|
:param ids: a list of identifiers
|
|
|
|
:type ids: list
|
2011-04-11 06:30:54 +00:00
|
|
|
:param fields: a list of fields to fetch, ``False`` or empty to fetch
|
|
|
|
all fields in the model
|
|
|
|
:type fields: list | False
|
2011-03-23 12:21:26 +00:00
|
|
|
:returns: a list of records, in the same order as the list of ids
|
|
|
|
:rtype: list
|
2011-03-23 12:08:06 +00:00
|
|
|
"""
|
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
records = Model.read(ids, fields, request.session.eval_context(request.context))
|
2011-03-23 12:08:06 +00:00
|
|
|
|
|
|
|
record_map = dict((record['id'], record) for record in records)
|
|
|
|
|
|
|
|
return [record_map[id] for id in ids if record_map.get(id)]
|
2011-06-17 16:08:49 +00:00
|
|
|
|
2011-03-17 14:06:38 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-03-21 08:13:31 +00:00
|
|
|
def load(self, req, model, id, fields):
|
2011-03-17 14:06:38 +00:00
|
|
|
m = req.session.model(model)
|
|
|
|
value = {}
|
2011-06-17 16:08:49 +00:00
|
|
|
r = m.read([id], False, req.session.eval_context(req.context))
|
2011-03-17 14:06:38 +00:00
|
|
|
if r:
|
|
|
|
value = r[0]
|
|
|
|
return {'value': value}
|
|
|
|
|
2011-04-11 11:35:16 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-06-17 16:08:49 +00:00
|
|
|
def create(self, req, model, data):
|
2011-04-11 11:35:16 +00:00
|
|
|
m = req.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
r = m.create(data, req.session.eval_context(req.context))
|
2011-04-11 11:35:16 +00:00
|
|
|
return {'result': r}
|
|
|
|
|
2011-04-05 14:32:29 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-06-17 16:08:49 +00:00
|
|
|
def save(self, req, model, id, data):
|
2011-04-05 14:32:29 +00:00
|
|
|
m = req.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
r = m.write([id], data, req.session.eval_context(req.context))
|
2011-04-05 14:32:29 +00:00
|
|
|
return {'result': r}
|
|
|
|
|
2011-05-26 21:06:41 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-06-29 13:12:33 +00:00
|
|
|
def unlink(self, request, model, ids=()):
|
2011-05-26 21:06:41 +00:00
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
return Model.unlink(ids, request.session.eval_context(request.context))
|
2011-05-26 21:06:41 +00:00
|
|
|
|
2011-06-28 15:59:56 +00:00
|
|
|
def call_common(self, req, model, method, args, domain_id=None, context_id=None):
|
2011-06-17 12:08:34 +00:00
|
|
|
domain = args[domain_id] if domain_id and len(args) - 1 >= domain_id else []
|
|
|
|
context = args[context_id] if context_id and len(args) - 1 >= context_id else {}
|
2011-06-29 13:12:33 +00:00
|
|
|
c, d = eval_context_and_domain(req.session, context, domain)
|
|
|
|
if domain_id and len(args) - 1 >= domain_id:
|
2011-06-17 16:08:49 +00:00
|
|
|
args[domain_id] = d
|
2011-06-29 13:12:33 +00:00
|
|
|
if context_id and len(args) - 1 >= context_id:
|
2011-06-17 16:08:49 +00:00
|
|
|
args[context_id] = c
|
2011-06-28 15:59:56 +00:00
|
|
|
|
2011-06-29 13:12:33 +00:00
|
|
|
return getattr(req.session.model(model), method)(*args)
|
2011-06-28 15:59:56 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def call(self, req, model, method, args, domain_id=None, context_id=None):
|
2011-07-14 10:05:22 +00:00
|
|
|
return self.call_common(req, model, method, args, domain_id, context_id)
|
2011-06-28 15:59:56 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def call_button(self, req, model, method, args, domain_id=None, context_id=None):
|
|
|
|
action = self.call_common(req, model, method, args, domain_id, context_id)
|
|
|
|
if isinstance(action, dict) and action.get('type') != '':
|
2011-06-29 15:14:00 +00:00
|
|
|
return {'result': clean_action(action, req.session)}
|
|
|
|
return {'result': False}
|
2011-04-05 14:32:29 +00:00
|
|
|
|
2011-04-21 15:56:05 +00:00
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def exec_workflow(self, req, model, id, signal):
|
|
|
|
r = req.session.exec_workflow(model, id, signal)
|
|
|
|
return {'result': r}
|
|
|
|
|
2011-04-07 13:07:25 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-06-17 14:19:45 +00:00
|
|
|
def default_get(self, req, model, fields):
|
2011-07-14 10:05:22 +00:00
|
|
|
Model = req.session.model(model)
|
|
|
|
return Model.default_get(fields, req.session.eval_context(req.context))
|
2011-04-27 12:20:51 +00:00
|
|
|
|
2011-05-10 08:34:20 +00:00
|
|
|
class DataGroup(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/group"
|
|
|
|
@openerpweb.jsonrequest
|
2011-07-13 10:37:25 +00:00
|
|
|
def read(self, request, model, fields, group_by_fields, domain=None, sort=None):
|
2011-05-10 08:34:20 +00:00
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
context, domain = eval_context_and_domain(request.session, request.context, domain)
|
2011-05-24 09:11:14 +00:00
|
|
|
|
|
|
|
return Model.read_group(
|
2011-07-11 11:32:28 +00:00
|
|
|
domain or [], fields, group_by_fields, 0, False,
|
2011-07-13 10:37:25 +00:00
|
|
|
dict(context, group_by=group_by_fields), sort or False)
|
2011-05-10 08:34:20 +00:00
|
|
|
|
2011-03-24 20:11:25 +00:00
|
|
|
class View(openerpweb.Controller):
|
2011-06-01 13:45:14 +00:00
|
|
|
_cp_path = "/base/view"
|
|
|
|
|
2011-04-11 10:31:18 +00:00
|
|
|
def fields_view_get(self, request, model, view_id, view_type,
|
|
|
|
transform=True, toolbar=False, submenu=False):
|
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
context = request.session.eval_context(request.context)
|
|
|
|
fvg = Model.fields_view_get(view_id, view_type, context, toolbar, submenu)
|
|
|
|
# todo fme?: check that we should pass the evaluated context here
|
|
|
|
self.process_view(request.session, fvg, context, transform)
|
2011-06-06 07:04:51 +00:00
|
|
|
return fvg
|
2011-07-13 10:26:12 +00:00
|
|
|
|
2011-06-06 07:04:51 +00:00
|
|
|
def process_view(self, session, fvg, context, transform):
|
2011-06-30 14:25:28 +00:00
|
|
|
# depending on how it feels, xmlrpclib.ServerProxy can translate
|
|
|
|
# XML-RPC strings to ``str`` or ``unicode``. ElementTree does not
|
|
|
|
# enjoy unicode strings which can not be trivially converted to
|
|
|
|
# strings, and it blows up during parsing.
|
|
|
|
|
|
|
|
# So ensure we fix this retardation by converting view xml back to
|
|
|
|
# bit strings.
|
|
|
|
if isinstance(fvg['arch'], unicode):
|
|
|
|
arch = fvg['arch'].encode('utf-8')
|
|
|
|
else:
|
|
|
|
arch = fvg['arch']
|
|
|
|
|
2011-03-24 20:11:25 +00:00
|
|
|
if transform:
|
2011-06-06 07:04:51 +00:00
|
|
|
evaluation_context = session.evaluation_context(context or {})
|
2011-06-30 14:25:28 +00:00
|
|
|
xml = self.transform_view(arch, session, evaluation_context)
|
2011-03-24 20:11:25 +00:00
|
|
|
else:
|
2011-06-30 14:25:28 +00:00
|
|
|
xml = ElementTree.fromstring(arch)
|
2011-04-11 10:31:18 +00:00
|
|
|
fvg['arch'] = Xml2Json.convert_element(xml)
|
2011-07-14 15:36:47 +00:00
|
|
|
|
|
|
|
for field in fvg['fields'].itervalues():
|
|
|
|
if field.get('views'):
|
|
|
|
for view in field["views"].itervalues():
|
2011-06-06 07:04:51 +00:00
|
|
|
self.process_view(session, view, None, transform)
|
2011-07-14 13:52:00 +00:00
|
|
|
if field.get('domain'):
|
|
|
|
field["domain"] = self.parse_domain(field["domain"], session)
|
|
|
|
if field.get('context'):
|
2011-07-14 15:36:47 +00:00
|
|
|
field["context"] = self.parse_context(field["context"], session)
|
2011-03-28 08:31:12 +00:00
|
|
|
|
2011-06-01 13:45:14 +00:00
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def add_custom(self, request, view_id, arch):
|
|
|
|
CustomView = request.session.model('ir.ui.view.custom')
|
|
|
|
CustomView.create({
|
|
|
|
'user_id': request.session._uid,
|
|
|
|
'ref_id': view_id,
|
|
|
|
'arch': arch
|
2011-06-17 16:08:49 +00:00
|
|
|
}, request.session.eval_context(request.context))
|
2011-06-01 13:45:14 +00:00
|
|
|
return {'result': True}
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-06-06 10:54:36 +00:00
|
|
|
def undo_custom(self, request, view_id, reset=False):
|
2011-06-01 13:45:14 +00:00
|
|
|
CustomView = request.session.model('ir.ui.view.custom')
|
2011-06-17 16:08:49 +00:00
|
|
|
context = request.session.eval_context(request.context)
|
|
|
|
vcustom = CustomView.search([('user_id', '=', request.session._uid), ('ref_id' ,'=', view_id)],
|
|
|
|
0, False, False, context)
|
2011-06-01 13:45:14 +00:00
|
|
|
if vcustom:
|
2011-06-06 10:54:36 +00:00
|
|
|
if reset:
|
2011-06-17 16:08:49 +00:00
|
|
|
CustomView.unlink(vcustom, context)
|
2011-06-06 10:54:36 +00:00
|
|
|
else:
|
2011-06-17 16:08:49 +00:00
|
|
|
CustomView.unlink([vcustom[0]], context)
|
2011-06-01 13:45:14 +00:00
|
|
|
return {'result': True}
|
|
|
|
return {'result': False}
|
|
|
|
|
2011-03-28 12:27:24 +00:00
|
|
|
def transform_view(self, view_string, session, context=None):
|
2011-03-28 08:31:12 +00:00
|
|
|
# transform nodes on the fly via iterparse, instead of
|
|
|
|
# doing it statically on the parsing result
|
|
|
|
parser = ElementTree.iterparse(StringIO(view_string), events=("start",))
|
2011-03-24 20:11:25 +00:00
|
|
|
root = None
|
|
|
|
for event, elem in parser:
|
|
|
|
if event == "start":
|
|
|
|
if root is None:
|
|
|
|
root = elem
|
2011-03-28 12:27:24 +00:00
|
|
|
self.parse_domains_and_contexts(elem, session)
|
2011-03-24 20:11:25 +00:00
|
|
|
return root
|
|
|
|
|
2011-07-14 13:52:00 +00:00
|
|
|
def parse_domain(self, domain, session):
|
|
|
|
""" Parses an arbitrary string containing a domain, transforms it
|
2011-03-29 09:09:41 +00:00
|
|
|
to either a literal domain or a :class:`openerpweb.nonliterals.Domain`
|
2011-03-28 12:27:24 +00:00
|
|
|
|
2011-07-14 13:52:00 +00:00
|
|
|
:param domain: the domain to parse, if the domain is not a string it is assumed to
|
|
|
|
be a literal domain and is returned as-is
|
|
|
|
:param session: Current OpenERP session
|
|
|
|
:type session: openerpweb.openerpweb.OpenERPSession
|
|
|
|
"""
|
|
|
|
if not isinstance(domain, (str, unicode)):
|
|
|
|
return domain
|
|
|
|
try:
|
|
|
|
return openerpweb.ast.literal_eval(domain)
|
|
|
|
except ValueError:
|
|
|
|
# not a literal
|
|
|
|
return openerpweb.nonliterals.Domain(session, domain)
|
|
|
|
|
|
|
|
def parse_context(self, context, session):
|
|
|
|
""" Parses an arbitrary string containing a context, transforms it
|
|
|
|
to either a literal context or a :class:`openerpweb.nonliterals.Context`
|
|
|
|
|
|
|
|
:param context: the context to parse, if the context is not a string it is assumed to
|
|
|
|
be a literal domain and is returned as-is
|
2011-03-29 09:09:41 +00:00
|
|
|
:param session: Current OpenERP session
|
2011-03-28 12:27:24 +00:00
|
|
|
:type session: openerpweb.openerpweb.OpenERPSession
|
|
|
|
"""
|
2011-07-14 13:52:00 +00:00
|
|
|
if not isinstance(context, (str, unicode)):
|
|
|
|
return context
|
|
|
|
try:
|
|
|
|
return openerpweb.ast.literal_eval(context)
|
|
|
|
except ValueError:
|
|
|
|
return openerpweb.nonliterals.Context(session, context)
|
2011-03-29 09:09:41 +00:00
|
|
|
|
|
|
|
def parse_domains_and_contexts(self, elem, session):
|
|
|
|
""" Converts domains and contexts from the view into Python objects,
|
|
|
|
either literals if they can be parsed by literal_eval or a special
|
|
|
|
placeholder object if the domain or context refers to free variables.
|
|
|
|
|
|
|
|
:param elem: the current node being parsed
|
|
|
|
:type param: xml.etree.ElementTree.Element
|
|
|
|
:param session: OpenERP session object, used to store and retrieve
|
|
|
|
non-literal objects
|
|
|
|
:type session: openerpweb.openerpweb.OpenERPSession
|
|
|
|
"""
|
2011-07-14 13:52:00 +00:00
|
|
|
for el in ['domain', 'filter_domain']:
|
|
|
|
domain = elem.get(el, '').strip()
|
|
|
|
if domain:
|
|
|
|
elem.set(el, self.parse_domain(domain, session))
|
2011-06-30 12:31:13 +00:00
|
|
|
for el in ['context', 'default_get']:
|
|
|
|
context_string = elem.get(el, '').strip()
|
|
|
|
if context_string:
|
2011-07-14 13:52:00 +00:00
|
|
|
elem.set(el, self.parse_context(context_string, session))
|
2011-03-24 20:11:25 +00:00
|
|
|
|
|
|
|
class FormView(View):
|
2011-03-02 18:56:06 +00:00
|
|
|
_cp_path = "/base/formview"
|
2011-03-21 08:13:31 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-04-06 14:47:51 +00:00
|
|
|
def load(self, req, model, view_id, toolbar=False):
|
2011-04-11 10:31:18 +00:00
|
|
|
fields_view = self.fields_view_get(req, model, view_id, 'form', toolbar=toolbar)
|
2011-03-24 20:11:25 +00:00
|
|
|
return {'fields_view': fields_view}
|
2011-03-21 08:13:31 +00:00
|
|
|
|
2011-03-24 20:11:25 +00:00
|
|
|
class ListView(View):
|
2011-03-02 18:56:06 +00:00
|
|
|
_cp_path = "/base/listview"
|
2011-03-21 08:13:31 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-04-06 14:47:51 +00:00
|
|
|
def load(self, req, model, view_id, toolbar=False):
|
2011-04-11 10:31:18 +00:00
|
|
|
fields_view = self.fields_view_get(req, model, view_id, 'tree', toolbar=toolbar)
|
2011-06-10 12:50:06 +00:00
|
|
|
return {'fields_view': fields_view}
|
|
|
|
|
2011-04-11 12:54:50 +00:00
|
|
|
def process_colors(self, view, row, context):
|
|
|
|
colors = view['arch']['attrs'].get('colors')
|
|
|
|
|
|
|
|
if not colors:
|
|
|
|
return None
|
|
|
|
|
|
|
|
color = [
|
|
|
|
pair.split(':')[0]
|
|
|
|
for pair in colors.split(';')
|
|
|
|
if eval(pair.split(':')[1], dict(context, **row))
|
|
|
|
]
|
|
|
|
|
|
|
|
if not color:
|
|
|
|
return None
|
|
|
|
elif len(color) == 1:
|
|
|
|
return color[0]
|
|
|
|
return 'maroon'
|
|
|
|
|
2011-03-24 20:11:25 +00:00
|
|
|
class SearchView(View):
|
2011-03-02 18:56:06 +00:00
|
|
|
_cp_path = "/base/searchview"
|
2011-03-21 08:13:31 +00:00
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
@openerpweb.jsonrequest
|
2011-03-21 08:13:31 +00:00
|
|
|
def load(self, req, model, view_id):
|
2011-04-11 10:31:18 +00:00
|
|
|
fields_view = self.fields_view_get(req, model, view_id, 'search')
|
2011-03-24 20:11:25 +00:00
|
|
|
return {'fields_view': fields_view}
|
2011-05-23 14:52:19 +00:00
|
|
|
|
2011-05-19 15:12:49 +00:00
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def fields_get(self, req, model):
|
|
|
|
Model = req.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
fields = Model.fields_get(False, req.session.eval_context(req.context))
|
2011-07-14 15:54:38 +00:00
|
|
|
for field in fields.values():
|
|
|
|
# shouldn't convert the views too?
|
|
|
|
if field.get('domain'):
|
|
|
|
field["domain"] = self.parse_domain(field["domain"], req.session)
|
|
|
|
if field.get('context'):
|
|
|
|
field["context"] = self.parse_domain(field["context"], req.session)
|
2011-05-19 15:12:49 +00:00
|
|
|
return {'fields': fields}
|
2011-03-21 08:13:31 +00:00
|
|
|
|
2011-05-23 14:52:19 +00:00
|
|
|
class Binary(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/binary"
|
|
|
|
|
|
|
|
@openerpweb.httprequest
|
|
|
|
def image(self, request, session_id, model, id, field, **kw):
|
|
|
|
cherrypy.response.headers['Content-Type'] = 'image/png'
|
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
context = request.session.eval_context(request.context)
|
2011-05-23 14:52:19 +00:00
|
|
|
try:
|
|
|
|
if not id:
|
2011-06-17 16:08:49 +00:00
|
|
|
res = Model.default_get([field], context).get(field, '')
|
2011-05-23 14:52:19 +00:00
|
|
|
else:
|
2011-06-17 16:08:49 +00:00
|
|
|
res = Model.read([int(id)], [field], context)[0].get(field, '')
|
2011-05-23 14:52:19 +00:00
|
|
|
return base64.decodestring(res)
|
|
|
|
except: # TODO: what's the exception here?
|
|
|
|
return self.placeholder()
|
|
|
|
def placeholder(self):
|
|
|
|
return open(os.path.join(openerpweb.path_addons, 'base', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
|
|
|
|
|
|
|
|
@openerpweb.httprequest
|
|
|
|
def saveas(self, request, session_id, model, id, field, fieldname, **kw):
|
|
|
|
Model = request.session.model(model)
|
2011-06-17 16:08:49 +00:00
|
|
|
context = request.session.eval_context(request.context)
|
|
|
|
res = Model.read([int(id)], [field, fieldname], context)[0]
|
2011-05-23 14:52:19 +00:00
|
|
|
filecontent = res.get(field, '')
|
|
|
|
if not filecontent:
|
|
|
|
raise cherrypy.NotFound
|
|
|
|
else:
|
|
|
|
cherrypy.response.headers['Content-Type'] = 'application/octet-stream'
|
|
|
|
filename = '%s_%s' % (model.replace('.', '_'), id)
|
|
|
|
if fieldname:
|
|
|
|
filename = res.get(fieldname, '') or filename
|
|
|
|
cherrypy.response.headers['Content-Disposition'] = 'attachment; filename=' + filename
|
|
|
|
return base64.decodestring(filecontent)
|
|
|
|
|
|
|
|
@openerpweb.httprequest
|
|
|
|
def upload(self, request, session_id, callback, ufile=None):
|
|
|
|
cherrypy.response.timeout = 500
|
|
|
|
headers = {}
|
|
|
|
for key, val in cherrypy.request.headers.iteritems():
|
|
|
|
headers[key.lower()] = val
|
|
|
|
size = int(headers.get('content-length', 0))
|
2011-06-29 13:12:33 +00:00
|
|
|
# TODO: might be useful to have a configuration flag for max-length file uploads
|
2011-05-23 14:52:19 +00:00
|
|
|
try:
|
|
|
|
out = """<script language="javascript" type="text/javascript">
|
|
|
|
var win = window.top.window,
|
|
|
|
callback = win[%s];
|
|
|
|
if (typeof(callback) === 'function') {
|
|
|
|
callback.apply(this, %s);
|
|
|
|
} else {
|
|
|
|
win.jQuery('#oe_notification', win.document).notify('create', {
|
|
|
|
title: "Ajax File Upload",
|
|
|
|
text: "Could not find callback"
|
|
|
|
});
|
|
|
|
}
|
|
|
|
</script>"""
|
|
|
|
data = ufile.file.read()
|
|
|
|
args = [size, ufile.filename, ufile.headers.getheader('Content-Type'), base64.encodestring(data)]
|
|
|
|
except Exception, e:
|
|
|
|
args = [False, e.message]
|
|
|
|
return out % (simplejson.dumps(callback), simplejson.dumps(args))
|
|
|
|
|
2011-05-26 21:06:41 +00:00
|
|
|
@openerpweb.httprequest
|
|
|
|
def upload_attachment(self, request, session_id, callback, model, id, ufile=None):
|
|
|
|
cherrypy.response.timeout = 500
|
2011-06-17 16:08:49 +00:00
|
|
|
context = request.session.eval_context(request.context)
|
2011-05-26 21:06:41 +00:00
|
|
|
Model = request.session.model('ir.attachment')
|
|
|
|
try:
|
|
|
|
out = """<script language="javascript" type="text/javascript">
|
|
|
|
var win = window.top.window,
|
|
|
|
callback = win[%s];
|
|
|
|
if (typeof(callback) === 'function') {
|
|
|
|
callback.call(this, %s);
|
|
|
|
}
|
|
|
|
</script>"""
|
|
|
|
attachment_id = Model.create({
|
|
|
|
'name': ufile.filename,
|
|
|
|
'datas': base64.encodestring(ufile.file.read()),
|
|
|
|
'res_model': model,
|
|
|
|
'res_id': int(id)
|
2011-06-17 16:08:49 +00:00
|
|
|
}, context)
|
2011-05-26 21:06:41 +00:00
|
|
|
args = {
|
|
|
|
'filename': ufile.filename,
|
|
|
|
'id': attachment_id
|
|
|
|
}
|
|
|
|
except Exception, e:
|
|
|
|
args = { 'error': e.message }
|
|
|
|
return out % (simplejson.dumps(callback), simplejson.dumps(args))
|
|
|
|
|
2011-03-02 18:56:06 +00:00
|
|
|
class Action(openerpweb.Controller):
|
|
|
|
_cp_path = "/base/action"
|
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
2011-05-31 19:52:02 +00:00
|
|
|
def load(self, req, action_id):
|
2011-04-21 13:23:54 +00:00
|
|
|
Actions = req.session.model('ir.actions.actions')
|
|
|
|
value = False
|
2011-06-17 16:08:49 +00:00
|
|
|
context = req.session.eval_context(req.context)
|
|
|
|
action_type = Actions.read([action_id], ['type'], context)
|
2011-04-21 13:23:54 +00:00
|
|
|
if action_type:
|
2011-06-17 16:08:49 +00:00
|
|
|
action = req.session.model(action_type[0]['type']).read([action_id], False,
|
2011-06-20 10:58:18 +00:00
|
|
|
context)
|
2011-04-21 13:23:54 +00:00
|
|
|
if action:
|
2011-05-31 19:52:02 +00:00
|
|
|
value = clean_action(action[0], req.session)
|
2011-04-21 16:13:43 +00:00
|
|
|
return {'result': value}
|
2011-06-30 06:24:24 +00:00
|
|
|
|
|
|
|
@openerpweb.jsonrequest
|
|
|
|
def run(self, req, action_id):
|
|
|
|
return clean_action(req.session.model('ir.actions.server').run(
|
|
|
|
[action_id], req.session.eval_context(req.context)), req.session)
|
2011-07-13 10:26:12 +00:00
|
|
|
|
|
|
|
#
|