[IMP] change placeholder inclusion logic
[odoo/odoo.git] / addons / web / controllers / main.py
index e27a07c..cfe25bb 100644 (file)
@@ -27,8 +27,12 @@ try:
 except ImportError:
     xlwt = None
 
-from .. import common
-openerpweb = common.http
+import openerp
+import openerp.modules.registry
+from openerp.tools.translate import _
+
+from .. import http
+openerpweb = http
 
 #----------------------------------------------------------
 # OpenERP Web helpers
@@ -78,67 +82,26 @@ def rjsmin(script):
     ).strip()
     return result
 
-def sass2scss(src):
-    # Validated by diff -u of sass2scss against:
-    # sass-convert -F sass -T scss openerp.sass openerp.scss
-    block = []
-    sass = ('', block)
-    reComment = re.compile(r'//.*$')
-    reIndent = re.compile(r'^\s+')
-    reIgnore = re.compile(r'^\s*(//.*)?$')
-    reFixes = { re.compile(r'\(\((.*)\)\)') : r'(\1)', }
-    lastLevel = 0
-    prevBlocks = {}
-    for l in src.split('\n'):
-        l = l.rstrip()
-        if reIgnore.search(l): continue
-        l = reComment.sub('', l)
-        l = l.rstrip()
-        indent = reIndent.match(l)
-        level = indent.end() if indent else 0
-        l = l[level:]
-        if level>lastLevel:
-            prevBlocks[lastLevel] = block
-            newBlock = []
-            block[-1] = (block[-1], newBlock)
-            block = newBlock
-        elif level<lastLevel:
-            block = prevBlocks[level]
-        lastLevel = level
-        if not l: continue
-        # Fixes
-        for ereg, repl in reFixes.items():
-            l = ereg.sub(repl if type(repl)==str else repl(), l)
-        block.append(l)
-
-    def write(sass, level=-1):
-        out = ""
-        indent = '  '*level
-        if type(sass)==tuple:
-            if level>=0:
-                out += indent+sass[0]+" {\n"
-            for e in sass[1]:
-                out += write(e, level+1)
-            if level>=0:
-                out = out.rstrip(" \n")
-                out += ' }\n'
-            if level==0:
-                out += "\n"
-        else:
-            out += indent+sass+";\n"
-        return out
-    return write(sass)
-
 def db_list(req):
-    dbs = []
     proxy = req.session.proxy("db")
     dbs = proxy.list()
     h = req.httprequest.environ['HTTP_HOST'].split(':')[0]
     d = h.split('.')[0]
-    r = req.config.dbfilter.replace('%h', h).replace('%d', d)
+    r = openerp.tools.config['dbfilter'].replace('%h', h).replace('%d', d)
     dbs = [i for i in dbs if re.match(r, i)]
     return dbs
 
+def db_monodb(req):
+    # if only one db exists, return it else return False
+    try:
+        dbs = db_list(req)
+        if len(dbs) == 1:
+            return dbs[0]
+    except xmlrpclib.Fault:
+        # ignore access denied
+        pass
+    return False
+
 def module_topological_sort(modules):
     """ Return a list of module names sorted so that their dependencies of the
     modules are listed before the module itself
@@ -207,7 +170,6 @@ def module_installed_bypass_session(dbname):
     loadable = openerpweb.addons_manifest.keys()
     modules = {}
     try:
-        import openerp.modules.registry
         registry = openerp.modules.registry.RegistryManager.get(dbname)
         with registry.cursor() as cr:
             m = registry.get('ir.module.module')
@@ -226,21 +188,16 @@ def module_installed_bypass_session(dbname):
     sorted_modules = module_topological_sort(modules)
     return sorted_modules
 
-def module_boot(req):
+def module_boot(req, db=None):
+    server_wide_modules = openerp.conf.server_wide_modules or ['web']
     serverside = []
     dbside = []
-    for i in req.config.server_wide_modules:
+    for i in server_wide_modules:
         if i in openerpweb.addons_manifest:
             serverside.append(i)
-    # if only one db load every module at boot
-    dbs = []
-    try:
-        dbs = db_list(req)
-    except xmlrpclib.Fault:
-        # ignore access denied
-        pass
-    if len(dbs) == 1:
-        dbside = module_installed_bypass_session(dbs[0])
+    monodb = db or db_monodb(req)
+    if monodb:
+        dbside = module_installed_bypass_session(monodb)
         dbside = [i for i in dbside if i not in serverside]
     addons = serverside + dbside
     return addons
@@ -300,14 +257,24 @@ def concat_files(file_list, reader=None, intersperse=""):
     files_concat = intersperse.join(files_content)
     return files_concat, checksum.hexdigest()
 
+concat_js_cache = {}
+
 def concat_js(file_list):
     content, checksum = concat_files(file_list, intersperse=';')
-    content = rjsmin(content)
-    return content, checksum 
+    if checksum in concat_js_cache:
+        content = concat_js_cache[checksum]
+    else:
+        content = rjsmin(content)
+        concat_js_cache[checksum] = content
+    return content, checksum
+
+def fs2web(path):
+    """convert FS path into web path"""
+    return '/'.join(path.split(os.path.sep))
 
-def manifest_glob(req, addons, key):
+def manifest_glob(req, extension, addons=None, db=None):
     if addons is None:
-        addons = module_boot(req)
+        addons = module_boot(req, db=db)
     else:
         addons = addons.split(',')
     r = []
@@ -317,19 +284,21 @@ def manifest_glob(req, addons, key):
             continue
         # ensure does not ends with /
         addons_path = os.path.join(manifest['addons_path'], '')[:-1]
-        globlist = manifest.get(key, [])
+        globlist = manifest.get(extension, [])
         for pattern in globlist:
             for path in glob.glob(os.path.normpath(os.path.join(addons_path, addon, pattern))):
-                r.append((path, path[len(addons_path):]))
+                r.append((path, fs2web(path[len(addons_path):])))
     return r
 
-def manifest_list(req, mods, extension):
+def manifest_list(req, extension, mods=None, db=None):
     if not req.debug:
         path = '/web/webclient/' + extension
         if mods is not None:
             path += '?mods=' + mods
+        elif db:
+            path += '?db=' + db
         return [path]
-    files = manifest_glob(req, mods, extension)
+    files = manifest_glob(req, extension, addons=mods, db=db)
     i_am_diabetic = req.httprequest.environ["QUERY_STRING"].count("no_sugar") >= 1 or \
                     req.httprequest.environ.get('HTTP_REFERER', '').count("no_sugar") >= 1
     if i_am_diabetic:
@@ -376,7 +345,13 @@ def make_conditional(req, response, last_modified=None, etag=None):
     return response.make_conditional(req.httprequest)
 
 def login_and_redirect(req, db, login, key, redirect_url='/'):
-    req.session.authenticate(db, login, key, {})
+    wsgienv = req.httprequest.environ
+    env = dict(
+        base_location=req.httprequest.url_root.rstrip('/'),
+        HTTP_HOST=wsgienv['HTTP_HOST'],
+        REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
+    )
+    req.session.authenticate(db, login, key, env)
     return set_cookie_and_redirect(req, redirect_url)
 
 def set_cookie_and_redirect(req, redirect_url):
@@ -386,40 +361,15 @@ def set_cookie_and_redirect(req, redirect_url):
     redirect.set_cookie('instance0|session_id', cookie_val)
     return redirect
 
-def eval_context_and_domain(session, context, domain=None):
-    e_context = session.eval_context(context)
-    # should we give the evaluated context as an evaluation context to the domain?
-    e_domain = session.eval_domain(domain or [])
-
-    return e_context, e_domain
-
 def load_actions_from_ir_values(req, key, key2, models, meta):
-    context = req.session.eval_context(req.context)
     Values = req.session.model('ir.values')
-    actions = Values.get(key, key2, models, meta, context)
+    actions = Values.get(key, key2, models, meta, req.context)
 
     return [(id, name, clean_action(req, action))
             for id, name, action in actions]
 
-def clean_action(req, action, do_not_eval=False):
+def clean_action(req, action):
     action.setdefault('flags', {})
-
-    context = req.session.eval_context(req.context)
-    eval_ctx = req.session.evaluation_context(context)
-
-    if not do_not_eval:
-        # values come from the server, we can just eval them
-        if action.get('context') and isinstance(action.get('context'), basestring):
-            action['context'] = eval( action['context'], eval_ctx ) or {}
-
-        if action.get('domain') and isinstance(action.get('domain'), basestring):
-            action['domain'] = eval( action['domain'], eval_ctx ) or []
-    else:
-        if 'context' in action:
-            action['context'] = parse_context(action['context'], req.session)
-        if 'domain' in action:
-            action['domain'] = parse_domain(action['domain'], req.session)
-
     action_type = action.setdefault('type', 'ir.actions.act_window_close')
     if action_type == 'ir.actions.act_window':
         return fix_view_modes(action)
@@ -498,38 +448,56 @@ def fix_view_modes(action):
 
     return action
 
-def parse_domain(domain, session):
-    """ Parses an arbitrary string containing a domain, transforms it
-    to either a literal domain or a :class:`common.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.openerpweb.OpenERPSession
-    """
-    if not isinstance(domain, basestring):
-        return domain
+def _local_web_translations(trans_file):
+    messages = []
     try:
-        return ast.literal_eval(domain)
-    except ValueError:
-        # not a literal
-        return common.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:`common.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.openerpweb.OpenERPSession
+        with open(trans_file) as t_file:
+            po = babel.messages.pofile.read_po(t_file)
+    except Exception:
+        return
+    for x in po:
+        if x.id and x.string and "openerp-web" in x.auto_comments:
+            messages.append({'id': x.id, 'string': x.string})
+    return messages
+
+def xml2json_from_elementtree(el, preserve_whitespaces=False):
+    """ xml2json-direct
+    Simple and straightforward XML-to-JSON converter in Python
+    New BSD Licensed
+    http://code.google.com/p/xml2json-direct/
     """
