Skip to content

Commit

Permalink
Split model in abstract & concrete.
Browse files Browse the repository at this point in the history
  • Loading branch information
yajo committed Feb 24, 2016
0 parents commit f8bb336
Show file tree
Hide file tree
Showing 14 changed files with 929 additions and 0 deletions.
91 changes: 91 additions & 0 deletions base_multi_image/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-3

====================
Multiple Images Base
====================

This module extends the functionality of any model to support multiple images
(a gallery) attached to it and allow you to manage them.

Installation
============

This module adds abstract models to work on. Its sole purpose is to serve as
base for other modules that implement galleries, so if you install this one
manually you will notice no change. You should install any other module based
on this one and this will get installed automatically.

Usage
=====

To manage all stored images, you need to:

* Go to *Settings > Configuration > Multi images*.

... but you probably prefer to manage them from the forms supplied by
submodules that inherit this behavior.

Development
===========

To develop a module based on this one:

* See module ``product_multi_image`` as an example.
* You have to inherit model ``base_multi_image.owner`` to the model that needs
the gallery::

class MyOwner(models.Model):
_name = "mymodule.name"
_inherit = "base_multi_image.owner"

# If you need this, you will need ``post_init_hook_for_submodules``
old_image_field = fields.Binary(related="image_main", store=False)

.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/149/8.0

Bug Tracker
===========

