[IMP] website: refactor website_image controller (and placeholder), move to model to make it reusable

It is "almost" reusable because it still depends on the global `request` variable,
this could be solved by making it an explicit parameter.
This commit is contained in:
Olivier Dony 2014-05-14 16:32:36 +02:00
parent 8f2bbf73ae
commit 6130ed3ce0
2 changed files with 103 additions and 79 deletions

View File

@ -1,18 +1,12 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import hashlib
import json
import logging
import math
import os
import datetime
import re
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
import werkzeug.wrappers
from PIL import Image
@ -336,13 +330,7 @@ class Website(openerp.addons.web.controllers.main.Home):
return request.website.kanban_col(**post)
def placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
return request.registry['website']._image_placeholder(response)
@http.route([
'/website/image',
@ -366,74 +354,10 @@ class Website(openerp.addons.web.controllers.main.Home):
The requested field is assumed to be base64-encoded image data in
all cases.
"""
Model = request.registry[model]
response = werkzeug.wrappers.Response()
return request.registry['website']._image(
request.cr, request.uid, model, id, field, response)
id = int(id)
ids = Model.search(request.cr, request.uid,
[('id', '=', id)], context=request.context)
if not ids and 'website_published' in Model._all_columns:
ids = Model.search(request.cr, openerp.SUPERUSER_ID,
[('id', '=', id), ('website_published', '=', True)], context=request.context)
if not ids:
return self.placeholder(response)
presized = '%s_big' % field
concurrency = '__last_update'
[record] = Model.read(request.cr, openerp.SUPERUSER_ID, [id],
[concurrency, field, presized],
context=request.context)
if concurrency in record:
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format + '.%f')
except ValueError:
# just in case we have a timestamp without microseconds
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format)
# Field does not exist on model or field set to False
if not record.get(field):
# FIXME: maybe a field which does not exist should be a 404?
return self.placeholder(response)
response.set_etag(hashlib.sha1(record[field]).hexdigest())
response.make_conditional(request.httprequest)
# conditional request match
if response.status_code == 304:
return response
data = (record.get(presized) or record[field]).decode('base64')
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
# record provides a pre-resized version of the base field, use that
# directly
if record.get(presized):
response.data = data
return response
fit = int(max_width), int(max_height)
w, h = image.size
max_w, max_h = fit
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail(fit, Image.ANTIALIAS)
image.save(response.stream, image.format)
# invalidate content-length computed by make_conditional as
# writing to response.stream does not do it (as of werkzeug 0.9.3)
del response.headers['Content-Length']
return response
#------------------------------------------------------
# Server actions

View File

@ -1,13 +1,20 @@
# -*- coding: utf-8 -*-
import cStringIO
import contextlib
import datetime
import hashlib
import inspect
import itertools
import logging
import math
import mimetypes
import os
import re
import urlparse
from PIL import Image
from sys import maxint
import werkzeug
import werkzeug.exceptions
import werkzeug.utils
@ -472,6 +479,99 @@ class website(osv.osv):
html += request.website._render(template, {'object_id': object_id})
return html
def _image_placeholder(self, response):
# file_open may return a StringIO. StringIO can be closed but are
# not context managers in Python 2 though that is fixed in 3
with contextlib.closing(openerp.tools.misc.file_open(
os.path.join('web', 'static', 'src', 'img', 'placeholder.png'),
mode='rb')) as f:
response.data = f.read()
return response.make_conditional(request.httprequest)
def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, context=None):
""" Fetches the requested field and ensures it does not go above
(max_width, max_height), resizing it if necessary.
Resizing is bypassed if the object provides a $field_big, which will
be interpreted as a pre-resized version of the base field.
If the record is not found or does not have the requested field,
returns a placeholder image via :meth:`~._image_placeholder`.
Sets and checks conditional response parameters:
* :mailheader:`ETag` is always set (and checked)
* :mailheader:`Last-Modified is set iif the record has a concurrency
field (``__last_update``)
The requested field is assumed to be base64-encoded image data in
all cases.
"""
Model = self.pool[model]
id = int(id)
ids = Model.search(cr, uid,
[('id', '=', id)], context=context)
if not ids and 'website_published' in Model._all_columns:
ids = Model.search(cr, openerp.SUPERUSER_ID,
[('id', '=', id), ('website_published', '=', True)], context=context)
if not ids:
return self._image_placeholder(response)
presized = '%s_big' % field
concurrency = '__last_update'
[record] = Model.read(cr, openerp.SUPERUSER_ID, [id],
[concurrency, field, presized],
context=context)
if concurrency in record:
server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
try:
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format + '.%f')
except ValueError:
# just in case we have a timestamp without microseconds
response.last_modified = datetime.datetime.strptime(
record[concurrency], server_format)
# Field does not exist on model or field set to False
if not record.get(field):
# FIXME: maybe a field which does not exist should be a 404?
return self._image_placeholder(response)
response.set_etag(hashlib.sha1(record[field]).hexdigest())
response.make_conditional(request.httprequest)
# conditional request match
if response.status_code == 304:
return response
data = (record.get(presized) or record[field]).decode('base64')
image = Image.open(cStringIO.StringIO(data))
response.mimetype = Image.MIME[image.format]
# record provides a pre-resized version of the base field, use that
# directly
if record.get(presized):
response.data = data
return response
fit = int(max_width), int(max_height)
w, h = image.size
max_w, max_h = fit
if w < max_w and h < max_h:
response.data = data
else:
image.thumbnail(fit, Image.ANTIALIAS)
image.save(response.stream, image.format)
# invalidate content-length computed by make_conditional as
# writing to response.stream does not do it (as of werkzeug 0.9.3)
del response.headers['Content-Length']
return response
class website_menu(osv.osv):
_name = "website.menu"
_description = "Website Menu"