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.

91 lines
4.1 KiB

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