-    if not isinstance(context, basestring):
-        return context
-    try:
-        return ast.literal_eval(context)
-    except ValueError:
-        return common.nonliterals.Context(session, context)
+    res = {}
+    if el.tag[0] == "{":
+        ns, name = el.tag.rsplit("}", 1)
+        res["tag"] = name
+        res["namespace"] = ns[1:]
+    else:
+        res["tag"] = el.tag
+    res["attrs"] = {}
+    for k, v in el.items():
+        res["attrs"][k] = v
+    kids = []
+    if el.text and (preserve_whitespaces or el.text.strip() != ''):
+        kids.append(el.text)
+    for kid in el:
+        kids.append(xml2json_from_elementtree(kid, preserve_whitespaces))
+        if kid.tail and (preserve_whitespaces or kid.tail.strip() != ''):
+            kids.append(kid.tail)
+    res["children"] = kids
+    return res
+
+def content_disposition(filename, req):
+    filename = filename.encode('utf8')
+    escaped = urllib2.quote(filename)
+    browser = req.httprequest.user_agent.browser
+    version = int((req.httprequest.user_agent.version or '0').split('.')[0])
+    if browser == 'msie' and version < 9:
+        return "attachment; filename=%s" % escaped
+    elif browser == 'safari':
+        return "attachment; filename=%s" % filename
+    else:
+        return "attachment; filename*=UTF-8''%s" % escaped
+
 
 #----------------------------------------------------------
 # OpenERP Web web Controllers
@@ -552,7 +520,23 @@ html_template = """<!DOCTYPE html>
             });
         </script>
     </head>
-    <body></body>
+    <body>
+        <!--[if lte IE 8]>
+            <script src="http://ajax.googleapis.com/ajax/libs/chrome-frame/1/CFInstall.min.js"></script>
+            <script>CFInstall.check({mode: "overlay"});</script>
+        <![endif]-->
+        
+         <!--[if lte IE 9]>
+            <script src="/web/static/lib/jquery.placeholder/jquery.placeholder.min.js"></script>
+            <script>
+                document.addEventListener("DOMNodeInserted",function(event){
+                    if ( $(event.target).is("input") || $(event.target).is("textarea") ) {
+                    $(event.target).placeholder();
+                    }
+                });    
+            </script>
+        <![endif]-->
+    </body>
 </html>
 """
 
@@ -560,14 +544,14 @@ class Home(openerpweb.Controller):
     _cp_path = '/'
 
     @openerpweb.httprequest
-    def index(self, req, s_action=None, **kw):
-        js = "\n        ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list(req, None, 'js'))
-        css = "\n        ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list(req, None, 'css'))
+    def index(self, req, s_action=None, db=None, **kw):
+        js = "\n        ".join('<script type="text/javascript" src="%s"></script>' % i for i in manifest_list(req, 'js', db=db))
+        css = "\n        ".join('<link rel="stylesheet" href="%s">' % i for i in manifest_list(req, 'css', db=db))
 
         r = html_template % {
             'js': js,
             'css': css,
-            'modules': simplejson.dumps(module_boot(req)),
+            'modules': simplejson.dumps(module_boot(req, db=db)),
             'init': 'var wc = new s.web.WebClient();wc.appendTo($(document.body));'
         }
         return r
@@ -581,19 +565,19 @@ class WebClient(openerpweb.Controller):
 
     @openerpweb.jsonrequest
     def csslist(self, req, mods=None):
-        return manifest_list(req, mods, 'css')
+        return manifest_list(req, 'css', mods=mods)
 
     @openerpweb.jsonrequest
     def jslist(self, req, mods=None):
-        return manifest_list(req, mods, 'js')
+        return manifest_list(req, 'js', mods=mods)
 
     @openerpweb.jsonrequest
     def qweblist(self, req, mods=None):
-        return manifest_list(req, mods, 'qweb')
+        return manifest_list(req, 'qweb', mods=mods)
 
     @openerpweb.httprequest
-    def css(self, req, mods=None):
-        files = list(manifest_glob(req, mods, 'css'))
+    def css(self, req, mods=None, db=None):
+        files = list(manifest_glob(req, 'css', addons=mods, db=db))
         last_modified = get_last_modified(f[0] for f in files)
         if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
             return werkzeug.wrappers.Response(status=304)
