website.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import os
  2. import time
  3. import hashlib
  4. import datetime
  5. import cStringIO
  6. from PIL import Image
  7. from sys import maxint
  8. import openerp
  9. from openerp.osv import osv, orm
  10. from openerp.addons.web.http import request
  11. from openerp.tools import image_resize_and_sharpen, image_save_for_web
  12. class website(osv.osv):
  13. _inherit = "website"
  14. def _image(self, cr, uid, model, id, field, response, max_width=maxint, max_height=maxint, cache=None, context=None):
  15. """ Fetches the requested field and ensures it does not go above
  16. (max_width, max_height), resizing it if necessary.
  17. Resizing is bypassed if the object provides a $field_big, which will
  18. be interpreted as a pre-resized version of the base field.
  19. If the record is not found or does not have the requested field,
  20. returns a placeholder image via :meth:`~._image_placeholder`.
  21. Sets and checks conditional response parameters:
  22. * :mailheader:`ETag` is always set (and checked)
  23. * :mailheader:`Last-Modified is set iif the record has a concurrency
  24. field (``__last_update``)
  25. The requested field is assumed to be base64-encoded image data in
  26. all cases.
  27. """
  28. Model = self.pool[model]
  29. id = int(id)
  30. ids = Model.search(cr, uid,
  31. [('id', '=', id)], context=context)
  32. if not ids and 'website_published' in Model._fields:
  33. ids = Model.search(cr, openerp.SUPERUSER_ID,
  34. [('id', '=', id), ('website_published', '=', True)], context=context)
  35. if not ids:
  36. return self._image_placeholder(response)
  37. concurrency = '__last_update'
  38. [record] = Model.read(cr, openerp.SUPERUSER_ID, [id],
  39. [concurrency, field],
  40. context=context)
  41. if concurrency in record:
  42. server_format = openerp.tools.misc.DEFAULT_SERVER_DATETIME_FORMAT
  43. try:
  44. response.last_modified = datetime.datetime.strptime(
  45. record[concurrency], server_format + '.%f')
  46. except ValueError:
  47. # just in case we have a timestamp without microseconds
  48. response.last_modified = datetime.datetime.strptime(
  49. record[concurrency], server_format)
  50. # Field does not exist on model or field set to False
  51. if not record.get(field):
  52. # FIXME: maybe a field which does not exist should be a 404?
  53. return self._image_placeholder(response)
  54. response.set_etag(hashlib.sha1(record[field]).hexdigest())
  55. response.make_conditional(request.httprequest)
  56. if cache:
  57. response.cache_control.max_age = cache
  58. response.expires = int(time.time() + cache)
  59. # conditional request match
  60. if response.status_code == 304:
  61. return response
  62. if model == 'ir.attachment' and field == 'url':
  63. path = record.get(field).strip('/')
  64. # Check that we serve a file from within the module
  65. if os.path.normpath(path).startswith('..'):
  66. return self._image_placeholder(response)
  67. # Check that the file actually exists
  68. path = path.split('/')
  69. resource = openerp.modules.get_module_resource(path[0], *path[1:])
  70. if not resource:
  71. return self._image_placeholder(response)
  72. data = open(resource, 'rb').read()
  73. else:
  74. data = record[field].decode('base64')
  75. image = Image.open(cStringIO.StringIO(data))
  76. response.mimetype = Image.MIME[image.format]
  77. filename = '%s_%s.%s' % (model.replace('.', '_'), id, str(image.format).lower())
  78. response.headers['Content-Disposition'] = 'inline; filename="%s"' % filename
  79. if (not max_width) and (not max_height):
  80. response.data = data
  81. return response
  82. w, h = image.size
  83. max_w = int(max_width) if max_width else maxint
  84. max_h = int(max_height) if max_height else maxint
  85. if w < max_w and h < max_h:
  86. response.data = data
  87. else:
  88. size = (max_w, max_h)
  89. img = image_resize_and_sharpen(image, size, preserve_aspect_ratio=True)
  90. image_save_for_web(img, response.stream, format=image.format)
  91. # invalidate content-length computed by make_conditional as
  92. # writing to response.stream does not do it (as of werkzeug 0.9.3)
  93. del response.headers['Content-Length']
  94. return response
  95. class ir_http(orm.AbstractModel):
  96. _inherit = 'ir.http'
  97. def _serve_attachment(self):
  98. response = super(ir_http, self)._serve_attachment()
  99. if response and response.mimetype == 'application/octet-stream':
  100. # Try to set mimetype via attachment mimetype field
  101. attach = self.pool['ir.attachment'].search_read(request.cr, openerp.SUPERUSER_ID,
  102. [('type', '=', 'binary'), ('url', '=', request.httprequest.path)],
  103. ['mimetype'], context=request.context)
  104. if attach and attach[0].get('mimetype'):
  105. response.mimetype = attach[0].get('mimetype')
  106. return response