import openerp
from .. import http
-from .. import nonliterals
openerpweb = http
#----------------------------------------------------------
return action
-def parse_domain(domain, session):
- """ Parses an arbitrary string containing a domain, transforms it
- to either a literal domain or a :class:`nonliterals.Domain`
-
- :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.OpenERPSession
- """
- if not isinstance(domain, basestring):
- return domain
- try:
- return ast.literal_eval(domain)
- except ValueError:
- # not a literal
- return nonliterals.Domain(session, domain)
-
-def parse_context(context, session):
- """ Parses an arbitrary string containing a context, transforms it
- to either a literal context or a :class:`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
- :param session: Current OpenERP session
- :type session: openerpweb.OpenERPSession
- """
- if not isinstance(context, basestring):
- return context
- try:
- return ast.literal_eval(context)
- except ValueError:
- return nonliterals.Context(session, context)
-
def _local_web_translations(trans_file):
messages = []
try:
menu_domain = [('parent_id', '=', False)]
if user_menu_id:
- domain_string = s.model('ir.actions.act_window').read([user_menu_id[0]], ['domain'],
- req.context)[0]['domain']
+ domain_string = s.model('ir.actions.act_window').read(
+ [user_menu_id[0]], ['domain'],req.context)[0]['domain']
if domain_string:
menu_domain = ast.literal_eval(domain_string)
fvg = Model.fields_view_get(view_id, view_type, req.context, toolbar, submenu)
# todo fme?: check that we should pass the evaluated context here
self.process_view(req.session, fvg, req.context, transform, (view_type == 'kanban'))
- if toolbar and transform:
- self.process_toolbar(req, fvg['toolbar'])
return fvg
def process_view(self, session, fvg, context, transform, preserve_whitespaces=False):
arch = fvg['arch']
fvg['arch_string'] = arch
- if transform:
- evaluation_context = session.evaluation_context(context or {})
- xml = self.transform_view(arch, session, evaluation_context)
- else:
- xml = ElementTree.fromstring(arch)
- fvg['arch'] = xml2json_from_elementtree(xml, preserve_whitespaces)
+ fvg['arch'] = xml2json_from_elementtree(
+ ElementTree.fromstring(arch), preserve_whitespaces)
if 'id' in fvg['fields']:
# Special case for id's
id_field['type'] = 'id'
for field in fvg['fields'].itervalues():
- if field.get('views'):
- for view in field["views"].itervalues():
- self.process_view(session, view, None, transform)
- if field.get('domain'):
- field["domain"] = parse_domain(field["domain"], session)
- if field.get('context'):
- field["context"] = parse_context(field["context"], session)
-
- def process_toolbar(self, req, toolbar):
- """
- The toolbar is a mapping of section_key: [action_descriptor]
-
- We need to clean all those actions in order to ensure correct
- round-tripping
- """
- for actions in toolbar.itervalues():
- for action in actions:
- if 'context' in action:
- action['context'] = parse_context(
- action['context'], req.session)
- if 'domain' in action:
- action['domain'] = parse_domain(
- action['domain'], req.session)
+ for view in field.get("views", {}).itervalues():
+ self.process_view(session, view, None, transform)
@openerpweb.jsonrequest
def add_custom(self, req, view_id, arch):
return {'result': True}
return {'result': False}
- def transform_view(self, view_string, session, context=None):
- # transform nodes on the fly via iterparse, instead of
- # doing it statically on the parsing result
- parser = ElementTree.iterparse(StringIO(view_string), events=("start",))
- root = None
- for event, elem in parser:
- if event == "start":
- if root is None:
- root = elem
- self.parse_domains_and_contexts(elem, session)
- return root
-
- 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
- """
- for el in ['domain', 'filter_domain']:
- domain = elem.get(el, '').strip()
- if domain:
- elem.set(el, parse_domain(domain, session))
- elem.set(el + '_string', domain)
- for el in ['context', 'default_get']:
- context_string = elem.get(el, '').strip()
- if context_string:
- elem.set(el, parse_context(context_string, session))
- elem.set(el + '_string', context_string)
-
@openerpweb.jsonrequest
def load(self, req, model, view_id, view_type, toolbar=False):
return self.fields_view_get(req, model, view_id, view_type, toolbar=toolbar)
ctx.update(req.context)
action = req.session.model(action_type).read([action_id], False, ctx)
if action:
- value = clean_action(req, action[0], do_not_eval)
+ value = clean_action(req, action[0])
return value
@openerpweb.jsonrequest
from openerp.modules import module
from .main import module_topological_sort
-from .. import http, nonliterals
+from .. import http
NOMODULE_TEMPLATE = Template(u"""<!DOCTYPE html>
<html>
import threading
import time
import traceback
-import urllib
import urlparse
import uuid
import xmlrpclib
import openerp
-import nonliterals
import session
_logger = logging.getLogger(__name__)
if not self.session:
self.session = session.OpenERPSession()
self.httpsession[self.session_id] = self.session
- self.context = self.params.pop('context', None)
+ self.context = self.params.pop('context', {})
self.debug = self.params.pop('debug', False) is not False
# Determine self.lang
lang = self.params.get('lang', None)
# we use _ as seprator where RFC2616 uses '-'
self.lang = lang.replace('-', '_')
+def reject_nonliteral(dct):
+ if '__ref' in dct:
+ raise ValueError(
+ "Non literal contexts can not be sent to the server anymore (%r)" % (dct,))
+ return dct
class JsonRequest(WebRequest):
""" JSON-RPC2 over HTTP.
try:
# Read POST content or POST Form Data named "request"
if requestf:
- self.jsonrequest = simplejson.load(requestf, object_hook=nonliterals.non_literal_decoder)
+ self.jsonrequest = simplejson.load(requestf, object_hook=reject_nonliteral)
else:
- self.jsonrequest = simplejson.loads(request, object_hook=nonliterals.non_literal_decoder)
+ self.jsonrequest = simplejson.loads(request, object_hook=reject_nonliteral)
self.init(self.jsonrequest.get("params", {}))
if _logger.isEnabledFor(logging.DEBUG):
_logger.debug("--> %s.%s\n%s", method.im_class.__name__, method.__name__, pprint.pformat(self.jsonrequest))
# We need then to manage http sessions manually.
response['httpsessionid'] = self.httpsession.sid
mime = 'application/javascript'
- body = "%s(%s);" % (jsonp, simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder),)
+ body = "%s(%s);" % (jsonp, simplejson.dumps(response),)
else:
mime = 'application/json'
- body = simplejson.dumps(response, cls=nonliterals.NonLiteralEncoder)
+ body = simplejson.dumps(response)
r = werkzeug.wrappers.Response(body, headers=[('Content-Type', mime), ('Content-Length', len(body))])
return r
+++ /dev/null
-# -*- coding: utf-8 -*-
-""" Manages the storage and lifecycle of non-literal domains and contexts
-(and potentially other structures) which have to be evaluated with client data,
-but still need to be safely round-tripped to and from the browser (and thus
-can't be sent there themselves).
-"""
-import simplejson.encoder
-
-__all__ = ['Domain', 'Context', 'NonLiteralEncoder', 'non_literal_decoder', 'CompoundDomain', 'CompoundContext']
-
-class NonLiteralEncoder(simplejson.encoder.JSONEncoder):
- def default(self, object):
- if not isinstance(object, (BaseDomain, BaseContext)):
- return super(NonLiteralEncoder, self).default(object)
- if isinstance(object, Domain):
- return {
- '__ref': 'domain',
- '__debug': object.domain_string
- }
- elif isinstance(object, Context):
- return {
- '__ref': 'context',
- '__debug': object.context_string
- }
- raise TypeError('Could not encode unknown non-literal %s' % object)
-
-def non_literal_decoder(dct):
- if '__ref' in dct:
- raise ValueError(
- "Non literal contexts can not be sent to the server anymore (%r)" % (dct,))
- return dct
-
-# TODO: use abstract base classes if 2.6+?
-class BaseDomain(object):
- def evaluate(self, context=None):
- raise NotImplementedError('Non literals must implement evaluate()')
-
-class BaseContext(object):
- def evaluate(self, context=None):
- raise NotImplementedError('Non literals must implement evaluate()')
-
-class Domain(BaseDomain):
- def __init__(self, session, domain_string):
- """ Uses session information to store the domain string and map it to a
- domain key, which can be safely round-tripped to the client.
-
- If initialized with a domain string, will generate a key for that
- string and store the domain string out of the way. When initialized
- with a key, considers this key is a reference to an existing domain
- string.
-
- :param session: the OpenERP Session to use when evaluating the domain
- :type session: web.common.session.OpenERPSession
- :param str domain_string: a non-literal domain in string form
- """
- self.session = session
- self.domain_string = domain_string
-
- def evaluate(self, context=None):
- """ Forces the evaluation of the linked domain, using the provided
- context (as well as the session's base context), and returns the
- evaluated result.
- """
- ctx = self.session.evaluation_context(context)
-
- try:
- return eval(self.domain_string, SuperDict(ctx))
- except NameError as e:
- raise ValueError('Error during evaluation of this domain: "%s", message: "%s"' % (self.domain_string, e.message))
-
-class Context(BaseContext):
- def __init__(self, session, context_string):
- """ Uses session information to store the context string and map it to
- a key (stored in a secret location under a secret mountain), which can
- be safely round-tripped to the client.
-
- If initialized with a context string, will generate a key for that
- string and store the context string out of the way. When initialized
- with a key, considers this key is a reference to an existing context
- string.
-
- :param session: the OpenERP Session to use when evaluating the context
- :type session: web.common.session.OpenERPSession
- :param str context_string: a non-literal context in string form
- """
- self.session = session
- self.context_string = context_string
-
- def evaluate(self, context=None):
- """ Forces the evaluation of the linked context, using the provided
- context (as well as the session's base context), and returns the
- evaluated result.
- """
- ctx = self.session.evaluation_context(context)
-
- try:
- return eval(self.context_string, SuperDict(ctx))
- except NameError as e:
- raise ValueError('Error during evaluation of this context: "%s", message: "%s"' % (self.context_string, e.message))
-
-class SuperDict(dict):
- def __getattr__(self, name):
- try:
- return self[name]
- except KeyError:
- raise AttributeError(name)
- def __getitem__(self, key):
- tmp = super(SuperDict, self).__getitem__(key)
- if isinstance(tmp, dict):
- return SuperDict(tmp)
- return tmp
import openerp
-import nonliterals
-
_logger = logging.getLogger(__name__)
#----------------------------------------------------------
var self = this;
return this.rpc("/web/action/load", { action_id: options.action_id })
.then(function (result) {
- var action = result;
if (options.needaction) {
- action.context.search_default_message_unread = true;
+ result.context = new instance.web.CompoundContext(
+ result.context,
+ {search_default_message_unread: true});
}
- return $.when(self.action_manager.do_action(action, {
+ return $.when(self.action_manager.do_action(result, {
clear_breadcrumbs: true,
action_menu_id: self.menu.current_menu,
})).fail(function() {
view_id: this.view_id,
view_type: "tree",
toolbar: this.view_manager ? !!this.view_manager.sidebar : false,
- context: this.dataset.get_context()
+ context: instance.web.pyeval.eval(
+ 'context', this.dataset.get_context())
}).done(this.on_loaded);
},
/**
return this.rpc('/web/treeview/action', {
id: id,
model: this.dataset.model,
- context: new instance.web.CompoundContext(
- this.dataset.get_context(), local_context)
+ context: instance.web.pyeval.eval(
+ 'context', new instance.web.CompoundContext(
+ this.dataset.get_context(), local_context))
}).then(function (actions) {
if (!actions.length) { return; }
var action = actions[0][2];
return self.do_action(result, options);
});
}
+
+ // Ensure context & domain are evaluated and can be manipulated/used
+ if (action.context) {
+ action.context = instance.web.pyeval.eval(
+ 'context', action.context);
+ }
+ if (action.domain) {
+ action.domain = instance.web.pyeval.eval(
+ 'domain', action.domain);
+ }
+
if (!action.type) {
console.error("No type for action", action);
return $.Deferred().reject();
context: instance.web.pyeval.eval(
'context', additional_context)
}).done(function(result) {
- result.context = _.extend(result.context || {},
+ result.context = new instance.web.CompoundContext(
+ result.context,
additional_context);
result.flags = result.flags || {};
result.flags.new_window = true;
</li>
<li t-if="widget.node.attrs.context" data-item="context">
<span class="oe_tooltip_technical_title">Context:</span>
- <t t-esc="widget.node.attrs.context_string"/>
+ <t t-esc="widget.node.attrs.context"/>
</li>
<li t-if="widget.node.attrs.domain" data-item="domain">
<span class="oe_tooltip_technical_title">Domain:</span>
- <t t-esc="widget.node.attrs.domain_string"/>
+ <t t-esc="widget.node.attrs.domain"/>
</li>
<li t-if="widget.node.attrs.modifiers and widget.node.attrs.modifiers != '{}'" data-item="modifiers">
<span class="oe_tooltip_technical_title">Modifiers:</span>
# -*- coding: utf-8 -*-
-from . import test_dataset, test_menu, test_serving_base, test_view, test_js
+from . import test_dataset, test_menu, test_serving_base, test_js
fast_suite = []
checks = [
test_dataset,
test_menu,
test_serving_base,
- test_view,
]
+++ /dev/null
-import xml.etree.ElementTree
-
-import unittest2
-
-import openerp.addons.web.controllers.main
-from .. import nonliterals, session as s
-
-def field_attrs(fields_view_get, fieldname):
- (field,) = filter(lambda f: f['attrs'].get('name') == fieldname,
- fields_view_get['arch']['children'])
- return field['attrs']
-
-#noinspection PyCompatibility
-class DomainsAndContextsTest(unittest2.TestCase):
- def setUp(self):
- self.view = openerp.addons.web.controllers.main.View()
-
- def test_convert_literal_domain(self):
- e = xml.etree.ElementTree.Element(
- 'field', domain=" [('somefield', '=', 3)] ")
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertEqual(
- e.get('domain'),
- [('somefield', '=', 3)])
-
- def test_convert_complex_domain(self):
- e = xml.etree.ElementTree.Element(
- 'field',
- domain="[('account_id.type','in',['receivable','payable']),"
- "('reconcile_id','=',False),"
- "('reconcile_partial_id','=',False),"
- "('state', '=', 'valid')]"
- )
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertEqual(
- e.get('domain'),
- [('account_id.type', 'in', ['receivable', 'payable']),
- ('reconcile_id', '=', False),
- ('reconcile_partial_id', '=', False),
- ('state', '=', 'valid')]
- )
-
- def test_retrieve_nonliteral_domain(self):
- domain_string = ("[('month','=',(datetime.date.today() - "
- "datetime.timedelta(365/12)).strftime('%%m'))]")
- e = xml.etree.ElementTree.Element(
- 'field', domain=domain_string)
-
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertIsInstance(e.get('domain'), nonliterals.Domain)
-
- def test_convert_literal_context(self):
- e = xml.etree.ElementTree.Element(
- 'field', context=" {'some_prop': 3} ")
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertEqual(
- e.get('context'),
- {'some_prop': 3})
-
- def test_convert_complex_context(self):
- e = xml.etree.ElementTree.Element(
- 'field',
- context="{'account_id.type': ['receivable','payable'],"
- "'reconcile_id': False,"
- "'reconcile_partial_id': False,"
- "'state': 'valid'}"
- )
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertEqual(
- e.get('context'),
- {'account_id.type': ['receivable', 'payable'],
- 'reconcile_id': False,
- 'reconcile_partial_id': False,
- 'state': 'valid'}
- )
-
- def test_retrieve_nonliteral_context(self):
- context_string = ("{'month': (datetime.date.today() - "
- "datetime.timedelta(365/12)).strftime('%%m')}")
- e = xml.etree.ElementTree.Element(
- 'field', context=context_string)
-
- self.view.parse_domains_and_contexts(e, None)
-
- self.assertIsInstance(e.get('context'), nonliterals.Context)
-
-class AttrsNormalizationTest(unittest2.TestCase):
- def setUp(self):
- self.view = openerp.addons.web.controllers.main.View()
-
- def test_identity(self):
- web_view = """
- <form string="Title">
- <group>
- <field name="some_field"/>
- <field name="some_other_field"/>
- </group>
- <field name="stuff"/>
- </form>
- """
-
- pristine = xml.etree.ElementTree.fromstring(web_view)
- transformed = self.view.transform_view(web_view, None)
-
- self.assertEqual(
- xml.etree.ElementTree.tostring(transformed),
- xml.etree.ElementTree.tostring(pristine)
- )