@@ -609,8 +593,7 @@ class WebClient(openerpweb.Controller):
                 data = fp.read().decode('utf-8')
 
             path = file_map[f]
-            # convert FS path into web path
-            web_dir = '/'.join(os.path.dirname(path).split(os.path.sep))
+            web_dir = os.path.dirname(path)
 
             data = re.sub(
                 rx_import,
@@ -632,8 +615,8 @@ class WebClient(openerpweb.Controller):
             last_modified, checksum)
 
     @openerpweb.httprequest
-    def js(self, req, mods=None):
-        files = [f[0] for f in manifest_glob(req, mods, 'js')]
+    def js(self, req, mods=None, db=None):
+        files = [f[0] for f in manifest_glob(req, 'js', addons=mods, db=db)]
         last_modified = get_last_modified(files)
         if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
             return werkzeug.wrappers.Response(status=304)
@@ -645,8 +628,8 @@ class WebClient(openerpweb.Controller):
             last_modified, checksum)
 
     @openerpweb.httprequest
-    def qweb(self, req, mods=None):
-        files = [f[0] for f in manifest_glob(req, mods, 'qweb')]
+    def qweb(self, req, mods=None, db=None):
+        files = [f[0] for f in manifest_glob(req, 'qweb', addons=mods, db=db)]
         last_modified = get_last_modified(files)
         if req.httprequest.if_modified_since and req.httprequest.if_modified_since >= last_modified:
             return werkzeug.wrappers.Response(status=304)
@@ -658,45 +641,56 @@ class WebClient(openerpweb.Controller):
             last_modified, checksum)
 
     @openerpweb.jsonrequest
+    def bootstrap_translations(self, req, mods):
+        """ Load local translations from *.po files, as a temporary solution
+            until we have established a valid session. This is meant only
+            for translating the login page and db management chrome, using
+            the browser's language. """
+        # For performance reasons we only load a single translation, so for
+        # sub-languages (that should only be partially translated) we load the
+        # main language PO instead - that should be enough for the login screen.
+        lang = req.lang.split('_')[0]
+
+        translations_per_module = {}
+        for addon_name in mods:
+            if openerpweb.addons_manifest[addon_name].get('bootstrap'):
+                addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
+                f_name = os.path.join(addons_path, addon_name, "i18n", lang + ".po")
+                if not os.path.exists(f_name):
+                    continue
+                translations_per_module[addon_name] = {'messages': _local_web_translations(f_name)}
+
+        return {"modules": translations_per_module,
+                "lang_parameters": None}
+
+    @openerpweb.jsonrequest
     def translations(self, req, mods, lang):
-        lang_model = req.session.model('res.lang')
-        ids = lang_model.search([("code", "=", lang)])
+        res_lang = req.session.model('res.lang')
+        ids = res_lang.search([("code", "=", lang)])
+        lang_params = None
         if ids:
