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.

196 lines
6.5 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. ('filestore', 'Filestore')],
  28. required=True)
  29. name = fields.Char(
  30. 'Image title',
  31. translate=True)
  32. filename = fields.Char()
  33. extension = fields.Char(
  34. 'File extension',
  35. readonly=True)
  36. attachment_id = fields.Many2one(
  37. 'ir.attachment',
  38. string='Attachment',
  39. domain="[('index_content', '=', 'image')]")
  40. file_db_store = fields.Binary(
  41. 'Image stored in database',
  42. filters='*.png,*.jpg,*.gif')
  43. path = fields.Char(
  44. "Image path",
  45. help="Image path")
  46. url = fields.Char(
  47. 'Image remote URL')
  48. image_main = fields.Binary(
  49. "Full-sized image",
  50. compute="_get_image")
  51. image_medium = fields.Binary(
  52. "Medium-sized image",
  53. compute="_get_image_sizes",
  54. help="Medium-sized image. It is automatically resized as a "
  55. "128 x 128 px image, with aspect ratio preserved, only when the "
  56. "image exceeds one of those sizes. Use this field in form views "
  57. "or kanban views.")
  58. image_small = fields.Binary(
  59. "Small-sized image",
  60. compute="_get_image_sizes",
  61. help="Small-sized image. It is automatically resized as a 64 x 64 px "
  62. "image, with aspect ratio preserved. Use this field anywhere a "
  63. "small image is required.")
  64. comments = fields.Text(
  65. 'Comments',
  66. translate=True)
  67. sequence = fields.Integer(
  68. default=10)
  69. show_technical = fields.Boolean(
  70. compute="_show_technical")
  71. @api.multi
  72. @api.depends('storage', 'path', 'file_db_store', 'url')
  73. def _get_image(self):
  74. """Get image data from the right storage type."""
  75. for s in self:
  76. s.image_main = getattr(s, "_get_image_from_%s" % s.storage)()
  77. @api.multi
  78. @api.depends("owner_id", "owner_model")
  79. def _show_technical(self):
  80. """Know if you need to show the technical fields."""
  81. self.show_technical = all(
  82. "default_owner_%s" % f not in self.env.context
  83. for f in ("id", "model"))
  84. @api.multi
  85. def _get_image_from_filestore(self):
  86. return self.attachment_id.datas
  87. @api.multi
  88. def _get_image_from_db(self):
  89. return self.file_db_store
  90. @api.multi
  91. def _get_image_from_file(self):
  92. if self.path and os.path.exists(self.path):
  93. try:
  94. with open(self.path, 'rb') as f:
  95. return base64.b64encode(f.read())
  96. except Exception as e:
  97. _logger.error("Can not open the image %s, error : %s",
  98. self.path, e, exc_info=True)
  99. else:
  100. _logger.error("The image %s doesn't exist ", self.path)
  101. return False
  102. @api.multi
  103. def _get_image_from_url(self):
  104. return self._get_image_from_url_cached(self.url)
  105. @api.model
  106. @tools.ormcache(skiparg=1)
  107. def _get_image_from_url_cached(self, url):
  108. """Allow to download an image and cache it by its URL."""
  109. if url:
  110. try:
  111. (filename, header) = urllib.urlretrieve(url)
  112. with open(filename, 'rb') as f:
  113. return base64.b64encode(f.read())
  114. except:
  115. _logger.error("URL %s cannot be fetched", url,
  116. exc_info=True)
  117. return False
  118. @api.multi
  119. @api.depends('image_main')
  120. def _get_image_sizes(self):
  121. for s in self:
  122. try:
  123. vals = tools.image_get_resized_images(
  124. s.with_context(bin_size=False).image_main)
  125. except:
  126. vals = {"image_medium": False,
  127. "image_small": False}
  128. s.update(vals)
  129. @api.model
  130. def _make_name_pretty(self, name):
  131. return name.replace('_', ' ').capitalize()
  132. @api.onchange('url')
  133. def _onchange_url(self):
  134. if self.url:
  135. filename = self.url.split('/')[-1]
  136. self.name, self.extension = os.path.splitext(filename)
  137. self.name = self._make_name_pretty(self.name)
  138. @api.onchange('path')
  139. def _onchange_path(self):
  140. if self.path:
  141. self.name, self.extension = os.path.splitext(os.path.basename(
  142. self.path))
  143. self.name = self._make_name_pretty(self.name)
  144. @api.onchange('filename')
  145. def _onchange_filename(self):
  146. if self.filename:
  147. self.name, self.extension = os.path.splitext(self.filename)
  148. self.name = self._make_name_pretty(self.name)
  149. @api.onchange('attachment_id')
  150. def _onchange_attachmend_id(self):
  151. if self.attachment_id:
  152. self.name = self.attachment_id.res_name
  153. @api.constrains('storage', 'url')
  154. def _check_url(self):
  155. if self.storage == 'url' and not self.url:
  156. raise exceptions.ValidationError(
  157. _('You must provide an URL for the image.'))
  158. @api.constrains('storage', 'path')
  159. def _check_path(self):
  160. if self.storage == 'file' and not self.path:
  161. raise exceptions.ValidationError(
  162. _('You must provide a file path for the image.'))
  163. @api.constrains('storage', 'file_db_store')
  164. def _check_store(self):
  165. if self.storage == 'db' and not self.file_db_store:
  166. raise exceptions.ValidationError(
  167. _('You must provide an attached file for the image.'))
  168. @api.constrains('storage', 'attachment_id')
  169. def _check_store(self):
  170. if self.storage == 'filestore' and not self.attachment_id:
  171. raise exceptions.ValidationError(
  172. _('You must provide an attachment for the image.'))