[ADD] Helper for /website/image (allows to use aggressive cache)

This commit is contained in:
Fabien Meghazi 2014-09-05 18:20:55 +02:00
parent cdcd488f5a
commit bc5e6fa2cb
12 changed files with 45 additions and 51 deletions

View File

@ -8,8 +8,6 @@ import xml.etree.ElementTree as ET
import logging import logging
import re import re
from sys import maxint
import werkzeug.utils import werkzeug.utils
import urllib2 import urllib2
import werkzeug.wrappers import werkzeug.wrappers
@ -17,7 +15,7 @@ from PIL import Image
import openerp import openerp
from openerp.addons.web import http from openerp.addons.web import http
from openerp.http import request, Response from openerp.http import request, STATIC_CACHE
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -397,8 +395,8 @@ class Website(openerp.addons.web.controllers.main.Home):
@http.route([ @http.route([
'/website/image', '/website/image',
'/website/image/<model>/<id>/<field>', '/website/image/<model>-<id>-<field>',
'/website/image/<model>/<id>/<field>/<int:max_width>x<int:max_height>' '/website/image/<model>-<id>-<field>-<int:max_width>x<int:max_height>'
], auth="public", website=True) ], auth="public", website=True)
def website_image(self, model, id, field, max_width=None, max_height=None): def website_image(self, model, id, field, max_width=None, max_height=None):
""" Fetches the requested field and ensures it does not go above """ Fetches the requested field and ensures it does not go above
@ -416,9 +414,12 @@ class Website(openerp.addons.web.controllers.main.Home):
all cases. all cases.
""" """
try: try:
idsha = id.split('_')
id = idsha[0]
response = werkzeug.wrappers.Response() response = werkzeug.wrappers.Response()
return request.registry['website']._image( return request.registry['website']._image(
request.cr, request.uid, model, id, field, response, max_width, max_height) request.cr, request.uid, model, id, field, response, max_width, max_height,
cache=STATIC_CACHE if len(idsha) > 1 else None)
except Exception: except Exception:
logger.exception("Cannot render image field %r of record %s[%s] at size(%s,%s)", logger.exception("Cannot render image field %r of record %s[%s] at size(%s,%s)",
field, model, id, max_width, max_height) field, model, id, max_width, max_height)

View File

@ -286,21 +286,17 @@ class Image(orm.AbstractModel):
def record_to_html(self, cr, uid, field_name, record, column, options=None, context=None): def record_to_html(self, cr, uid, field_name, record, column, options=None, context=None):
if options is None: options = {} if options is None: options = {}
classes = ['img', 'img-responsive'] + options.get('class', '').split() aclasses = ['img', 'img-responsive'] + options.get('class', '').split()
classes = ' '.join(itertools.imap(escape, aclasses))
url_frags = { max_size = None
'classes': ' '.join(itertools.imap(escape, classes)),
'model': record._model._name,
'id': record.id,
'field': field_name,
'max_size': '',
}
max_width, max_height = options.get('max_width', 0), options.get('max_height', 0) max_width, max_height = options.get('max_width', 0), options.get('max_height', 0)
if max_width or max_height: if max_width or max_height:
url_frags['max_size'] = '/%sx%s' % (max_width, max_height) max_size = '%sx%s' % (max_width, max_height)
img = '<img class="%(classes)s" src="/website/image/%(model)s/%(id)s/%(field)s%(max_size)s"/>' src = self.pool['website'].image_url(cr, uid, record, field_name, max_size)
return ir_qweb.HTMLSafe(img % url_frags) img = '<img class="%s" src="%s"/>' % (classes, src)
return ir_qweb.HTMLSafe(img)
local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$') local_url_re = re.compile(r'^/(?P<module>[^]]+)/static/(?P<rest>.+)$')
def from_html(self, cr, uid, model, column, element, context=None): def from_html(self, cr, uid, model, column, element, context=None):

View File