-            lang_obj = lang_model.read(ids[0], ["direction", "date_format", "time_format",
+            lang_params = res_lang.read(ids[0], ["direction", "date_format", "time_format",
                                                 "grouping", "decimal_point", "thousands_sep"])
-        else:
-            lang_obj = None
 
-        if "_" in lang:
-            separator = "_"
-        else:
-            separator = "@"
-        langs = lang.split(separator)
-        langs = [separator.join(langs[:x]) for x in range(1, len(langs) + 1)]
-
-        transs = {}
-        for addon_name in mods:
-            transl = {"messages":[]}
-            transs[addon_name] = transl
-            addons_path = openerpweb.addons_manifest[addon_name]['addons_path']
-            for l in langs:
-                f_name = os.path.join(addons_path, addon_name, "i18n", l + ".po")
-                if not os.path.exists(f_name):
-                    continue
-                try:
-                    with open(f_name) as t_file:
-                        po = babel.messages.pofile.read_po(t_file)
-                except Exception:
-                    continue
-                for x in po:
-                    if x.id and x.string and "openerp-web" in x.auto_comments:
-                        transl["messages"].append({'id': x.id, 'string': x.string})
-        return {"modules": transs,
-                "lang_parameters": lang_obj}
+        # Regional languages (ll_CC) must inherit/override their parent lang (ll), but this is
+        # done server-side when the language is loaded, so we only need to load the user's lang.
+        ir_translation = req.session.model('ir.translation')
+        translations_per_module = {}
+        messages = ir_translation.search_read([('module','in',mods),('lang','=',lang),
+                                               ('comments','like','openerp-web'),('value','!=',False),
+                                               ('value','!=','')],
+                                              ['module','src','value','lang'], order='module')
+        for mod, msg_group in itertools.groupby(messages, key=operator.itemgetter('module')):
+            translations_per_module.setdefault(mod,{'messages':[]})
+            translations_per_module[mod]['messages'].extend({'id': m['src'],
+                                                             'string': m['value']} \
+                                                                for m in msg_group)
+        return {"modules": translations_per_module,
+                "lang_parameters": lang_params}
 
     @openerpweb.jsonrequest
     def version_info(self, req):
-        return req.proxy('common').version()['openerp']
+        return openerp.service.web_services.RPC_VERSION_1
 
 class Proxy(openerpweb.Controller):
     _cp_path = '/web/proxy'
@@ -722,21 +716,36 @@ class Database(openerpweb.Controller):
 
     @openerpweb.jsonrequest
     def get_list(self, req):
-        dbs = db_list(req)
-        return {"db_list": dbs}
+        return db_list(req)
 
     @openerpweb.jsonrequest
     def create(self, req, fields):
         params = dict(map(operator.itemgetter('name', 'value'), fields))
-        create_attrs = (
+        return req.session.proxy("db").create_database(
             params['super_admin_pwd'],
             params['db_name'],
             bool(params.get('demo_data')),
             params['db_lang'],
-            params['create_admin_pwd']
+            params['create_admin_pwd'])
+
+    @openerpweb.jsonrequest
+    def duplicate(self, req, fields):
+        params = dict(map(operator.itemgetter('name', 'value'), fields))
+        return req.session.proxy("db").duplicate_database(
+            params['super_admin_pwd'],
+            params['db_original_name'],
+            params['db_name'])
+
+    @openerpweb.jsonrequest
+    def duplicate(self, req, fields):
+        params = dict(map(operator.itemgetter('name', 'value'), fields))
+        duplicate_attrs = (
+            params['super_admin_pwd'],
+            params['db_original_name'],
+            params['db_name'],
         )
 
-        return req.session.proxy("db").create_database(*create_attrs)
+        return req.session.proxy("db").duplicate_database(*duplicate_attrs)
 
     @openerpweb.jsonrequest
     def drop(self, req, fields):
@@ -749,7 +758,7 @@ class Database(openerpweb.Controller):
         except xmlrpclib.Fault, e:
             if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
                 return {'error': e.faultCode, 'title': 'Drop Database'}
-        return {'error': 'Could not drop database !', 'title': 'Drop Database'}
+        return {'error': _('Could not drop database !'), 'title': _('Drop Database')}
 
     @openerpweb.httprequest
     def backup(self, req, backup_db, backup_pwd, token):
@@ -763,11 +772,11 @@ class Database(openerpweb.Controller):
             }
             return req.make_response(db_dump,
                [('Content-Type', 'application/octet-stream; charset=binary'),
-               ('Content-Disposition', 'attachment; filename="' + filename + '"')],
+               ('Content-Disposition', content_disposition(filename, req))],
                {'fileToken': int(token)}
             )
         except xmlrpclib.Fault, e:
-             return simplejson.dumps([[],[{'error': e.faultCode, 'title': 'backup Database'}]])
+            return simplejson.dumps([[],[{'error': e.faultCode, 'title': _('Backup Database')}]])
 
     @openerpweb.httprequest
     def restore(self, req, db_file, restore_pwd, new_db):
@@ -788,8 +797,8 @@ class Database(openerpweb.Controller):
             return req.session.proxy("db").change_admin_password(old_password, new_password)
         except xmlrpclib.Fault, e:
             if e.faultCode and e.faultCode.split(':')[0] == 'AccessDenied':
-                return {'error': e.faultCode, 'title': 'Change Password'}
-        return {'error': 'Error, password not changed !', 'title': 'Change Password'}
+                return {'error': e.faultCode, 'title': _('Change Password')}
+        return {'error': _('Error, password not changed !'), 'title': _('Change Password')}
 
 class Session(openerpweb.Controller):
     _cp_path = "/web/session"
@@ -799,9 +808,9 @@ class Session(openerpweb.Controller):
         return {
             "session_id": req.session_id,
             "uid": req.session._uid,
-            "context": req.session.get_context() if req.session._uid else {},
+            "user_context": req.session.get_context() if req.session._uid else {},
             "db": req.session._db,
-            "login": req.session._login,
+            "username": req.session._login,
         }
 
     @openerpweb.jsonrequest
@@ -811,12 +820,10 @@ class Session(openerpweb.Controller):
     @openerpweb.jsonrequest
     def authenticate(self, req, db, login, password, base_location=None):
         wsgienv = req.httprequest.environ
-        release = common.release
         env = dict(
             base_location=base_location,
             HTTP_HOST=wsgienv['HTTP_HOST'],
             REMOTE_ADDR=wsgienv['REMOTE_ADDR'],
-            user_agent="%s / %s" % (release.name, release.version),
         )
         req.session.authenticate(db, login, password, env)
 
@@ -827,31 +834,28 @@ class Session(openerpweb.Controller):
         old_password, new_password,confirm_password = operator.itemgetter('old_pwd', 'new_password','confirm_pwd')(
                 dict(map(operator.itemgetter('name', 'value'), fields)))
         if not (old_password.strip() and new_password.strip() and confirm_password.strip()):
-            return {'error':'All passwords have to be filled.','title': 'Change Password'}
+            return {'error':_('You cannot leave any password empty.'),'title': _('Change Password')}
         if new_password != confirm_password:
-            return {'error': 'The new password and its confirmation must be identical.','title': 'Change Password'}
+            return {'error': _('The new password and its confirmation must be identical.'),'title': _('Change Password')}
         try:
             if req.session.model('res.users').change_password(
                 old_password, new_password):
                 return {'new_password':new_password}
         except Exception:
-            return {'error': 'Original password incorrect, your password was not changed.', 'title': 'Change Password'}
-        return {'error': 'Error, password not changed !', 'title': 'Change Password'}
+            return {'error': _('The old password you provided is incorrect, your password was not changed.'), 'title': _('Change Password')}
+        return {'error': _('Error, password not changed !'), 'title': _('Change Password')}
 
     @openerpweb.jsonrequest
     def sc_list(self, req):
         return req.session.model('ir.ui.view_sc').get_sc(
-            req.session._uid, "ir.ui.menu", req.session.eval_context(req.context))
+            req.session._uid, "ir.ui.menu", req.context)
 
     @openerpweb.jsonrequest
     def get_lang_list(self, req):
         try:
-            return {
-                'lang_list': (req.session.proxy("db").list_lang() or []),
-                'error': ""
-            }
+            return req.session.proxy("db").list_lang() or []
         except Exception, e:
-            return {"error": e, "title": "Languages"}
+            return {"error": e, "title": _("Languages")}
 
     @openerpweb.jsonrequest
     def modules(self, req):
@@ -859,59 +863,6 @@ class Session(openerpweb.Controller):
         return module_installed(req)
 
     @openerpweb.jsonrequest
-    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)
-        """
-        context, domain = eval_context_and_domain(req.session,
-                                                  common.nonliterals.CompoundContext(*(contexts or [])),
-                                                  common.nonliterals.CompoundDomain(*(domains or [])))
-
-        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
-        }
-
-    @openerpweb.jsonrequest
     def save_session_action(self, req, the_action):
         """
         This method store an action object in the session object and returns an integer
@@ -964,14 +915,7 @@ class Menu(openerpweb.Controller):
     _cp_path = "/web/menu"
 
     @openerpweb.jsonrequest
-    def load(self, req):
-        return {'data': self.do_load(req)}
-
-    @openerpweb.jsonrequest
     def get_user_roots(self, req):
-        return self.do_get_user_roots(req)
-
-    def do_get_user_roots(self, req):
         """ Return all root menu ids visible for the session user.
 
         :param req: A request object, with an OpenERP session attribute
@@ -980,20 +924,22 @@ class Menu(openerpweb.Controller):
         :rtype: list(int)
         """
         s = req.session
-        context = s.eval_context(req.context)
         Menus = s.model('ir.ui.menu')
         # If a menu action is defined use its domain to get the root menu items
-        user_menu_id = s.model('res.users').read([s._uid], ['menu_id'], context)[0]['menu_id']
+        user_menu_id = s.model('res.users').read([s._uid], ['menu_id'],
+                                                 req.context)[0]['menu_id']
 
         menu_domain = [('parent_id', '=', False)]
         if user_menu_id:
-            domain_string = s.model('ir.actions.act_window').read([user_menu_id[0]], ['domain'], 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)
 
-        return Menus.search(menu_domain, 0, False, False, context)
+        return Menus.search(menu_domain, 0, False, False, req.context)
 
-    def do_load(self, req):
+    @openerpweb.jsonrequest
+    def load(self, req):
         """ Loads all menu items (all applications and their sub-menus).
 
         :param req: A request object, with an OpenERP session attribute
@@ -1001,23 +947,34 @@ class Menu(openerpweb.Controller):
         :return: the menu root
         :rtype: dict('children': menu_nodes)
         """
-        context = req.session.eval_context(req.context)
         Menus = req.session.model('ir.ui.menu')
 