Bugs are tracked on `GitHub Issues
<https://github.com/OCA/server-tools/issues>`_. In case of trouble, please
check there if your issue has already been reported. If you spotted it first,
help us smashing it by providing a detailed and welcomed `feedback
<https://github.com/OCA/
server-tools/issues/new?body=module:%20
base_multi_image%0Aversion:%20
8.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

Credits
=======

Original implementation
-----------------------
This module is inspired in previous module *product_images* from OpenLabs
and Akretion.

Contributors
------------

* Sharoon Thomas
* Pedro M. Baeza <[email protected]>
* Rafael Blasco <[email protected]>
* Jairo Llopis <[email protected]>

Maintainer
----------

.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org

This module is maintained by the OCA.

OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.

To contribute to this module, please visit http://odoo-community.org.
7 changes: 7 additions & 0 deletions base_multi_image/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <[email protected]>
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import models
27 changes: 27 additions & 0 deletions base_multi_image/__openerp__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <[email protected]>
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

{
"name": "Multiple images base",
"summary": "Allow multiple images for database objects",
"version": "8.0.1.0.0",
"author": "Serv. Tecnol. Avanzados - Pedro M. Baeza, "
"Antiun Ingeniería, S.L., "
"Odoo Community Association (OCA)",
"license": "AGPL-3",
"website": "http://www.antiun.com",
"category": "Tools",
"depends": ['base'],
'installable': True,
"data": [
"security/ir.model.access.csv",
"views/image_view.xml",
],
"images": [
"images/form.png",
"images/kanban.png",
],
}
33 changes: 33 additions & 0 deletions base_multi_image/hooks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding: utf-8 -*-
# © 2016 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from openerp import SUPERUSER_ID
import logging

_logger = logging.getLogger(__name__)


def post_init_hook_for_submodules(cr, registry, model, field):
"""Moves images from single to multi mode.
Feel free to use this as a ``post_init_hook`` for submodules.
:param str model:
Model name, like ``product.template``.
:param str field:
Binary field that had the images in that :param:`model`, like
``image``.
"""
with cr.savepoint():
records = registry[model].search(
cr,
SUPERUSER_ID,
[(field, "!=", False)],
context=dict())

_logger.info("Moving images from %s to multi image mode.", model)
for r in registry[model].browse(cr, SUPERUSER_ID, records):
_logger.debug("Setting up multi image for record %d.", r.id)
r.image_main = r[field]
22 changes: 22 additions & 0 deletions base_multi_image/i18n/sv.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Translation of OpenERP Server.
# This file contains the translation of the following modules:
# * product_images_olbs
#
msgid ""
msgstr ""
"Project-Id-Version: OpenERP Server 5.0.14\n"
"Report-Msgid-Bugs-To: [email protected]\n"
"POT-Creation-Date: 2010-11-22 10:19:32+0000\n"
"PO-Revision-Date: 2010-11-22 10:19:32+0000\n"
"Last-Translator: <>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"

#. module: product_images_olbs
#: model:ir.module.module,shortdesc:product_images_olbs.module_meta_information
msgid "Product Image Gallery"
msgstr "Product Image Gallery"

Binary file added base_multi_image/images/form.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added base_multi_image/images/kanban.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions base_multi_image/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
# © 2009 Sharoon Thomas Open Labs Business Solutions
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

from . import image, owner
181 changes: 181 additions & 0 deletions base_multi_image/models/image.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <[email protected]>
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).

import base64
import urllib
import os
import logging
from openerp import models, fields, api, exceptions, _
from openerp import tools

_logger = logging.getLogger(__name__)


class ImageABC(models.Model):
_name = "base_multi_image.image"
_sql_constraints = [
('uniq_name_owner', 'UNIQUE(owner_id, owner_model, name)',
_('A document can have only one image with the same name.')),
]

owner_id = fields.Integer(
"Owner",
required=True)
owner_model = fields.Char(
required=True)
storage = fields.Selection(
[('url', 'URL'), ('file', 'OS file'), ('db', 'Database')],
required=True,
default='db')
name = fields.Char(
'Image title',
required=True,
translate=True)
filename = fields.Char()
extension = fields.Char(
'File extension',
readonly=True)
file_db_store = fields.Binary(
'Image stored in database',
filters='*.png,*.jpg,*.gif')
path = fields.Char(
"Image path",
help="Image path")
url = fields.Char(
'Image remote URL')
image_main = fields.Binary(
"Full-sized image",
compute="_get_image")
image_medium = fields.Binary(
"Medium-sized image",
compute="_get_image_sizes",
help="Medium-sized image. It is automatically resized as a "
"128 x 128 px image, with aspect ratio preserved, only when the "
"image exceeds one of those sizes. Use this field in form views "
"or kanban views.")
image_small = fields.Binary(
"Small-sized image",
compute="_get_image_sizes",
help="Small-sized image. It is automatically resized as a 64 x 64 px "
"image, with aspect ratio preserved. Use this field anywhere a "
"small image is required.")
comments = fields.Text(
'Comments',
translate=True)
show_technical = fields.Boolean(
compute="_show_technical")

@api.multi
@api.depends('storage', 'path', 'file_db_store', 'url')
def _get_image(self):
"""Get image data from the right storage type."""
for s in self:
s.image_main = getattr(s, "_get_image_from_%s" % s.storage)()

@api.multi
@api.depends("owner_id", "owner_model")
def _show_technical(self):
"""Know if you need to show the technical fields."""
self.show_technical = all(
"default_owner_%s" % f not in self.env.context
for f in ("id", "model"))

@api.multi
def _get_image_from_db(self):
return self.file_db_store

@api.multi
def _get_image_from_file(self):
if self.path and os.path.exists(self.path):
try:
with open(self.path, 'rb') as f:
return base64.b64encode(f.read())
except Exception as e:
_logger.error("Can not open the image %s, error : %s",
self.path, e, exc_info=True)
else:
_logger.error("The image %s doesn't exist ", self.path)

return False

@api.multi
def _get_image_from_url(self):
if self.url:
try:
(filename, header) = urllib.urlretrieve(self.url)
with open(filename, 'rb') as f:
return base64.b64encode(f.read())
except:
_logger.error("URL %s cannot be fetched", self.url,
exc_info=True)

return False

@api.multi
@api.depends('image_main')
def _get_image_sizes(self):
for s in self:
try:
vals = tools.image_get_resized_images(
s.with_context(bin_size=False).image_main)
except:
vals = {"image_medium": False,
"image_small": False}
s.update(vals)

@api.multi
def _check_filestore(self):
"""check if the filestore is created, and do it otherwise."""
for s in self:
dir_path = os.path.dirname(s.path)
try:
if not os.path.exists(dir_path):
os.makedirs(dir_path)
except OSError as e:
raise exceptions.Warning(
_('The image filestore cannot be created, %s') % e)

@api.model
def _make_pretty(self, name):
return name.replace('_', ' ').capitalize()

@api.onchange('url')
def _onchange_url(self):
if self.url:
filename = self.url.split('/')[-1]
self.name, self.extension = os.path.splitext(filename)
self.name = self._make_pretty(self.name)

@api.onchange('path')
def _onchange_path(self):
if self.path:
self.name, self.extension = os.path.splitext(os.path.basename(
self.path))
self.name = self._make_pretty(self.name)

@api.onchange('filename')
def _onchange_filename(self):
if self.filename:
self.name, self.extension = os.path.splitext(self.filename)
self.name = self._make_pretty(self.name)

@api.constrains('storage', 'url')
def _check_url(self):
if self.storage == 'url' and not self.url:
raise exceptions.ValidationError(
'You must provide an URL for the image.')

@api.constrains('storage', 'path')
def _check_path(self):
if self.storage == 'file' and not self.path:
raise exceptions.ValidationError(
'You must provide a file path for the image.')

@api.constrains('storage', 'file_db_store')
def _check_store(self):
if self.storage == 'db' and not self.file_db_store:
raise exceptions.ValidationError(
'You must provide an attached file for the image.')
Loading

0 comments on commit f8bb336

Please sign in to comment.