From e70f74b1066e8af3163333194c70c4d030168792 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 13:49:48 +0100 Subject: [PATCH 1/7] [CLEAN] or: reverted website-al unnecessary change bzr revid: tde@openerp.com-20140116124948-15jqamtain0ie20c --- oe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From c5262d8d94c8241c7f753f5002b305eec1305f3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 13:50:01 +0100 Subject: [PATCH 2/7] [CLEAN] setup.py: reverted website-al unnecessary change bzr revid: tde@openerp.com-20140116125001-1fkbbseyohukwcdg --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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"], } } From 051ef6d01fef01f9399cd2777ee3230c299f04f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 13:52:22 +0100 Subject: [PATCH 3/7] [CLEAN] [DOC] renamed misc_qweb.rst file into ir_qweb.rst bzr revid: tde@openerp.com-20140116125222-aag6zfjprbbnuhcu --- doc/{06_misc_qweb.rst => 06_ir_qweb.rst} | 0 doc/06_misc.rst | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename doc/{06_misc_qweb.rst => 06_ir_qweb.rst} (100%) 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 From 2c32d92ee5ebced364dcec46bf0734a713b98cb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 14:40:56 +0100 Subject: [PATCH 4/7] [CLEAN] res_config, res_users: cleaned call to imd.get_object() + code cleaning - res_config: raise by default, no need to set the argument at True - res_users: directly embedded get_user_groups_view code inside update_user_groups_view. The try / except is still necessary, because when installing a new db, groups and users are created before the user_groups_view view effectively exist. Due to some circular references, the try / except is therefore necessary to install a new db. bzr revid: tde@openerp.com-20140116134056-nyiybx7zsr8rhkiv --- openerp/addons/base/res/res_config.py | 2 +- openerp/addons/base/res/res_users.py | 17 +++++++---------- 2 files changed, 8 insertions(+), 11 deletions(-) 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 []) From 8af1afb1a0d45c67ee4665b61bfe3c916c970783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 15:06:21 +0100 Subject: [PATCH 5/7] [CLEAN] [REM] ir_qweb: removed access rules on the various ir_web models as those are abstract models. They should not have any access rules. bzr revid: tde@openerp.com-20140116140621-6mjkp82oy0iyahi7 --- openerp/addons/base/security/ir.model.access.csv | 11 ----------- 1 file changed, 11 deletions(-) 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 From c76a3aba8fa7947ac6fcb9f563f35cb25a562072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 19:27:50 +0100 Subject: [PATCH 6/7] [CLEAN] ir_qweb: simplified and cleaned QWebcontext implementation - now using openerp.tools.safe_eval, instead of a custom eval with custom builtins - removed undefined_handler, hardcoded to a lambda function that returns None for a missing attribute tools.safe_eval: added a parameter locals_builtins. This allows to copy the builtins in the locals. This hack is due to the fact that the locals always returns None, allowing to simplify templates. Otherwise we would have to test the existence of each variable before actually using it. However as the locals always return None for every key, the globals are never checked. Copying the builtins inside the local allows to have a complete locals, but sligtly break the globals/locals separation. To be reviewed and approved. bzr revid: tde@openerp.com-20140116182750-1rnw8iljt5a9gb4u --- openerp/addons/base/ir/ir_qweb.py | 114 ++++++++++----------------- openerp/addons/base/ir/ir_ui_view.py | 5 +- openerp/tools/safe_eval.py | 51 ++++++------ 3 files changed, 70 insertions(+), 100 deletions(-) diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index 41d1a1d17e1..cd76dbd13f5 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -1,52 +1,54 @@ # -*- 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), -} + +# 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 +57,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 +227,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 +237,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/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) From f4bb798cb8a3f2fe524d62cda7be462e692dda0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 16 Jan 2014 19:33:15 +0100 Subject: [PATCH 7/7] [CLEAN] ir_qweb: removed commented and now dead BUILTINS bzr revid: tde@openerp.com-20140116183315-g5ootf670kwx1z1z --- openerp/addons/base/ir/ir_qweb.py | 29 ----------------------------- 1 file changed, 29 deletions(-) diff --git a/openerp/addons/base/ir/ir_qweb.py b/openerp/addons/base/ir/ir_qweb.py index cd76dbd13f5..aef88a1b375 100644 --- a/openerp/addons/base/ir/ir_qweb.py +++ b/openerp/addons/base/ir/ir_qweb.py @@ -21,35 +21,6 @@ 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): self.message = message