-        menu_roots = Menus.read(self.do_get_user_roots(req), ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context)
-        menu_root = {'id': False, 'name': 'root', 'parent_id': [-1, ''], 'children' : menu_roots}
+        fields = ['name', 'sequence', 'parent_id', 'action']
+        menu_root_ids = self.get_user_roots(req)
+        menu_roots = Menus.read(menu_root_ids, fields, req.context) if menu_root_ids else []
+        menu_root = {
+            'id': False,
+            'name': 'root',
+            'parent_id': [-1, ''],
+            'children': menu_roots,
+            'all_menu_ids': menu_root_ids,
+        }
+        if not menu_roots:
+            return menu_root
 
         # menus are loaded fully unlike a regular tree view, cause there are a
         # limited number of items (752 when all 6.1 addons are installed)
-        menu_ids = Menus.search([], 0, False, False, context)
-        menu_items = Menus.read(menu_ids, ['name', 'sequence', 'parent_id', 'action', 'needaction_enabled', 'needaction_counter'], context)
+        menu_ids = Menus.search([('id', 'child_of', menu_root_ids)], 0, False, False, req.context)
+        menu_items = Menus.read(menu_ids, fields, req.context)
         # adds roots at the end of the sequence, so that they will overwrite
         # equivalent menu items from full menu read when put into id:item
         # mapping, resulting in children being correctly set on the roots.
         menu_items.extend(menu_roots)
+        menu_root['all_menu_ids'] = menu_ids # includes menu_root_ids!
 
         # make a tree using parent_id
-        menu_items_map = dict((menu_item["id"], menu_item) for menu_item in menu_items)
+        menu_items_map = dict(
+            (menu_item["id"], menu_item) for menu_item in menu_items)
         for menu_item in menu_items:
             if menu_item['parent_id']:
                 parent = menu_item['parent_id'][0]
@@ -1035,7 +992,17 @@ class Menu(openerpweb.Controller):
         return menu_root
 
     @openerpweb.jsonrequest
+    def load_needaction(self, req, menu_ids):
+        """ Loads needaction counters for specific menu ids.
+
+            :return: needaction data
+            :rtype: dict(menu_id: {'needaction_enabled': boolean, 'needaction_counter': int})
+        """
+        return req.session.model('ir.ui.menu').get_needaction_data(menu_ids, req.context)
+
+    @openerpweb.jsonrequest
     def action(self, req, menu_id):
+        # still used by web_shortcut
         actions = load_actions_from_ir_values(req,'action', 'tree_but_open',
                                              [('ir.ui.menu', menu_id)], False)
         return {"action": actions}
