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.

176 lines
5.8 KiB

  1. # -*- coding: utf-8 -*-
  2. # © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
  3. # Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
  4. # © 2015 Antiun Ingeniería S.L. - Jairo Llopis
  5. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
  6. import base64
  7. import urllib
  8. import os
  9. import logging
  10. from openerp import models, fields, api, exceptions, _
  11. from openerp import tools
  12. _logger = logging.getLogger(__name__)
  13. class Image(models.Model):
  14. _name = "base_multi_image.image"
  15. _order = "sequence, owner_model, owner_id, id"
  16. _sql_constraints = [
  17. ('uniq_name_owner', 'UNIQUE(owner_id, owner_model, name)',
  18. _('A document can have only one image with the same name.')),
  19. ]
  20. owner_id = fields.Integer(
  21. "Owner",
  22. required=True)
  23. owner_model = fields.Char(
  24. required=True)
  25. storage = fields.Selection(
  26. [('url', 'URL'), ('file', 'OS file'), ('db', 'Database')],
  27. required=True)
  28. name = fields.Char(
  29. 'Image title',
  30. translate=True)
  31. filename = fields.Char()
  32. extension = fields.Char(
  33. 'File extension',
  34. readonly=True)
  35. file_db_store = fields.Binary(
  36. 'Image stored in database',
  37. filters='*.png,*.jpg,*.gif')
  38. path = fields.Char(
  39. "Image path",
  40. help="Image path")
  41. url = fields.Char(
  42. 'Image remote URL')
  43. image_main = fields.Binary(
  44. "Full-sized image",
  45. compute="_get_image")
  46. image_medium = fields.Binary(
  47. "Medium-sized image",
  48. compute="_get_image_sizes",
  49. help="Medium-sized image. It is automatically resized as a "
  50. "128 x 128 px image, with aspect ratio preserved, only when the "
  51. "image exceeds one of those sizes. Use this field in form views "
  52. "or kanban views.")
  53. image_small = fields.Binary(
  54. "Small-sized image",
  55. compute="_get_image_sizes",
  56. help="Small-sized image. It is automatically resized as a 64 x 64 px "
  57. "image, with aspect ratio preserved. Use this field anywhere a "
  58. "small image is required.")
  59. comments = fields.Text(
  60. 'Comments',
  61. translate=True)
  62. sequence = fields.Integer(
  63. default=10)
  64. show_technical = fields.Boolean(
  65. compute="_show_technical")
  66. @api.multi
  67. @api.depends('storage', 'path', 'file_db_store', 'url')
  68. def _get_image(self):
  69. """Get image data from the right storage type."""
  70. for s in self:
  71. s.image_main = getattr(s, "_get_image_from_%s" % s.storage)()
  72. @api.multi
  73. @api.depends("owner_id", "owner_model")
  74. def _show_technical(self):
  75. """Know if you need to show the technical fields."""
  76. self.show_technical = all(
  77. "default_owner_%s" % f not in self.env.context
  78. for f in ("id", "model"))
  79. @api.multi
  80. def _get_image_from_db(self):
  81. return self.file_db_store
  82. @api.multi
  83. def _get_image_from_file(self):
  84. if self.path and os.path.exists(self.path):
  85. try:
  86. with open(self.path, 'rb') as f:
  87. return base64.b64encode(f.read())
  88. except Exception as e:
  89. _logger.error("Can not open the image %s, error : %s",
  90. self.path, e, exc_info=True)
  91. else:
  92. _logger.error("The image %s doesn't exist ", self.path)
  93. return False
  94. @api.multi
  95. def _get_image_from_url(self):
  96. return self._get_image_from_url_cached(self.url)
  97. @api.model
  98. @tools.ormcache(skiparg=1)
  99. def _get_image_from_url_cached(self, url):
  100. """Allow to download an image and cache it by its URL."""
  101. if url:
  102. try:
  103. (filename, header) = urllib.urlretrieve(url)
  104. with open(filename, 'rb') as f:
  105. return base64.b64encode(f.read())
  106. except:
  107. _logger.error("URL %s cannot be fetched", url,
  108. exc_info=True)
  109. return False
  110. @api.multi
  111. @api.depends('image_main')
  112. def _get_image_sizes(self):
  113. for s in self:
  114. try:
  115. vals = tools.image_get_resized_images(
  116. s.with_context(bin_size=False).image_main)
  117. except:
  118. vals = {"image_medium": False,
  119. "image_small": False}
  120. s.update(vals)
  121. @api.model
  122. def _make_name_pretty(self, name):
  123. return name.replace('_', ' ').capitalize()
  124. @api.onchange('url')
  125. def _onchange_url(self):
  126. if self.url:
  127. filename = self.url.split('/')[-1]
  128. self.name, self.extension = os.path.splitext(filename)
  129. self.name = self._make_name_pretty(self.name)
  130. @api.onchange('path')
  131. def _onchange_path(self):
  132. if self.path:
  133. self.name, self.extension = os.path.splitext(os.path.basename(
  134. self.path))
  135. self.name = self._make_name_pretty(self.name)
  136. @api.onchange('filename')
  137. def _onchange_filename(self):
  138. if self.filename:
  139. self.name, self.extension = os.path.splitext(self.filename)
  140. self.name = self._make_name_pretty(self.name)
  141. @api.constrains('storage', 'url')
  142. def _check_url(self):
  143. if self.storage == 'url' and not self.url:
  144. raise exceptions.ValidationError(
  145. 'You must provide an URL for the image.')
  146. @api.constrains('storage', 'path')
  147. def _check_path(self):
  148. if self.storage == 'file' and not self.path:
  149. raise exceptions.ValidationError(
  150. 'You must provide a file path for the image.')
  151. @api.constrains('storage', 'file_db_store')
  152. def _check_store(self):
  153. if self.storage == 'db' and not self.file_db_store:
  154. raise exceptions.ValidationError(
  155. 'You must provide an attached file for the image.')