diff --git a/doc/06_misc_qweb.rst b/doc/06_ir_qweb.rst similarity index 100% rename from doc/06_misc_qweb.rst rename to doc/06_ir_qweb.rst diff --git a/doc/06_misc.rst b/doc/06_misc.rst index 3a79052d995..d56b26fb018 100644 --- a/doc/06_misc.rst +++ b/doc/06_misc.rst @@ -11,4 +11,4 @@ Miscellanous 06_misc_user_img_specs.rst 06_misc_import.rst 06_misc_auto_join.rst - 06_misc_qweb.rst + 06_ir_qweb.rst diff --git a/oe b/oe index 5987cc71a8a..59827731409 100755 --- a/oe +++ b/oe @@ -1,4 +1,4 @@ -#! /usr/bin/env python +#! /usr/bin/env python2 if __name__ == '__main__': import openerpcommand.main diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 41d1a1d17e1..aef88a1b375 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -1,52 +1,25 @@ # -*- coding: utf-8 -*- +import collections import cStringIO import datetime import json import logging import math import re -import urllib -import xml # FIXME use lxml and etree +import xml # FIXME use lxml and etree import babel import babel.dates -import dateutil.relativedelta import werkzeug.utils from PIL import Image import openerp.tools +from openerp.tools.safe_eval import safe_eval as eval from openerp.osv import osv, orm, fields from openerp.tools.translate import _ _logger = logging.getLogger(__name__) -BUILTINS = { - 'False': False, - 'None': None, - 'True': True, - 'abs': abs, - 'bool': bool, - 'dict': dict, - 'filter': filter, - 'len': len, - 'list': list, - 'map': map, - 'max': max, - 'min': min, - 'reduce': reduce, - 'repr': repr, - 'round': round, - 'set': set, - 'str': str, - 'tuple': tuple, - 'quote': urllib.quote, - 'urlencode': urllib.urlencode, - 'datetime': datetime, - # dateutil.relativedelta is an old-style class and cannot be directly - # instanciated wihtin a jinja2 expression, so a lambda "proxy" is - # is needed, apparently. - 'relativedelta': lambda *a, **kw : dateutil.relativedelta.relativedelta(*a, **kw), -} class QWebException(Exception): def __init__(self, message, template=None, node=None, attribute=None): @@ -55,59 +28,27 @@ class QWebException(Exception): self.node = node self.attribute = attribute -## We use a jinja2 sandboxed environment to render qWeb templates. -#from openerp.tools.safe_eval import safe_eval as eval -#from jinja2.sandbox import SandboxedEnvironment -#from jinja2.exceptions import SecurityError, UndefinedError -#UNSAFE = ["browse", "search", "read", "unlink", "read_group"] -#SAFE = ["_name"] class QWebContext(dict): - def __init__(self, cr, uid, data, undefined_handler=None, loader=None, - templates=None, context=None): + def __init__(self, cr, uid, data, loader=None, templates=None, context=None): self.cr = cr self.uid = uid self.loader = loader - self.undefined_handler = undefined_handler self.templates = templates or {} self.context = context - dic = BUILTINS.copy() - dic.update(data) + dic = dict(data) super(QWebContext, self).__init__(dic) self['defined'] = lambda key: key in self - def __getitem__(self, key): - if key in self: - return self.get(key) - elif not self.undefined_handler: - raise NameError("QWeb: name %r is not defined while rendering template %r" % (key, self.get('__template__'))) - else: - return self.get(key, self.undefined_handler(key, self)) - def safe_eval(self, expr): - # This is too slow, we should cached compiled expressions attribute of - # qweb to will be changed into a model object ir.qweb. - # - # The cache should be on qweb, and qweb context contructor take qweb as - # argument to store the cache. - # - #class QWebSandboxedEnvironment(SandboxedEnvironment): - # def is_safe_attribute(self, obj, attr, value): - # if str(attr) in SAFE: - # res = True - # else: - # res = super(QWebSandboxedEnvironment, self).is_safe_attribute(obj, attr, value) - # if str(attr) in UNSAFE or not res: - # raise SecurityError("access to attribute '%s' of '%s' object is unsafe." % (attr,obj)) - # return res - #env = qWebSandboxedEnvironment(variable_start_string="${", variable_end_string="}") - #env.globals.update(context) - #env.compile_expression(expr)() - return eval(expr, None, self) + locals_dict = collections.defaultdict(lambda: None) + locals_dict.update(self) + locals_dict.pop('cr', None) + locals_dict.pop('loader', None) + return eval(expr, None, locals_dict, nocopy=True, locals_builtins=True) def copy(self): return QWebContext(self.cr, self.uid, dict.copy(self), - undefined_handler=self.undefined_handler, loader=self.loader, templates=self.templates, context=self.context) @@ -257,8 +198,7 @@ class QWeb(orm.AbstractModel): xmlid = imd.search_read(cr, uid, domain, ['module', 'name'])[0] return '%s.%s' % (xmlid['module'], xmlid['name']) - def render(self, cr, uid, id_or_xml_id, v=None, loader=None, - undefined_handler=None, context=None): + def render(self, cr, uid, id_or_xml_id, v=None, loader=None, context=None): if isinstance(id_or_xml_id, list): id_or_xml_id = id_or_xml_id[0] tname = id_or_xml_id @@ -268,8 +208,7 @@ class QWeb(orm.AbstractModel): if v is None: v = {} if not isinstance(v, QWebContext): - v = QWebContext(cr, uid, v, undefined_handler=undefined_handler, - loader=loader, context=context) + v = QWebContext(cr, uid, v, loader=loader, context=context) v['__template__'] = tname stack = v.get('__stack__', []) if stack: diff --git a/openerp/addons/base/ir/ir_ui_view.py b/openerp/addons/base/ir/ir_ui_view.py index 4a3d0a5fd18..29b1a45b5a7 100644 --- a/openerp/addons/base/ir/ir_ui_view.py +++ b/openerp/addons/base/ir/ir_ui_view.py @@ -795,10 +795,7 @@ class view(osv.osv): def loader(name): return self.read_template(cr, uid, name, context=context) - return self.pool[engine].render( - cr, uid, id_or_xml_id, values, - loader=loader, undefined_handler=lambda key, v: None, - context=context) + return self.pool[engine].render(cr, uid, id_or_xml_id, values, loader=loader, context=context) # maybe used to print the workflow ? diff --git a/openerp/addons/base/res/res_config.py b/openerp/addons/base/res/res_config.py index 548df0ec89e..2dedaccbc12 100644 --- a/openerp/addons/base/res/res_config.py +++ b/openerp/addons/base/res/res_config.py @@ -446,7 +446,7 @@ class res_config_settings(osv.osv_memory, res_config_module_installation_mixin): ir_module = self.pool['ir.module.module'] def ref(xml_id): mod, xml = xml_id.split('.', 1) - return ir_model_data.get_object(cr, uid, mod, xml, context=context, check_existence_and_raise=True) + return ir_model_data.get_object(cr, uid, mod, xml, context=context) defaults, groups, modules, others = [], [], [], [] for name, field in self._columns.items(): diff --git a/openerp/addons/base/res/res_users.py b/openerp/addons/base/res/res_users.py index bcce38ee3e3..43d3706d72d 100644 --- a/openerp/addons/base/res/res_users.py +++ b/openerp/addons/base/res/res_users.py @@ -681,8 +681,13 @@ class groups_view(osv.osv): def update_user_groups_view(self, cr, uid, context=None): # the view with id 'base.user_groups_view' inherits the user form view, # and introduces the reified group fields - view = self.get_user_groups_view(cr, uid, context) - if view: + # we have to try-catch this, because at first init the view does not exist + # but we are already creating some basic groups + try: + view = self.pool['ir.model.data'].get_object(cr, SUPERUSER_ID, 'base', 'user_groups_view', context=context, check_existence_and_raise=False) + except ValueError: + view = False + if view and view.exists() and view._table_name == 'ir.ui.view': xml1, xml2 = [], [] xml1.append(E.separator(string=_('Application'), colspan="4")) for app, kind, gs in self.get_groups_by_application(cr, uid, context): @@ -707,14 +712,6 @@ class groups_view(osv.osv): view.write({'arch': xml_content}) return True - def get_user_groups_view(self, cr, uid, context=None): - try: - view = self.pool['ir.model.data'].get_object(cr, SUPERUSER_ID, 'base', 'user_groups_view', context=context, check_existence_and_raise=False) - assert view and view.exists() and view._table_name == 'ir.ui.view' - except Exception: - view = False - return view - def get_application_groups(self, cr, uid, domain=None, context=None): return self.search(cr, uid, domain or []) diff --git a/openerp/addons/base/security/ir.model.access.csv b/openerp/addons/base/security/ir.model.access.csv index 28da1f46233..25df6e117d0 100644 --- a/openerp/addons/base/security/ir.model.access.csv +++ b/openerp/addons/base/security/ir.model.access.csv @@ -111,16 +111,5 @@ "access_ir_mail_server","ir_mail_server","model_ir_mail_server","group_system",1,1,1,1 "access_ir_actions_client","ir_actions_client all","model_ir_actions_client",,1,0,0,0 "access_ir_needaction_mixin","ir_needaction_mixin","model_ir_needaction_mixin",,1,1,1,1 -access_ir_qweb,access_ir_qweb,model_ir_qweb,,0,0,0,0 -access_ir_qweb_field,access_ir_qweb_field,model_ir_qweb_field,,0,0,0,0 -access_ir_qweb_field_float,access_ir_qweb_field_float,model_ir_qweb_field_float,,0,0,0,0 -access_ir_qweb_field_date,access_ir_qweb_field_date,model_ir_qweb_field_date,,0,0,0,0 -access_ir_qweb_field_datetime,access_ir_qweb_field_datetime,model_ir_qweb_field_datetime,,0,0,0,0 -access_ir_qweb_field_text,access_ir_qweb_field_text,model_ir_qweb_field_text,,0,0,0,0 -access_ir_qweb_field_selection,access_ir_qweb_field_selection,model_ir_qweb_field_selection,,0,0,0,0 -access_ir_qweb_field_many2one,access_ir_qweb_field_many2one,model_ir_qweb_field_many2one,,0,0,0,0 -access_ir_qweb_field_html,access_ir_qweb_field_html,model_ir_qweb_field_html,,0,0,0,0 -access_ir_qweb_field_image,access_ir_qweb_field_image,model_ir_qweb_field_image,,0,0,0,0 -access_ir_qweb_field_monetary,access_ir_qweb_field_monetary,model_ir_qweb_field_monetary,,0,0,0,0 "access_res_font_all","res_res_font all","model_res_font",,1,0,0,0 "access_res_font_group_user","res_res_font group_user","model_res_font","group_user",1,1,1,1 diff --git a/openerp/tools/safe_eval.py b/openerp/tools/safe_eval.py index a18d0a83fae..b65d5c1434d 100644 --- a/openerp/tools/safe_eval.py +++ b/openerp/tools/safe_eval.py @@ -174,7 +174,7 @@ def _import(name, globals=None, locals=None, fromlist=None, level=-1): return __import__(name, globals, locals, level) raise ImportError(name) -def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False): +def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=False, locals_builtins=False): """safe_eval(expression[, globals[, locals[, mode[, nocopy]]]]) -> result System-restricted Python expression evaluation @@ -218,29 +218,34 @@ def safe_eval(expr, globals_dict=None, locals_dict=None, mode="eval", nocopy=Fal locals_dict = dict(locals_dict) globals_dict.update( - __builtins__ = { - '__import__': _import, - 'True': True, - 'False': False, - 'None': None, - 'str': str, - 'globals': locals, - 'locals': locals, - 'bool': bool, - 'dict': dict, - 'list': list, - 'tuple': tuple, - 'map': map, - 'abs': abs, - 'min': min, - 'max': max, - 'reduce': reduce, - 'filter': filter, - 'round': round, - 'len': len, - 'set' : set - } + __builtins__={ + '__import__': _import, + 'True': True, + 'False': False, + 'None': None, + 'str': str, + 'globals': locals, + 'locals': locals, + 'bool': bool, + 'dict': dict, + 'list': list, + 'tuple': tuple, + 'map': map, + 'abs': abs, + 'min': min, + 'max': max, + 'reduce': reduce, + 'filter': filter, + 'round': round, + 'len': len, + 'set': set, + 'repr': repr, + } ) + if locals_builtins: + if locals_dict is None: + locals_dict = {} + locals_dict.update(globals_dict.get('__builtins__')) c = test_expr(expr, _SAFE_OPCODES, mode=mode) try: return eval(c, globals_dict, locals_dict) diff --git a/setup.py b/setup.py index aa3ce9b2049..05178fb12ac 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,7 @@ def py2exe_options(): "skip_archive": 1, "optimize": 0, # keep the assert running, because the integrated tests rely on them. "dist_dir": 'dist', - "packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "docutils", "email", "encodings", "imaplib", "jinja2", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "pytz", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml"], + "packages": [ "DAV", "HTMLParser", "PIL", "asynchat", "asyncore", "commands", "dateutil", "decimal", "docutils", "email", "encodings", "imaplib", "jinja2", "lxml", "lxml._elementpath", "lxml.builder", "lxml.etree", "lxml.objectify", "mako", "openerp", "poplib", "pychart", "pydot", "pyparsing", "pytz", "reportlab", "select", "simplejson", "smtplib", "uuid", "vatnumber", "vobject", "xml", "xml.dom", "yaml", ], "excludes" : ["Tkconstants","Tkinter","tcl"], } }