@@ -1067,12 +1034,10 @@ class DataSet(openerpweb.Controller):
         """
         Model = req.session.model(model)
 
-        context, domain = eval_context_and_domain(
-            req.session, req.context, domain)
-
-        ids = Model.search(domain, offset or 0, limit or False, sort or False, context)
+        ids = Model.search(domain, offset or 0, limit or False, sort or False,
+                           req.context)
         if limit and len(ids) == limit:
-            length = Model.search_count(domain, context)
+            length = Model.search_count(domain, req.context)
         else:
             length = len(ids) + (offset or 0)
         if fields and fields == ['id']:
@@ -1082,7 +1047,7 @@ class DataSet(openerpweb.Controller):
                 'records': [{'id': id} for id in ids]
             }
 
-        records = Model.read(ids, fields or False, context)
+        records = Model.read(ids, fields or False, req.context)
         records.sort(key=lambda obj: ids.index(obj['id']))
         return {
             'length': length,
@@ -1093,93 +1058,42 @@ class DataSet(openerpweb.Controller):
     def load(self, req, model, id, fields):
         m = req.session.model(model)
         value = {}
-        r = m.read([id], False, req.session.eval_context(req.context))
+        r = m.read([id], False, req.context)
         if r:
             value = r[0]
         return {'value': value}
 
     def call_common(self, req, model, method, args, domain_id=None, context_id=None):
-        has_domain = domain_id is not None and domain_id < len(args)
-        has_context = context_id is not None and context_id < len(args)
-
-        domain = args[domain_id] if has_domain else []
-        context = args[context_id] if has_context else {}
-        c, d = eval_context_and_domain(req.session, context, domain)
-        if has_domain:
-            args[domain_id] = d
-        if has_context:
-            args[context_id] = c
-
         return self._call_kw(req, model, method, args, {})
-    
-    def _call_kw(self, req, model, method, args, kwargs):
-        for i in xrange(len(args)):
-            if isinstance(args[i], common.nonliterals.BaseContext):
-                args[i] = req.session.eval_context(args[i])
-            elif isinstance(args[i], common.nonliterals.BaseDomain):
-                args[i] = req.session.eval_domain(args[i])
-        for k in kwargs.keys():
-            if isinstance(kwargs[k], common.nonliterals.BaseContext):
-                kwargs[k] = req.session.eval_context(kwargs[k])
-            elif isinstance(kwargs[k], common.nonliterals.BaseDomain):
-                kwargs[k] = req.session.eval_domain(kwargs[k])
 
+    def _call_kw(self, req, model, method, args, kwargs):
         # Temporary implements future display_name special field for model#read()
-        if method == 'read' and kwargs.get('context') and kwargs['context'].get('future_display_name'):
+        if method == 'read' and kwargs.get('context', {}).get('future_display_name'):
             if 'display_name' in args[1]:
-                names = req.session.model(model).name_get(args[0], **kwargs)
+                names = dict(req.session.model(model).name_get(args[0], **kwargs))
                 args[1].remove('display_name')
-                r = getattr(req.session.model(model), method)(*args, **kwargs)
-                for i in range(len(r)):
-                    r[i]['display_name'] = names[i][1] or "%s#%d" % (model, names[i][0])
-                return r
+                records = req.session.model(model).read(*args, **kwargs)
+                for record in records:
+                    record['display_name'] = \
+                        names.get(record['id']) or "%s#%d" % (model, (record['id']))
+                return records
 
         return getattr(req.session.model(model), method)(*args, **kwargs)
 
     @openerpweb.jsonrequest
-    def onchange(self, req, model, method, args, context_id=None):
-        """ Support method for handling onchange calls: behaves much like call
-        with the following differences:
-
-        * Does not take a domain_id
-        * Is aware of the return value's structure, and will parse the domains
-          if needed in order to return either parsed literal domains (in JSON)
-          or non-literal domain instances, allowing those domains to be used
-          from JS
-
-        :param req:
-        :type req: web.common.http.JsonRequest
-        :param str model: object type on which to call the method
-        :param str method: name of the onchange handler method
-        :param list args: arguments to call the onchange handler with
-        :param int context_id: index of the context object in the list of
-                               arguments
-        :return: result of the onchange call with all domains parsed
-        """
-        result = self.call_common(req, model, method, args, context_id=context_id)
-        if not result or 'domain' not in result:
-            return result
-
-        result['domain'] = dict(
-            (k, parse_domain(v, req.session))
-            for k, v in result['domain'].iteritems())
-
-        return result
-
-    @openerpweb.jsonrequest
     def call(self, req, model, method, args, domain_id=None, context_id=None):
-        return self.call_common(req, model, method, args, domain_id, context_id)
-    
+        return self._call_kw(req, model, method, args, {})
+
     @openerpweb.jsonrequest
     def call_kw(self, req, model, method, args, kwargs):
         return self._call_kw(req, model, method, args, kwargs)
 
     @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)
+        action = self._call_kw(req, model, method, args, {})
         if isinstance(action, dict) and action.get('type') != '':
-            return {'result': clean_action(req, action)}
-        return {'result': False}
+            return clean_action(req, action)
+        return False
 
     @openerpweb.jsonrequest
     def exec_workflow(self, req, model, id, signal):
@@ -1208,83 +1122,9 @@ class DataSet(openerpweb.Controller):
             m.write(id, { field: i + offset })
         return True
 
-class DataGroup(openerpweb.Controller):
-    _cp_path = "/web/group"
-    @openerpweb.jsonrequest
-    def read(self, req, model, fields, group_by_fields, domain=None, sort=None):
-        Model = req.session.model(model)
-        context, domain = eval_context_and_domain(req.session, req.context, domain)
-
-        return Model.read_group(
-            domain or [], fields, group_by_fields, 0, False,
-            dict(context, group_by=group_by_fields), sort or False)
-
 class View(openerpweb.Controller):
     _cp_path = "/web/view"
 
-    def fields_view_get(self, req, model, view_id, view_type,
-                        transform=True, toolbar=False, submenu=False):
-        Model = req.session.model(model)
-        context = req.session.eval_context(req.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(req.session, fvg, 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):
-        # 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']
-        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'] = common.xml2json.from_elementtree(xml, preserve_whitespaces)
-
-        if 'id' in fvg['fields']:
-            # Special case for id's
-            id_field = fvg['fields']['id']
-            id_field['original_type'] = id_field['type']
-            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)
-
     @openerpweb.jsonrequest
     def add_custom(self, req, view_id, arch):
         CustomView = req.session.model('ir.ui.view.custom')
@@ -1292,61 +1132,22 @@ class View(openerpweb.Controller):
             'user_id': req.session._uid,
             'ref_id': view_id,
             'arch': arch
-        }, req.session.eval_context(req.context))
+        }, req.context)
         return {'result': True}
 
     @openerpweb.jsonrequest
     def undo_custom(self, req, view_id, reset=False):
         CustomView = req.session.model('ir.ui.view.custom')
-        context = req.session.eval_context(req.context)
         vcustom = CustomView.search([('user_id', '=', req.session._uid), ('ref_id' ,'=', view_id)],
-                                    0, False, False, context)
+                                    0, False, False, req.context)
         if vcustom:
             if reset:
-                CustomView.unlink(vcustom, context)
+                CustomView.unlink(vcustom, req.context)
             else:
-                CustomView.unlink([vcustom[0]], context)
+                CustomView.unlink([vcustom[0]], req.context)
             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)
-
 class TreeView(View):
     _cp_path = "/web/treeview"
 
@@ -1356,50 +1157,6 @@ class TreeView(View):
             req,'action', 'tree_but_open',[(model, id)],
             False)
 
-class SearchView(View):
-    _cp_path = "/web/searchview"
-
-    @openerpweb.jsonrequest
-    def load(self, req, model, view_id):
-        fields_view = self.fields_view_get(req, model, view_id, 'search')
-        return {'fields_view': fields_view}
-
-    @openerpweb.jsonrequest
-    def fields_get(self, req, model):
-        Model = req.session.model(model)
-        fields = Model.fields_get(False, req.session.eval_context(req.context))
-        for field in fields.values():
-            # shouldn't convert the views too?
-            if field.get('domain'):
-                field["domain"] = parse_domain(field["domain"], req.session)
-            if field.get('context'):
-                field["context"] = parse_context(field["context"], req.session)
-        return {'fields': fields}
-
-    @openerpweb.jsonrequest
-    def get_filters(self, req, model):
-        logger = logging.getLogger(__name__ + '.SearchView.get_filters')
-        Model = req.session.model("ir.filters")
-        filters = Model.get_filters(model)
-        for filter in filters:
-            try:
-                parsed_context = parse_context(filter["context"], req.session)
-                filter["context"] = (parsed_context
-                        if not isinstance(parsed_context, common.nonliterals.BaseContext)
-                        else req.session.eval_context(parsed_context))
-
-                parsed_domain = parse_domain(filter["domain"], req.session)
-                filter["domain"] = (parsed_domain
-                        if not isinstance(parsed_domain, common.nonliterals.BaseDomain)
-                        else req.session.eval_domain(parsed_domain))
-            except Exception:
-                logger.exception("Failed to parse custom filter %s in %s",
-                                 filter['name'], model)
-                filter['disabled'] = True
-                del filter['context']
-                del filter['domain']
-        return filters
-
 class Binary(openerpweb.Controller):
     _cp_path = "/web/binary"
 
@@ -1407,7 +1164,6 @@ class Binary(openerpweb.Controller):
     def image(self, req, model, id, field, **kw):
         last_update = '__last_update'
         Model = req.session.model(model)
-        context = req.session.eval_context(req.context)
         headers = [('Content-Type', 'image/png')]
         etag = req.httprequest.headers.get('If-None-Match')
         hashed_session = hashlib.md5(req.session_id).hexdigest()
@@ -1418,19 +1174,32 @@ class Binary(openerpweb.Controller):
             if not id and hashed_session == etag:
                 return werkzeug.wrappers.Response(status=304)
             else:
-                date = Model.read([id], [last_update], context)[0].get(last_update)
+                date = Model.read([id], [last_update], req.context)[0].get(last_update)
                 if hashlib.md5(date).hexdigest() == etag:
                     return werkzeug.wrappers.Response(status=304)
 
         retag = hashed_session
         try:
             if not id:
-                res = Model.default_get([field], context).get(field)
-                image_data = base64.b64decode(res)
+                res = Model.default_get([field], req.context).get(field)
+                image_base64 = res
             else:
-                res = Model.read([id], [last_update, field], context)[0]
+                res = Model.read([id], [last_update, field], req.context)[0]
                 retag = hashlib.md5(res.get(last_update)).hexdigest()
-                image_data = base64.b64decode(res.get(field))
+                image_base64 = res.get(field)
+
+            if kw.get('resize'):
+                resize = kw.get('resize').split(',')
+                if len(resize) == 2 and int(resize[0]) and int(resize[1]):
+                    width = int(resize[0])
+                    height = int(resize[1])
+                    # resize maximum 500*500
+                    if width > 500: width = 500
+                    if height > 500: height = 500
+                    image_base64 = openerp.tools.image_resize_image(base64_source=image_base64, size=(width, height), encoding='base64', filetype='PNG')
+
+            image_data = base64.b64decode(image_base64)
+
         except (TypeError, xmlrpclib.Fault):
             image_data = self.placeholder(req)
         headers.append(('ETag', retag))
@@ -1441,20 +1210,10 @@ class Binary(openerpweb.Controller):
         except:
             pass
         return req.make_response(image_data, headers)
-    def placeholder(self, req):
+
+    def placeholder(self, req, image='placeholder.png'):
         addons_path = openerpweb.addons_manifest['web']['addons_path']
-        return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', 'placeholder.png'), 'rb').read()
-    def content_disposition(self, filename, req):
-        filename = filename.encode('utf8')
-        escaped = urllib2.quote(filename)
-        browser = req.httprequest.user_agent.browser
-        version = int((req.httprequest.user_agent.version or '0').split('.')[0])
-        if browser == 'msie' and version < 9:
-            return "attachment; filename=%s" % escaped
-        elif browser == 'safari':
-            return "attachment; filename=%s" % filename
-        else:
-            return "attachment; filename*=UTF-8''%s" % escaped
+        return open(os.path.join(addons_path, 'web', 'static', 'src', 'img', image), 'rb').read()
 
     @openerpweb.httprequest
     def saveas(self, req, model, field, id=None, filename_field=None, **kw):
@@ -1473,14 +1232,13 @@ class Binary(openerpweb.Controller):
         :returns: :class:`werkzeug.wrappers.Response`
         """
         Model = req.session.model(model)
