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.

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