You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

88 lines
4.1 KiB

  1. ###################################################################################
  2. #
  3. # Copyright (C) 2018 MuK IT GmbH
  4. #
  5. # This program is free software: you can redistribute it and/or modify
  6. # it under the terms of the GNU Affero General Public License as
  7. # published by the Free Software Foundation, either version 3 of the
  8. # License, or (at your option) any later version.
  9. #
  10. # This program is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. # GNU Affero General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Affero General Public License
  16. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #
  18. ###################################################################################
  19. import io
  20. import logging
  21. import mimetypes
  22. from odoo import models
  23. from odoo.http import request, STATIC_CACHE
  24. from odoo.exceptions import AccessError
  25. _logger = logging.getLogger(__name__)
  26. class FileIrHttp(models.AbstractModel):
  27. _inherit = 'ir.http'
  28. @classmethod
  29. def file_content(cls, xmlid=None, model=None, id=None, field='content', unique=False,
  30. filename=None, filename_field='content_fname', download=False,
  31. mimetype=None, default_mimetype='application/octet-stream', env=None):
  32. """ Get file, attachment or downloadable content
  33. If the xmlid and id parameter is omitted, fetches the default value for the
  34. binary field (via the default_get method), otherwise fetches the field for
  35. that precise record.
  36. :param str xmlid: xmlid of the record
  37. :param str model: name of the model to fetch the binary from
  38. :param int id: id of the record from which to fetch the binary
  39. :param str field: binary field
  40. :param bool unique: add a max-age for the cache control
  41. :param str filename: choose a filename
  42. :param str filename_field: if not create an filename with model-id-field
  43. :param bool download: apply headers to download the file
  44. :param str mimetype: mintype of the field (for headers)
  45. :param str default_mimetype: default mintype if no mintype found
  46. :param Environment env: by default use request.env
  47. :returns: (status, headers, content)
  48. """
  49. obj = None
  50. env = env or request.env
  51. if xmlid:
  52. obj = cls._xmlid_to_obj(env, xmlid)
  53. elif id and model in env.registry:
  54. obj = env[model].browse(int(id))
  55. if not obj or not obj.exists() or field not in obj:
  56. return (404, [], None)
  57. try:
  58. last_update = obj['__last_update']
  59. except AccessError:
  60. return (403, [], None)
  61. status, headers, content = None, [], None
  62. content = obj.with_context({'stream': True})[field] or io.BytesIO()
  63. if not filename:
  64. if filename_field in obj:
  65. filename = obj[filename_field]
  66. else:
  67. filename = "%s-%s-%s" % (obj._name, obj.id, field)
  68. mimetype = 'mimetype' in obj and obj.mimetype or False
  69. if not mimetype and filename:
  70. mimetype = mimetypes.guess_type(filename)[0]
  71. if not mimetype:
  72. mimetype = default_mimetype
  73. headers += [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
  74. etag = bool(request) and request.httprequest.headers.get('If-None-Match')
  75. retag = '"%s"' % obj.with_context({'checksum': True})[field] if content else ""
  76. status = status or (304 if etag == retag else 200)
  77. headers.append(('ETag', retag))
  78. headers.append(('Cache-Control', 'max-age=%s' % (STATIC_CACHE if unique else 0)))
  79. if download:
  80. headers.append(('Content-Disposition', cls.content_disposition(filename)))
  81. return (status, headers, content)