-        context = req.session.eval_context(req.context)
         fields = [field]
         if filename_field:
             fields.append(filename_field)
         if id:
-            res = Model.read([int(id)], fields, context)[0]
+            res = Model.read([int(id)], fields, req.context)[0]
         else:
-            res = Model.default_get(fields, context)
+            res = Model.default_get(fields, req.context)
         filecontent = base64.b64decode(res.get(field, ''))
         if not filecontent:
             return req.not_found()
@@ -1490,29 +1248,31 @@ class Binary(openerpweb.Controller):
                 filename = res.get(filename_field, '') or filename
             return req.make_response(filecontent,
                 [('Content-Type', 'application/octet-stream'),
-                 ('Content-Disposition', self.content_disposition(filename, req))])
+                 ('Content-Disposition', content_disposition(filename, req))])
 
     @openerpweb.httprequest
     def saveas_ajax(self, req, data, token):
         jdata = simplejson.loads(data)
         model = jdata['model']
         field = jdata['field']
+        data = jdata['data']
         id = jdata.get('id', None)
         filename_field = jdata.get('filename_field', None)
-        context = jdata.get('context', dict())
+        context = jdata.get('context', {})
 
-        context = req.session.eval_context(context)
         Model = req.session.model(model)
         fields = [field]
         if filename_field:
             fields.append(filename_field)
-        if id:
+        if data:
+            res = { field: data }
+        elif id:
             res = Model.read([int(id)], fields, context)[0]
         else:
             res = Model.default_get(fields, context)
         filecontent = base64.b64decode(res.get(field, ''))
         if not filecontent:
-            raise ValueError("No content found for field '%s' on '%s:%s'" %
+            raise ValueError(_("No content found for field '%s' on '%s:%s'") %
                 (field, model, id))
         else:
             filename = '%s_%s' % (model.replace('.', '_'), id)
@@ -1520,17 +1280,17 @@ class Binary(openerpweb.Controller):
                 filename = res.get(filename_field, '') or filename
             return req.make_response(filecontent,
                 headers=[('Content-Type', 'application/octet-stream'),
-                        ('Content-Disposition', self.content_disposition(filename, req))],
+                        ('Content-Disposition', content_disposition(filename, req))],
                 cookies={'fileToken': int(token)})
 
     @openerpweb.httprequest
     def upload(self, req, callback, ufile):
         # TODO: might be useful to have a configuration flag for max-length file uploads
+        out = """<script language="javascript" type="text/javascript">
+                    var win = window.top.window;
+                    win.jQuery(win).trigger(%s, %s);
+                </script>"""
         try:
-            out = """<script language="javascript" type="text/javascript">
-                        var win = window.top.window;
-                        win.jQuery(win).trigger(%s, %s);
-                    </script>"""
             data = ufile.read()
             args = [len(data), ufile.filename,
                     ufile.content_type, base64.b64encode(data)]
@@ -1540,44 +1300,63 @@ class Binary(openerpweb.Controller):
 
     @openerpweb.httprequest
     def upload_attachment(self, req, callback, model, id, ufile):
-        context = req.session.eval_context(req.context)
         Model = req.session.model('ir.attachment')
+        out = """<script language="javascript" type="text/javascript">
+                    var win = window.top.window;
+                    win.jQuery(win).trigger(%s, %s);
+                </script>"""
         try:
-            out = """<script language="javascript" type="text/javascript">
-                        var win = window.top.window;
-                        win.jQuery(win).trigger(%s, %s);
-                    </script>"""
             attachment_id = Model.create({
                 'name': ufile.filename,
                 'datas': base64.encodestring(ufile.read()),
                 'datas_fname': ufile.filename,
                 'res_model': model,
                 'res_id': int(id)
-            }, context)
+            }, req.context)
             args = {
                 'filename': ufile.filename,
                 'id':  attachment_id
             }
-        except Exception, e:
-            args = { 'error': e.message }
+        except xmlrpclib.Fault, e:
+            args = {'error':e.faultCode }
         return out % (simplejson.dumps(callback), simplejson.dumps(args))
 
+    @openerpweb.httprequest
+    def company_logo(self, req, dbname=None):
+        # TODO add etag, refactor to use /image code for etag
+        uid = None
+        if req.session._db:
+            dbname = req.session._db
+            uid = req.session._uid
+        elif dbname is None:
+            dbname = db_monodb(req)
+
+        if uid is None:
+            uid = openerp.SUPERUSER_ID
+
+        if not dbname:
+            image_data = self.placeholder(req, 'logo.png')
+        else:
+            registry = openerp.modules.registry.RegistryManager.get(dbname)
+            with registry.cursor() as cr:
+                user = registry.get('res.users').browse(cr, uid, uid)
+                if user.company_id.logo_web:
+                    image_data = user.company_id.logo_web.decode('base64')
+                else:
+                    image_data = self.placeholder(req, 'nologo.png')
+        headers = [
+            ('Content-Type', 'image/png'),
+            ('Content-Length', len(image_data)),
+        ]
+        return req.make_response(image_data, headers)
+
 class Action(openerpweb.Controller):
     _cp_path = "/web/action"
 
-    # For most actions, the type attribute and the model name are the same, but
-    # there are exceptions. This dict is used to remap action type attributes
-    # to the "real" model name when they differ.
-    action_mapping = {
-        "ir.actions.act_url": "ir.actions.url",
-    }
-
     @openerpweb.jsonrequest
     def load(self, req, action_id, do_not_eval=False):
         Actions = req.session.model('ir.actions.actions')
         value = False
-        context = req.session.eval_context(req.context)
-
         try:
             action_id = int(action_id)
         except ValueError:
@@ -1588,23 +1367,22 @@ class Action(openerpweb.Controller):
             except Exception:
                 action_id = 0   # force failed read
 
-        base_action = Actions.read([action_id], ['type'], context)
+        base_action = Actions.read([action_id], ['type'], req.context)
         if base_action:
             ctx = {}
             action_type = base_action[0]['type']
             if action_type == 'ir.actions.report.xml':
                 ctx.update({'bin_size': True})