@ -4,13 +4,13 @@ import contextlib
import datetime import datetime
import hashlib import hashlib
import inspect import inspect
import itertools
import logging import logging
import math import math
import mimetypes import mimetypes
import unicodedata import unicodedata
import os import os
import re import re
import time
import urlparse import urlparse
from PIL import Image from PIL import Image
@ -513,7 +513,7 @@ class website(osv.osv):
response.data = f.read() response.data = f.read()
return response.make_conditional(request.httprequest) return response.make_conditional(request.httprequest)
def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, context=None): def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, cache=None, context=None):
""" Fetches the requested field and ensures it does not go above """ Fetches the requested field and ensures it does not go above
(max_width, max_height), resizing it if necessary. (max_width, max_height), resizing it if necessary.
@ -565,6 +565,10 @@ class website(osv.osv):
response.set_etag(hashlib.sha1(record[field]).hexdigest()) response.set_etag(hashlib.sha1(record[field]).hexdigest())
response.make_conditional(request.httprequest) response.make_conditional(request.httprequest)
if cache:
response.cache_control.max_age = cache
response.expires = int(time.time() + cache)
# conditional request match # conditional request match
if response.status_code == 304: if response.status_code == 304:
return response return response
@ -593,6 +597,13 @@ class website(osv.osv):
return response return response
def image_url(self, cr, uid, record, field, size=None, context=None):
"""Returns a local url that points to the image field of a given browse record."""
model = record._name
id = '%s_%s' % (record.id, hashlib.sha1(record.write_date).hexdigest()[0:7])
size = '' if size is None else '-%s' % size
return '/website/image/%s-%s-%s%s' % (model, id, field, size)
class website_menu(osv.osv): class website_menu(osv.osv):
_name = "website.menu" _name = "website.menu"
@ -668,11 +679,7 @@ class ir_attachment(osv.osv):
if attach.url: if attach.url:
result[attach.id] = attach.url result[attach.id] = attach.url
else: else:
result[attach.id] = urlplus('/website/image', { result[attach.id] = self.pool['website'].image_url(cr, uid, attach, 'datas')
'model': 'ir.attachment',
'field': 'datas',
'id': attach.id
})
return result return result
def _datas_checksum(self, cr, uid, ids, name, arg, context=None): def _datas_checksum(self, cr, uid, ids, name, arg, context=None):
return dict( return dict(

View File

@ -123,7 +123,7 @@
<div t-foreach="blog_posts" t-as="blog_post" class="mb32"> <div t-foreach="blog_posts" t-as="blog_post" class="mb32">
<img class="img-circle pull-right mt16" <img class="img-circle pull-right mt16"
t-attf-src="/website/image/res.partner/{{ blog_post.author_id.id }}/image_small" t-att-src="website.image_url(blog_post.author_id, 'image_small')"
style="width: 50px;"/> style="width: 50px;"/>
<a t-attf-href="/blog/#{ slug(blog_post.blog_id) }/post/#{ slug(blog_post) }"> <a t-attf-href="/blog/#{ slug(blog_post.blog_id) }/post/#{ slug(blog_post) }">
@ -205,7 +205,7 @@
<h2 t-field="blog_post.subtitle"/> <h2 t-field="blog_post.subtitle"/>
<p class="post-meta text-muted text-center" name="blog_post_data"/> <p class="post-meta text-muted text-center" name="blog_post_data"/>
<div> <div>
<img class="img-circle" t-attf-src="/website/image/res.partner/{{ blog_post.author_id.id }}/image_small" style="width: 30px; margin-right: 10px;"/> <img class="img-circle" t-att-src="website.image_url(blog_post.author_id, 'image_small')" style="width: 30px; margin-right: 10px;"/>
<span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{ <span t-field="blog_post.author_id" style="display: inline-block;" t-field-options='{
"widget": "contact", "widget": "contact",
"fields": ["name"] "fields": ["name"]
@ -224,7 +224,7 @@
<ul class="media-list" id="comments-list"> <ul class="media-list" id="comments-list">
<li t-foreach="comments" t-as="message" class="media"> <li t-foreach="comments" t-as="message" class="media">
<span class="pull-left"> <span class="pull-left">
<img class="media-object img img-circle" t-attf-src="/website/image/mail.message/{{ message.id }}/author_avatar" style="width: 30px"/> <img class="media-object img img-circle" t-att-src="website.image_url(message, 'author_avatar')" style="width: 30px"/>
</span> </span>
<div class="media-body"> <div class="media-body">
<t t-call="website.publish_short"> <t t-call="website.publish_short">
@ -249,7 +249,7 @@
<h1 t-field="next_post.name"/> <h1 t-field="next_post.name"/>
<h2 t-field="next_post.subtitle"/> <h2 t-field="next_post.subtitle"/>
<div> <div>
<img class="img-circle" t-attf-src="'/website/image/res.partner/{{ next_post.author_id.id }}/image_small" style="width: 30px; margin-right: 10px;"/> <img class="img-circle" t-att-src="website.image_url(next_post.author_id, 'image_small')" style="width: 30px; margin-right: 10px;"/>
<span t-field="next_post.author_id" style="display: inline-block;" t-field-options='{ <span t-field="next_post.author_id" style="display: inline-block;" t-field-options='{
"widget": "contact", "widget": "contact",
"fields": ["name"] "fields": ["name"]
@ -292,7 +292,7 @@
<form id="comment" t-attf-action="/blogpost/comment" method="POST"> <form id="comment" t-attf-action="/blogpost/comment" method="POST">
<div class="media"> <div class="media">
<span class="pull-left"> <span class="pull-left">
<img class="img img-circle media-object" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 30px"/> <img class="img img-circle media-object" t-att-src="website.image_url(user_id.partner_id, 'image_small')" style="width: 30px"/>
</span> </span>
<div class="media-body"> <div class="media-body">
<input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/> <input name="blog_post_id" t-att-value="blog_post.id" type="hidden"/>

View File

@ -404,7 +404,7 @@
<div class="country_events_list"> <div class="country_events_list">
<div> <div>
<t t-if="country"> <t t-if="country">
<img class="img-rounded img-responsive" t-attf-src="/website/image/res.country/{{ country.id }}/image"></img> <img class="img-rounded img-responsive" t-att-src="website.image_url(country, 'image')"></img>
<h4><b>Events: <span t-esc="country.name"></span></b></h4> <h4><b>Events: <span t-esc="country.name"></span></b></h4>
</t> </t>
<t t-if="not country"> <t t-if="not country">

View File

@ -12,6 +12,3 @@ class hr(osv.osv):
_defaults = { _defaults = {
'website_published': False 'website_published': False
} }
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image/%s/%s/%s" % (self._name, ids[0], field)

View File

@ -12,7 +12,7 @@
<div t-foreach="employee_ids" t-as="employee" class="col-sm-3 col-lg-2 mt16 text-center colsize"> <div t-foreach="employee_ids" t-as="employee" class="col-sm-3 col-lg-2 mt16 text-center colsize">
<t t-call="website.publish_management"><t t-set="object" t-value="employee"/></t> <t t-call="website.publish_management"><t t-set="object" t-value="employee"/></t>
<div class="clearfix"/> <div class="clearfix"/>
<img t-att-src="employee.img('image_medium')" class="img shadow img-rounded"/> <img t-att-src="website.image_url(employee, 'image_medium')" class="img shadow img-rounded"/>
<div class="mt8"> <div class="mt8">
<strong t-field="employee.name"></strong> <strong t-field="employee.name"></strong>
</div> </div>

View File

@ -39,7 +39,7 @@
</div> </div>
<div class="row mt8" t-foreach="groups" t-as="group"> <div class="row mt8" t-foreach="groups" t-as="group">
<div class="col-md-3"> <div class="col-md-3">
<img t-attf-src="/website/image/mail.group/{{ group['id'] }}/image_small" class="pull-left"/> <img t-att-src="website.image_url(group, 'image_small')" class="pull-left"/>
<strong><a t-attf-href="/groups/#{ slug(group) }" t-esc="group.name"/></strong><br /> <strong><a t-attf-href="/groups/#{ slug(group) }" t-esc="group.name"/></strong><br />
<t t-if="group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain"> <t t-if="group.alias_id and group.alias_id.alias_name and group.alias_id.alias_domain">
<i class='fa fa-envelope-o'/> <i class='fa fa-envelope-o'/>
@ -177,7 +177,7 @@
</div> </div>
<div class="media"> <div class="media">
<img class="img-rounded pull-left mt0 media-object o_mg_avatar" <img class="img-rounded pull-left mt0 media-object o_mg_avatar"
t-attf-src="/website/image/mail.message/{{ message.id }}/author_avatar"/> t-att-src="website.image_url(message, 'author_avatar')"/>
<div class="media-body"> <div class="media-body">
<h4 class="media-heading" t-esc="message.description"/> <h4 class="media-heading" t-esc="message.description"/>
<small> <small>
@ -247,7 +247,7 @@
<ul class="media-list"> <ul class="media-list">
<li t-foreach="messages" t-as="thread" class="media"> <li t-foreach="messages" t-as="thread" class="media">
<img class="img-rounded pull-left mt0 media-object o_mg_avatar" <img class="img-rounded pull-left mt0 media-object o_mg_avatar"
t-attf-src="/website/image/mail.message/{{ thread.id }}/author_avatar"/> t-att-src="website.image_url(thread, 'author_avatar')"/>
<div class="media-body"> <div class="media-body">
<h4 class="media-heading"> <h4 class="media-heading">
<a t-attf-href="/groups/#{slug(group)}/#{slug(thread)}?mode=#{mode}&amp;date_begin=#{date_begin}&amp;date_end=#{date_end}" t-esc="thread.description"/> <a t-attf-href="/groups/#{slug(group)}/#{slug(thread)}?mode=#{mode}&amp;date_begin=#{date_begin}&amp;date_end=#{date_end}" t-esc="thread.description"/>

View File

@ -116,7 +116,7 @@
<t t-foreach="quotation.message_ids" t-as="message"> <t t-foreach="quotation.message_ids" t-as="message">
<li class="media" t-if="message.type &lt;&gt; 'comment' or message.subtype_id"> <li class="media" t-if="message.type &lt;&gt; 'comment' or message.subtype_id">
<div class="media-body"> <div class="media-body">
<img class="media-object pull-left" t-attf-src="/website/image/res.partner/{{ message.author_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/> <img class="media-object pull-left" t-att-src="website.image_url(message.author_id, 'image_small')" style="width: 50px; margin-right: 10px;"/>
<div class="media-body"> <div class="media-body">
<h5 class="media-heading"> <h5 class="media-heading">
<span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small> <span t-field="message.author_id"/> <small>on <span t-field="message.date"/></small>
@ -134,7 +134,7 @@
<xpath expr="//h1" position="after"> <xpath expr="//h1" position="after">
<section class="mb32 css_editable_mode_hidden hidden-print"> <section class="mb32 css_editable_mode_hidden hidden-print">
<form id="comment" t-attf-action="/quote/#{quotation.id}/#{quotation.access_token}/post" method="POST"> <form id="comment" t-attf-action="/quote/#{quotation.id}/#{quotation.access_token}/post" method="POST">
<img class="img pull-left img-rounded" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/> <img class="img pull-left img-rounded" t-att-src="website.image_url(user_id.partner_id, 'image_small')" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%"> <div class="pull-left mb32" style="width: 75%%">
<textarea rows="4" name="comment" class="form-control" placeholder="Send us a note..."></textarea> <textarea rows="4" name="comment" class="form-control" placeholder="Send us a note..."></textarea>
<button type="submit" class="btn btn-primary mt8">Send</button> <button type="submit" class="btn btn-primary mt8">Send</button>

View File

@ -178,9 +178,6 @@ class product_template(osv.Model):
else: else:
return self.set_sequence_bottom(cr, uid, ids, context=context) return self.set_sequence_bottom(cr, uid, ids, context=context)
def img(self, cr, uid, ids, field='image_small', context=None):
return "/website/image/%s/%s/%s" % (self._name, ids[0], field)
class product_product(osv.Model): class product_product(osv.Model):
_inherit = "product.product" _inherit = "product.product"
@ -194,10 +191,6 @@ class product_product(osv.Model):
'website_url': fields.function(_website_url, string="Website url", type="char"), 'website_url': fields.function(_website_url, string="Website url", type="char"),
} }
def img(self, cr, uid, ids, field='image_small', context=None):
temp_id = self.browse(cr, uid, ids[0], context=context).product_tmpl_id.id
return "/website/image/product.template/%s/%s" % (temp_id, field)
class product_attribute(osv.Model): class product_attribute(osv.Model):
_inherit = "product.attribute" _inherit = "product.attribute"
_columns = { _columns = {

View File

@ -121,7 +121,7 @@ $('.oe_website_sale').each(function () {
if (product_id) { if (product_id) {
var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img'); var $img = $(this).closest('tr.js_product, .oe_website_sale').find('span[data-oe-model^="product."][data-oe-type="image"] img');
$img.attr("src", "/website/image/product.product/" + product_id + "/image"); $img.attr("src", "/website/image/product.product-" + product_id + "-image");
$img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id) $img.parent().attr('data-oe-model', 'product.product').attr('data-oe-id', product_id)
.data('oe-model', 'product.product').data('oe-id', product_id); .data('oe-model', 'product.product').data('oe-id', product_id);
} }

View File

@ -77,7 +77,7 @@
</div> </div>
<div class="oe_product_image"> <div class="oe_product_image">
<a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))"> <a itemprop="url" t-att-href="keep('/shop/product/%s' % slug(product), page=(pager['page']['num'] if pager['page']['num']>1 else None))">
<img itemprop="image" class="img img-responsive" t-attf-src="/website/image/product.template/#{product.id}/image{{'' if product_image_big else '/300x300' }}"/> <img itemprop="image" class="img img-responsive" t-att-src="website.image_url(product, 'image', None if product_image_big else '300x300')"/>
</a> </a>
</div> </div>
<section> <section>
@ -374,7 +374,7 @@
<section t-attf-class="container oe_website_sale #{(compute_currency(product.lst_price) - product.price) &gt; 0.1 and 'discount'}" id="product_detail"> <section t-attf-class="container oe_website_sale #{(compute_currency(product.lst_price) - product.price) &gt; 0.1 and 'discount'}" id="product_detail">
<div class="row"> <div class="row">
<div class="col-sm-7 col-md-7 col-lg-7"> <div class="col-sm-7 col-md-7 col-lg-7">
<span itemprop="image" t-field="product.image" t-field-options='{"widget": "image", "class": "product_detail_img"}'/> <span itemprop="image" class="agr" t-field="product.image" t-field-options='{"widget": "image", "class": "product_detail_img"}'/>
</div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1"> </div><div class="col-sm-5 col-md-5 col-lg-4 col-lg-offset-1">
<h1 itemprop="name" t-field="product.name">Product Name</h1> <h1 itemprop="name" t-field="product.name">Product Name</h1>
<span itemprop="url" style="display:none;" t-esc="'/shop/product/%s' % slug(product)"/> <span itemprop="url" style="display:none;" t-esc="'/shop/product/%s' % slug(product)"/>
@ -603,7 +603,7 @@
<ul class="media-list" id="comments-list" t-if="product.website_message_ids"> <ul class="media-list" id="comments-list" t-if="product.website_message_ids">
<li t-foreach="product.website_message_ids" t-as="message" class="media"> <li t-foreach="product.website_message_ids" t-as="message" class="media">
<div class="media-body oe_msg"> <div class="media-body oe_msg">
<img class="media-object pull-left oe_msg_avatar" t-attf-src="/website/image/res.partner/{{ message.author_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/> <img class="media-object pull-left oe_msg_avatar" t-att-src="website.image_url(message.author_id, 'image_small')" style="width: 50px; margin-right: 10px;"/>
<div class="media-body oe_msg_content"> <div class="media-body oe_msg_content">
<t t-call="website.publish_short"> <t t-call="website.publish_short">
<t t-set="object" t-value="message"/> <t t-set="object" t-value="message"/>
@ -633,7 +633,7 @@
</ul> </ul>
<div class="css_editable_mode_hidden"> <div class="css_editable_mode_hidden">
<form id="comment" t-attf-action="/shop/product/comment/#{product.id}" method="POST"> <form id="comment" t-attf-action="/shop/product/comment/#{product.id}" method="POST">
<img class="img pull-left img-rounded" t-attf-src="/website/image/res.partner/{{ user_id.partner_id.id }}/image_small" style="width: 50px; margin-right: 10px;"/> <img class="img pull-left img-rounded" t-att-src="website.image_url(user_id.partner_id, 'image_small')" style="width: 50px; margin-right: 10px;"/>
<div class="pull-left mb32" style="width: 75%%"> <div class="pull-left mb32" style="width: 75%%">
<textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea> <textarea rows="3" name="comment" class="form-control" placeholder="Write a comment..."></textarea>
<a class="btn btn-primary mt8 a-submit">Post</a> <a class="btn btn-primary mt8 a-submit">Post</a>