diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index eff28b61fcd..aee0997e4b4 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -314,6 +314,23 @@ class Website(openerp.addons.web.controllers.main.Home): '/website/image///' ], auth="public", website=True) def website_image(self, model, id, field, max_width=maxint, max_height=maxint): + """ 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:`~.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 = request.registry[model] response = werkzeug.wrappers.Response() @@ -322,15 +339,17 @@ class Website(openerp.addons.web.controllers.main.Home): ids = Model.search(request.cr, request.uid, [('id', '=', id)], context=request.context) \ - or Model.search(request.cr, openerp.SUPERUSER_ID, - [('id', '=', id), ('website_published', '=', True)], context=request.context) + or 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], context=request.context) + [concurrency, field, presized], + context=request.context) if concurrency in record: server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT @@ -354,25 +373,28 @@ class Website(openerp.addons.web.controllers.main.Home): if response.status_code == 304: return response - data = record[field].decode('base64') - fit = int(max_width), int(max_height) + data = (record.get(presized) or record[field]).decode('base64') - buf = cStringIO.StringIO(data) - - image = Image.open(buf) - image.load() + 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.set_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 + response.set_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) + # 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 diff --git a/addons/website/models/website.py b/addons/website/models/website.py index f48dbd06be5..1a541865987 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -580,10 +580,26 @@ class ir_attachment(osv.osv): return hashlib.new('sha1', attachment_dict['datas']).hexdigest() return None + def _datas_big(self, cr, uid, ids, name, arg, context=None): + result = dict.fromkeys(ids, False) + if context and context.get('bin_size'): + return result + + for record in self.browse(cr, uid, ids, context=context): + if not record.datas: continue + try: + result[record.id] = openerp.tools.image_resize_image_big(record.datas) + except IOError: # apparently the error PIL.Image.open raises + pass + + return result + _columns = { 'datas_checksum': fields.function(_datas_checksum, size=40, string="Datas checksum", type='char', store=True, select=True), - 'website_url': fields.function(_website_url_get, string="Attachment URL", type='char') + 'website_url': fields.function(_website_url_get, string="Attachment URL", type='char'), + 'datas_big': fields.function (_datas_big, type='binary', store=True, + string="Resized file content"), } def create(self, cr, uid, values, context=None):