-            ctx.update(context)
-            action_model = self.action_mapping.get(action_type, action_type)
-            action = req.session.model(action_model).read([action_id], False, ctx)
+            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)
-        return {'result': value}
+                value = clean_action(req, action[0])
+        return value
 
     @openerpweb.jsonrequest
     def run(self, req, action_id):
         return_action = req.session.model('ir.actions.server').run(
-            [action_id], req.session.eval_context(req.context))
+            [action_id], req.context)
         if return_action:
             return clean_action(req, return_action)
         else:
@@ -1629,7 +1407,7 @@ class Export(View):
 
     def fields_get(self, req, model):
         Model = req.session.model(model)
-        fields = Model.fields_get(False, req.session.eval_context(req.context))
+        fields = Model.fields_get(False, req.context)
         return fields
 
     @openerpweb.jsonrequest
@@ -1699,6 +1477,8 @@ class Export(View):
     def fields_info(self, req, model, export_fields):
         info = {}
         fields = self.fields_get(req, model)
+        if ".id" in export_fields:
+            fields['.id'] = fields.pop('id', {'string': 'ID'})
 
         # To make fields retrieval more efficient, fetch all sub-fields of a
         # given field at the same time. Because the order in the export list is
@@ -1779,12 +1559,11 @@ class Export(View):
                                 'import_compat')(
                 simplejson.loads(data))
 
-        context = req.session.eval_context(req.context)
         Model = req.session.model(model)
-        ids = ids or Model.search(domain, 0, False, False, context)
+        ids = ids or Model.search(domain, 0, False, False, req.context)
 
         field_names = map(operator.itemgetter('name'), fields)
-        import_data = Model.export_data(ids, field_names, context).get('datas',[])
+        import_data = Model.export_data(ids, field_names, req.context).get('datas',[])
 
         if import_compat:
             columns_headers = field_names
@@ -1793,7 +1572,8 @@ class Export(View):
 
 
         return req.make_response(self.from_data(columns_headers, import_data),
-            headers=[('Content-Disposition', 'attachment; filename="%s"' % self.filename(model)),
+            headers=[('Content-Disposition',
+                            content_disposition(self.filename(model), req)),
                      ('Content-Type', self.content_type)],
             cookies={'fileToken': int(token)})
 
@@ -1888,9 +1668,8 @@ class Reports(View):
         action = simplejson.loads(action)
 
         report_srv = req.session.proxy("report")
-        context = req.session.eval_context(
-            common.nonliterals.CompoundContext(
-                req.context or {}, action[ "context"]))
+        context = dict(req.context)
+        context.update(action["context"])
 
         report_data = {}
         report_ids = context["active_ids"]
@@ -1929,121 +1708,13 @@ class Reports(View):
                 file_name = reports.read(res_id[0], ['name'], context)['name']
             else:
                 file_name = action['report_name']
+        file_name = '%s.%s' % (file_name, report_struct['format'])
 
         return req.make_response(report,
              headers=[
-                 # maybe we should take of what characters can appear in a file name?
-                 ('Content-Disposition', 'attachment; filename="%s.%s"' % (file_name, report_struct['format'])),
+                 ('Content-Disposition', content_disposition(file_name, req)),
                  ('Content-Type', report_mimetype),
                  ('Content-Length', len(report))],
              cookies={'fileToken': int(token)})
 
-class Import(View):
-    _cp_path = "/web/import"
-
-    def fields_get(self, req, model):
-        Model = req.session.model(model)
-        fields = Model.fields_get(False, req.session.eval_context(req.context))
-        return fields
-
-    @openerpweb.httprequest
-    def detect_data(self, req, csvfile, csvsep=',', csvdel='"', csvcode='utf-8', jsonp='callback'):
-        try:
-            data = list(csv.reader(
-                csvfile, quotechar=str(csvdel), delimiter=str(csvsep)))
-        except csv.Error, e:
-            csvfile.seek(0)
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({'error': {
-                    'message': 'Error parsing CSV file: %s' % e,
-                    # decodes each byte to a unicode character, which may or
-                    # may not be printable, but decoding will succeed.
-                    # Otherwise simplejson will try to decode the `str` using
-                    # utf-8, which is very likely to blow up on characters out
-                    # of the ascii range (in range [128, 256))
-                    'preview': csvfile.read(200).decode('iso-8859-1')}}))
-
-        try:
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps(
-                    {'records': data[:10]}, encoding=csvcode))
-        except UnicodeDecodeError:
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({
-                    'message': u"Failed to decode CSV file using encoding %s, "
-                               u"try switching to a different encoding" % csvcode
-                }))
-
-    @openerpweb.httprequest
-    def import_data(self, req, model, csvfile, csvsep, csvdel, csvcode, jsonp,
-                    meta):
-        modle_obj = req.session.model(model)
-        skip, indices, fields = operator.itemgetter('skip', 'indices', 'fields')(
-            simplejson.loads(meta))
-
-        error = None
-        if not (csvdel and len(csvdel) == 1):
-            error = u"The CSV delimiter must be a single character"
-
-        if not indices and fields:
-            error = u"You must select at least one field to import"
-
-        if error:
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({'error': {'message': error}}))
-
-        # skip ignored records (@skip parameter)
-        # then skip empty lines (not valid csv)
-        # nb: should these operations be reverted?
-        rows_to_import = itertools.ifilter(
-            None,
-            itertools.islice(
-                csv.reader(csvfile, quotechar=str(csvdel), delimiter=str(csvsep)),
-                skip, None))
-
-        # if only one index, itemgetter will return an atom rather than a tuple
-        if len(indices) == 1: mapper = lambda row: [row[indices[0]]]
-        else: mapper = operator.itemgetter(*indices)
-
-        data = None
-        error = None
-        try:
-            # decode each data row
-            data = [
-                [record.decode(csvcode) for record in row]
-                for row in itertools.imap(mapper, rows_to_import)
-                # don't insert completely empty rows (can happen due to fields
-                # filtering in case of e.g. o2m content rows)
-                if any(row)
-            ]
-        except UnicodeDecodeError:
-            error = u"Failed to decode CSV file using encoding %s" % csvcode
-        except csv.Error, e:
-            error = u"Could not process CSV file: %s" % e
-
-        # If the file contains nothing,
-        if not data:
-            error = u"File to import is empty"
-        if error:
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({'error': {'message': error}}))
-
-        try:
-            (code, record, message, _nope) = modle_obj.import_data(
-                fields, data, 'init', '', False,
-                req.session.eval_context(req.context))
-        except xmlrpclib.Fault, e:
-            error = {"message": u"%s, %s" % (e.faultCode, e.faultString)}
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({'error':error}))
-
-        if code != -1:
-            return '<script>window.top.%s(%s);</script>' % (
-                jsonp, simplejson.dumps({'success':True}))
-
-        msg = u"Error during import: %s\n\nTrying to import record %r" % (
-            message, record)
-        return '<script>window.top.%s(%s);</script>' % (
-            jsonp, simplejson.dumps({'error': {'message':msg}}))
-
 # vim:expandtab:tabstop=4:softtabstop=4:shiftwidth=4: