[MERGE] Forward-port latest saas-3 bugfixes, up to 30f43da
[odoo/odoo.git] / addons / website / models / ir_http.py
index 6577383..d348d17 100644 (file)
@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
+import datetime
+import hashlib
 import logging
 import re
 import traceback
-
 import werkzeug
 import werkzeug.routing
 
@@ -78,30 +79,61 @@ class ir_http(orm.AbstractModel):
 
         return self._dispatch()
 
-    def _postprocess_args(self, arguments):
-        if hasattr(request, 'rerouting'):
-            url = request.rerouting[0]
-        else:
-            url = request.httprequest.url
-        original_url = url
-        for arg in arguments.itervalues():
-            if isinstance(arg, orm.browse_record) and isinstance(arg._uid, RequestUID):
-                placeholder = arg._uid
-                arg._uid = request.uid
-                try:
-                    good_slug = slug(arg)
-                    if str(arg.id) != placeholder.value and placeholder.value != good_slug:
-                        # TODO: properly recompose the url instead of using replace()
-                        url = url.replace(placeholder.value, good_slug)
-                except KeyError:
-                    return self._handle_exception(werkzeug.exceptions.NotFound())
-        if url != original_url:
-            werkzeug.exceptions.abort(werkzeug.utils.redirect(url))
+    def _postprocess_args(self, arguments, rule):
+        if not getattr(request, 'website_enabled', False):
+            return super(ir_http, self)._postprocess_args(arguments, rule)
+
+        for arg, val in arguments.items():
+            # Replace uid placeholder by the current request.uid
+            if isinstance(val, orm.browse_record) and isinstance(val._uid, RequestUID):
+                val._uid = request.uid
+        try:
+            _, path = rule.build(arguments)
+            assert path is not None
+        except Exception:
+            return self._handle_exception(werkzeug.exceptions.NotFound())
+
+        if request.httprequest.method in ('GET', 'HEAD'):
+            generated_path = werkzeug.url_unquote_plus(path)
+            current_path = werkzeug.url_unquote_plus(request.httprequest.path)
+            if generated_path != current_path:
+                if request.lang != request.website.default_lang_code:
+                    path = '/' + request.lang + path
+                return werkzeug.utils.redirect(path)
+
+    def _serve_attachment(self):
+        domain = [('type', '=', 'binary'), ('url', '=', request.httprequest.path)]
+        attach = self.pool['ir.attachment'].search_read(request.cr, openerp.SUPERUSER_ID, domain, ['__last_update', 'datas', 'mimetype'], context=request.context)
+        if attach:
+            wdate = attach[0]['__last_update']
+            datas = attach[0]['datas']
+            response = werkzeug.wrappers.Response()
+            server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
+            try:
+                response.last_modified = datetime.datetime.strptime(wdate, server_format + '.%f')
+            except ValueError:
+                # just in case we have a timestamp without microseconds
+                response.last_modified = datetime.datetime.strptime(wdate, server_format)
+
+            response.set_etag(hashlib.sha1(datas).hexdigest())
+            response.make_conditional(request.httprequest)
+
+            if response.status_code == 304:
+                return response
+
+            response.mimetype = attach[0]['mimetype']
+            response.data = datas.decode('base64')
+            return response
 
     def _handle_exception(self, exception=None, code=500):
         try:
             return super(ir_http, self)._handle_exception(exception)
         except Exception:
+
+        attach = self._serve_attachment()
+        if attach:
+            return attach
+
             if getattr(request, 'website_enabled', False) and request.website:
                 values = dict(
                     exception=exception,
@@ -140,8 +172,9 @@ class ir_http(orm.AbstractModel):
             raise
 
 class ModelConverter(ir.ir_http.ModelConverter):
-    def __init__(self, url_map, model=False):
+    def __init__(self, url_map, model=False, domain='[]'):
         super(ModelConverter, self).__init__(url_map, model)
+        self.domain = domain
         self.regex = r'(?:[A-Za-z0-9-_]+?-)?(\d+)(?=$|/)'
 
     def to_url(self, value):
@@ -153,24 +186,31 @@ class ModelConverter(ir.ir_http.ModelConverter):
         return request.registry[self.model].browse(
             request.cr, _uid, int(m.group(1)), context=request.context)
 
-    def generate(self, cr, uid, query=None, context=None):
-        return request.registry[self.model].name_search(
-            cr, uid, name=query or '', context=context)
+    def generate(self, cr, uid, query=None, args=None, context=None):
+        obj = request.registry[self.model]
+        domain = eval( self.domain, (args or {}).copy())
+        if query:
+            domain.append((obj._rec_name, 'ilike', '%'+query+'%'))
+        for record in obj.search_read(cr, uid, domain=domain, fields=['write_date',obj._rec_name], context=context):
+            if record.get(obj._rec_name, False):
+                yield {'loc': (record['id'], record[obj._rec_name])}
 
 class PageConverter(werkzeug.routing.PathConverter):
-    """ Only point of this converter is to bundle pages enumeration logic
-
-    Sads got: no way to get the view's human-readable name even if one exists
-    """
-    def generate(self, cr, uid, query=None, context=None):
+    """ Only point of this converter is to bundle pages enumeration logic """
+    def generate(self, cr, uid, query=None, args={}, context=None):
         View = request.registry['ir.ui.view']
-        views = View.search_read(
-            cr, uid, [['page', '=', True]],
-            fields=[], order='name', context=context)
-        xids = View.get_external_id(
-            cr, uid, [view['id'] for view in views], context=context)
-
+        views = View.search_read(cr, uid, [['page', '=', True]],
+            fields=['xml_id','priority','write_date'], order='name', context=context)
         for view in views:
-            xid = xids[view['id']]
-            if xid and (not query or query.lower() in xid.lower()):
-                yield xid
+            xid = view['xml_id'].startswith('website.') and view['xml_id'][8:] or view['xml_id']
+            # the 'page/homepage' url is indexed as '/', avoid aving the same page referenced twice
+            # when we will have an url mapping mechanism, replace this by a rule: page/homepage --> /
+            if xid=='homepage': continue
+            if query and query.lower() not in xid.lower():
+                continue
+            record = {'loc': xid}
+            if view['priority'] <> 16:
+                record['__priority'] = min(round(view['priority'] / 32.0,1), 1)
+            if view.get('write_date'):
+                record['__lastmod'] = view['write_date'][:10]
+            yield record