Browse Source

Merge pull request #374 from Yajo/multi_image

[8.0][base_multi_image] Base module for models with multiple images (a gallery)
pull/385/head
Pedro M. Baeza 9 years ago
parent
commit
542a7e868b
  1. 109
      base_multi_image/README.rst
  2. 7
      base_multi_image/__init__.py
  3. 27
      base_multi_image/__openerp__.py
  4. 33
      base_multi_image/hooks.py
  5. 22
      base_multi_image/i18n/sv.po
  6. BIN
      base_multi_image/images/form.png
  7. BIN
      base_multi_image/images/kanban.png
  8. 5
      base_multi_image/models/__init__.py
  9. 176
      base_multi_image/models/image.py
  10. 98
      base_multi_image/models/owner.py
  11. 2
      base_multi_image/security/ir.model.access.csv
  12. BIN
      base_multi_image/static/description/icon.png
  13. 320
      base_multi_image/static/description/icon.svg
  14. 142
      base_multi_image/views/image_view.xml

109
base_multi_image/README.rst

@ -0,0 +1,109 @@
.. 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 = "my.model.name"
_inherit = ["my.model.name", "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)
* Somewhere in the owner view, add::
<field
name="image_ids"
nolabel="1"
context="{
'default_owner_model': 'my.model.name',
'default_owner_id': id,
}"
mode="kanban"/>
.. 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
Known issues / Roadmap
======================
* *OS file* storage mode for images is meant to provide a path where Odoo has
read access and the image is already found, **not for making the module store
images there**. It would be nice to add that feature though.
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
------------
* Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
* Rafael Blasco <rafabn@antiun.com>
* Jairo Llopis <yajo.sk8@gmail.com>
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
base_multi_image/__init__.py

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# © 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
base_multi_image/__openerp__.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# © 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
base_multi_image/hooks.py

@ -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
base_multi_image/i18n/sv.po

@ -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: support@openerp.com\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"

BIN
base_multi_image/images/form.png

After

Width: 1212  |  Height: 479  |  Size: 49 KiB

BIN
base_multi_image/images/kanban.png

After

Width: 1131  |  Height: 501  |  Size: 139 KiB

5
base_multi_image/models/__init__.py

@ -0,0 +1,5 @@
# -*- coding: utf-8 -*-
# © 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

176
base_multi_image/models/image.py

@ -0,0 +1,176 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# © 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 Image(models.Model):
_name = "base_multi_image.image"
_order = "sequence, owner_model, owner_id, id"
_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)
name = fields.Char(
'Image title',
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)
sequence = fields.Integer(
default=10)
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):
return self._get_image_from_url_cached(self.url)
@api.model
@tools.ormcache(skiparg=1)
def _get_image_from_url_cached(self, url):
"""Allow to download an image and cache it by its URL."""
if url:
try:
(filename, header) = urllib.urlretrieve(url)
with open(filename, 'rb') as f:
return base64.b64encode(f.read())
except:
_logger.error("URL %s cannot be fetched", 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.model
def _make_name_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_name_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_name_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_name_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.')

98
base_multi_image/models/owner.py

@ -0,0 +1,98 @@
# -*- coding: utf-8 -*-
# © 2014 Serv. Tecnol. Avanzados (http://www.serviciosbaeza.com)
# Pedro M. Baeza <pedro.baeza@serviciosbaeza.com>
# © 2015 Antiun Ingeniería S.L. - Jairo Llopis
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import _, api, fields, models, tools
class Owner(models.AbstractModel):
_name = "base_multi_image.owner"
image_ids = fields.One2many(
comodel_name='base_multi_image.image',
inverse_name='owner_id',
string='Images',
domain=lambda self: [("owner_model", "=", self._name)],
copy=True)
image_main = fields.Binary(
string="Main image",
store=False,
compute="_get_multi_image",
inverse="_set_multi_image_main")
image_main_medium = fields.Binary(
string="Medium image",
compute="_get_multi_image",
inverse="_set_multi_image_main_medium",
store=False)
image_main_small = fields.Binary(
string="Small image",
compute="_get_multi_image",
inverse="_set_multi_image_main_small",
store=False)
@api.multi
@api.depends('image_ids')
def _get_multi_image(self):
"""Get the main image for this object.
This is provided as a compatibility layer for submodels that already
had one image per record.
"""
for s in self:
first = s.image_ids[:1]
s.image_main = first.image_main
s.image_main_medium = first.image_medium
s.image_main_small = first.image_small
@api.multi
def _set_multi_image(self, image=False, name=False):
"""Save or delete the main image for this record.
This is provided as a compatibility layer for submodels that already
had one image per record.
"""
# Values to save
values = {
"storage": "db",
"file_db_store": tools.image_resize_image_big(image),
"owner_model": self._name,
}
if name:
values["name"] = name
for s in self:
if image:
values["owner_id"] = s.id
# Editing
if s.image_ids:
s.image_ids[0].write(values)
# Adding
else:
values.setdefault("name", name or _("Main image"))
s.image_ids = [(0, 0, values)]
# Deleting
elif s.image_ids:
s.image_ids[0].unlink()
@api.multi
def _set_multi_image_main(self):
self._set_multi_image(self.image_main)
@api.multi
def _set_multi_image_main_medium(self):
self._set_multi_image(self.image_main_medium)
@api.multi
def _set_multi_image_main_small(self):
self._set_multi_image(self.image_main_small)
@api.multi
def unlink(self):
"""Mimic `ondelete="cascade"` for multi images."""
images = self.mapped("image_ids")
result = super(Owner, self).unlink()
if result:
images.unlink()
return result

2
base_multi_image/security/ir.model.access.csv

@ -0,0 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_images","Manage multi images","model_base_multi_image_image","base.group_user",1,1,1,1

BIN
base_multi_image/static/description/icon.png

After

Width: 128  |  Height: 128  |  Size: 15 KiB

320
base_multi_image/static/description/icon.svg

@ -0,0 +1,320 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
sodipodi:docname="icon.svg"
viewBox="0 0 128 128"
version="1.1"
inkscape:version="0.91 r"
width="128"
height="128"
inkscape:export-filename="icon.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<title
id="title3056">Drawings Icon</title>
<defs
id="defs4">
<linearGradient
id="linearGradient3840">
<stop
id="stop3842"
style="stop-color:#008000"
offset="0" />
<stop
id="stop3844"
style="stop-color:#9def9d"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3629">
<stop
id="stop3631"
style="stop-color:#ffffff"
offset="0" />
<stop
id="stop3633"
style="stop-color:#dedede"
offset="1" />
</linearGradient>
<linearGradient
id="linearGradient3637"
y2="689.96997"
xlink:href="#linearGradient3629"
gradientUnits="userSpaceOnUse"
x2="355.06"
y1="627.34003"
x1="265.67999"
inkscape:collect="always" />
<filter
id="filter3711"
inkscape:collect="always"
style="color-interpolation-filters:sRGB">
<feGaussianBlur
id="feGaussianBlur3713"
stdDeviation="1.6175068"
inkscape:collect="always" />
</filter>
<linearGradient
id="linearGradient3744"
y2="689.96997"
xlink:href="#linearGradient3629"
gradientUnits="userSpaceOnUse"
x2="355.06"
gradientTransform="matrix(0.77469246,0,0,0.77284478,74.87419,156.55306)"
y1="627.34003"
x1="265.67999"
inkscape:collect="always" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4174"
gradientUnits="userSpaceOnUse"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4248"
gradientUnits="userSpaceOnUse"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4250"
gradientUnits="userSpaceOnUse"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4252"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.77469246,0,0,0.77284478,74.87419,156.55306)"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4266"
gradientUnits="userSpaceOnUse"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4268"
gradientUnits="userSpaceOnUse"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient3629"
id="linearGradient4270"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.77469246,0,0,0.77284478,74.87419,156.55306)"
x1="265.67999"
y1="627.34003"
x2="355.06"
y2="689.96997" />
</defs>
<sodipodi:namedview
id="base"
fit-margin-left="0"
inkscape:zoom="11.2"
borderopacity="1.0"
inkscape:current-layer="layer1"
inkscape:cx="92.246275"
inkscape:cy="80.63155"
inkscape:window-maximized="1"
showgrid="false"
fit-margin-right="0"
inkscape:snap-nodes="false"
inkscape:document-units="px"
bordercolor="#666666"
inkscape:window-x="65"
inkscape:window-y="24"
fit-margin-bottom="0"
inkscape:window-width="1855"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
pagecolor="#ffffff"
inkscape:window-height="1056"
fit-margin-top="0" />
<g
id="layer1"
inkscape:label="Papers"
inkscape:groupmode="layer"
transform="translate(-254.06,-602.12)">
<g
id="g4254"
transform="matrix(0.77433453,-0.17292855,0.17292855,0.77433453,-60.645266,215.27704)">
<g
id="g4256"
transform="translate(2.4999999,3.9285715)">
<path
id="path4258"
style="fill:url(#linearGradient4266);stroke:#000000;stroke-width:1px;filter:url(#filter3711)"
inkscape:connector-curvature="0"
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z" />
<path
id="path4260"
style="fill:url(#linearGradient4268)"
inkscape:connector-curvature="0"
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z" />
</g>
<path
sodipodi:nodetypes="ccccc"
id="path4262"
style="fill:url(#linearGradient4270);stroke:#b8b8b8;stroke-width:0.77376807"
inkscape:connector-curvature="0"
d="m 279.33626,638.76952 65.69064,-12.23055 17.78177,72.34343 -65.82128,12.79442 z" />
<image
y="681.4696"
x="120.36314"
id="image4264"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAACXZwQWcAAABAAAAAQADq8/hgAAAE WklEQVRYw9WX6XKjRhCAef8HiySQvGt5vfZuEselOUAcEpe4GdI9MAgQOjb5k3SVyzY1801PX9Ot Nf9StP80QJR5miRpXtb/AFCnvmMySgmhlJn2Mal+BSBSj1NCGeNSGAMOd0/iQYCI95TAXnm+FCr/ I2ZYPwJILEJhPaGm7flBFIW+Z5sUvwEivguovG7pMR0cV2e+BbYArF3cBqQclKfEvryvSB2KaHa6 BYhgDSP7ZN7gmUNQCf86wCdgcBaKq04/cTzAuwbA/czKb8VdZYMSI8IAEOJ+XjTiFkF4SDjOARII HLiBK+4E/xHOIdEloMSAAwZx7hEOBKIquwA4lFPbR/3uEhzCqSUmgBiwrGgeIlQm5b0zO0CN3yKw 34QgQC4JKZqrGAFC0MpWvuwJ3V6hWD3BI5wchoDaBAumzYQgmsrd7ewZx5bosHIAAAtQp4+nXUuA +2yXy9Xyi4OsIorjauBLZQWtd0Gqrt3EvCXQlb4BMZYfsPP7cr0gvS4FaNw6Qus0ovtez8DZcYyH t8Wmk9XWdF+Mjf570Ke4q46UgAgUCtX55mKl/wSbsD83hrEE0VGJ1RrEWHz2aaXuIAEe7b3SNG/6 01oSzL/W20/T2r2uDNACARvjWelZQTTaCiCg2vSR1bzrsFgSQMk8SbPi8FWX+0GFbX2OXMarDoAm OGfo+wpXt7cwj4Hv+1n+rSMYW3HOfS4TAgHZIDIVYG38wNzchyB+kj4ZUwB4npw6ABokmgA2qz9k fbIkoWDLzQSQ0tbw2gA20kA/nmyqCHG8nmqQd2prbSKQZAIwnk5B5PSE/EWfACCUZGFSgHQKeE6D sCcExfc5wKEDRLMaJHBwTwA/zFzhOLBBPGODoCfEyYUb0XVBB1AGHXvho/SVDsSjF15QrtMG1xlp sDbCrCewj7UxAWAJSjsAlJOuHI0AX9Mi8IMgsJnMC2MMOJA2f7RhXI8AG/2LVxZZVlQWmKElnAFi T5nMH62L67Mb3lTmbIzVK3Uc9r6GvJAEyMa6d0KXP1oXliqbRPPzN0NvBcrBAmSpr37wlrB8GeRS 6zkJECZVNRKeuLfty1C+wc/zp7TD9jVQN7DUDq2vkUEzfAymIl9uZ5iL1B0U1Rw7surmc4SE/sUB E3KaDB8Wd1QS7hJQga4Kayow2aAsXiV0L458HE/jx9UbPi33CIf+ITwDSnxM/IcIcAGIrHzaH+BX 8Ky4awdq41nBZYsjG4/kEQLjg9Q5A9A1jJ7u3CJEa1OzmuvSKgubwPA24IT7WT7fJ5YmEtwbASWO 2AkP94871WpPOCc8vmYHaORhv5lf75VrV3bD+9nZIrUJamhXN9v9kMlu3wonYVlGe9msU1/cGTgK px0YmO2fsrKq66rMk8Bh7dd99sDIk+xxxsE5icqhqfsLflkz1pkbukSCBzI5bqG0EGrPGvfK2FeG DseRi1I5eVFuB8WvDp51FvsH13Fcz4+y6n86Oz8kfwPMD02INEiadQAAAABJRU5ErkJggg== "
preserveAspectRatio="none"
height="73.870087"
width="66.013809"
transform="matrix(0.98201428,-0.18880665,0.23736204,0.97142126,0,0)" />
</g>
<g
id="g4229"
transform="matrix(0.79340926,0,0,0.79340926,57.365182,120.76411)">
<g
transform="translate(2.4999999,3.9285715)"
id="g3719">
<path
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient4174);stroke:#000000;stroke-width:1px;filter:url(#filter3711)"
id="path3689" />
<path
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient3637)"
id="path2851" />
</g>
<path
d="m 279.33626,638.76952 65.69064,-12.23055 17.78177,72.34343 -65.82128,12.79442 z"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient3744);stroke:#b8b8b8;stroke-width:0.77376807"
id="path3737"
sodipodi:nodetypes="ccccc" />
<image
transform="matrix(0.98201428,-0.18880665,0.23736204,0.97142126,0,0)"
width="66.013809"
height="73.870087"
preserveAspectRatio="none"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAACXZwQWcAAABAAAAAQADq8/hgAAAE WklEQVRYw9WX6XKjRhCAef8HiySQvGt5vfZuEselOUAcEpe4GdI9MAgQOjb5k3SVyzY1801PX9Ot Nf9StP80QJR5miRpXtb/AFCnvmMySgmhlJn2Mal+BSBSj1NCGeNSGAMOd0/iQYCI95TAXnm+FCr/ I2ZYPwJILEJhPaGm7flBFIW+Z5sUvwEivguovG7pMR0cV2e+BbYArF3cBqQclKfEvryvSB2KaHa6 BYhgDSP7ZN7gmUNQCf86wCdgcBaKq04/cTzAuwbA/czKb8VdZYMSI8IAEOJ+XjTiFkF4SDjOARII HLiBK+4E/xHOIdEloMSAAwZx7hEOBKIquwA4lFPbR/3uEhzCqSUmgBiwrGgeIlQm5b0zO0CN3yKw 34QgQC4JKZqrGAFC0MpWvuwJ3V6hWD3BI5wchoDaBAumzYQgmsrd7ewZx5bosHIAAAtQp4+nXUuA +2yXy9Xyi4OsIorjauBLZQWtd0Gqrt3EvCXQlb4BMZYfsPP7cr0gvS4FaNw6Qus0ovtez8DZcYyH t8Wmk9XWdF+Mjf570Ke4q46UgAgUCtX55mKl/wSbsD83hrEE0VGJ1RrEWHz2aaXuIAEe7b3SNG/6 01oSzL/W20/T2r2uDNACARvjWelZQTTaCiCg2vSR1bzrsFgSQMk8SbPi8FWX+0GFbX2OXMarDoAm OGfo+wpXt7cwj4Hv+1n+rSMYW3HOfS4TAgHZIDIVYG38wNzchyB+kj4ZUwB4npw6ABokmgA2qz9k fbIkoWDLzQSQ0tbw2gA20kA/nmyqCHG8nmqQd2prbSKQZAIwnk5B5PSE/EWfACCUZGFSgHQKeE6D sCcExfc5wKEDRLMaJHBwTwA/zFzhOLBBPGODoCfEyYUb0XVBB1AGHXvho/SVDsSjF15QrtMG1xlp sDbCrCewj7UxAWAJSjsAlJOuHI0AX9Mi8IMgsJnMC2MMOJA2f7RhXI8AG/2LVxZZVlQWmKElnAFi T5nMH62L67Mb3lTmbIzVK3Uc9r6GvJAEyMa6d0KXP1oXliqbRPPzN0NvBcrBAmSpr37wlrB8GeRS 6zkJECZVNRKeuLfty1C+wc/zp7TD9jVQN7DUDq2vkUEzfAymIl9uZ5iL1B0U1Rw7surmc4SE/sUB E3KaDB8Wd1QS7hJQga4Kayow2aAsXiV0L458HE/jx9UbPi33CIf+ITwDSnxM/IcIcAGIrHzaH+BX 8Ky4awdq41nBZYsjG4/kEQLjg9Q5A9A1jJ7u3CJEa1OzmuvSKgubwPA24IT7WT7fJ5YmEtwbASWO 2AkP94871WpPOCc8vmYHaORhv5lf75VrV3bD+9nZIrUJamhXN9v9kMlu3wonYVlGe9msU1/cGTgK px0YmO2fsrKq66rMk8Bh7dd99sDIk+xxxsE5icqhqfsLflkz1pkbukSCBzI5bqG0EGrPGvfK2FeG DseRi1I5eVFuB8WvDp51FvsH13Fcz4+y6n86Oz8kfwPMD02INEiadQAAAABJRU5ErkJggg== "
id="image4222"
x="120.36314"
y="681.4696" />
</g>
<g
id="g4236"
transform="matrix(0.75185332,0.25340647,-0.25340647,0.75185332,272.3295,77.897414)">
<g
id="g4238"
transform="translate(2.4999999,3.9285715)">
<path
id="path4240"
style="fill:url(#linearGradient4248);stroke:#000000;stroke-width:1px;filter:url(#filter3711)"
inkscape:connector-curvature="0"
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z" />
<path
id="path4242"
style="fill:url(#linearGradient4250)"
inkscape:connector-curvature="0"
d="m 266.18,625.07 80.307,-15.152 26.769,90.914 -87.883,17.678 -19.193,-93.439 z" />
</g>
<path
sodipodi:nodetypes="ccccc"
id="path4244"
style="fill:url(#linearGradient4252);stroke:#b8b8b8;stroke-width:0.77376807"
inkscape:connector-curvature="0"
d="m 279.33626,638.76952 65.69064,-12.23055 17.78177,72.34343 -65.82128,12.79442 z" />
<image
y="681.4696"
x="120.36314"
id="image4246"
xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAAAAACPAi4CAAAACXZwQWcAAABAAAAAQADq8/hgAAAE WklEQVRYw9WX6XKjRhCAef8HiySQvGt5vfZuEselOUAcEpe4GdI9MAgQOjb5k3SVyzY1801PX9Ot Nf9StP80QJR5miRpXtb/AFCnvmMySgmhlJn2Mal+BSBSj1NCGeNSGAMOd0/iQYCI95TAXnm+FCr/ I2ZYPwJILEJhPaGm7flBFIW+Z5sUvwEivguovG7pMR0cV2e+BbYArF3cBqQclKfEvryvSB2KaHa6 BYhgDSP7ZN7gmUNQCf86wCdgcBaKq04/cTzAuwbA/czKb8VdZYMSI8IAEOJ+XjTiFkF4SDjOARII HLiBK+4E/xHOIdEloMSAAwZx7hEOBKIquwA4lFPbR/3uEhzCqSUmgBiwrGgeIlQm5b0zO0CN3yKw 34QgQC4JKZqrGAFC0MpWvuwJ3V6hWD3BI5wchoDaBAumzYQgmsrd7ewZx5bosHIAAAtQp4+nXUuA +2yXy9Xyi4OsIorjauBLZQWtd0Gqrt3EvCXQlb4BMZYfsPP7cr0gvS4FaNw6Qus0ovtez8DZcYyH t8Wmk9XWdF+Mjf570Ke4q46UgAgUCtX55mKl/wSbsD83hrEE0VGJ1RrEWHz2aaXuIAEe7b3SNG/6 01oSzL/W20/T2r2uDNACARvjWelZQTTaCiCg2vSR1bzrsFgSQMk8SbPi8FWX+0GFbX2OXMarDoAm OGfo+wpXt7cwj4Hv+1n+rSMYW3HOfS4TAgHZIDIVYG38wNzchyB+kj4ZUwB4npw6ABokmgA2qz9k fbIkoWDLzQSQ0tbw2gA20kA/nmyqCHG8nmqQd2prbSKQZAIwnk5B5PSE/EWfACCUZGFSgHQKeE6D sCcExfc5wKEDRLMaJHBwTwA/zFzhOLBBPGODoCfEyYUb0XVBB1AGHXvho/SVDsSjF15QrtMG1xlp sDbCrCewj7UxAWAJSjsAlJOuHI0AX9Mi8IMgsJnMC2MMOJA2f7RhXI8AG/2LVxZZVlQWmKElnAFi T5nMH62L67Mb3lTmbIzVK3Uc9r6GvJAEyMa6d0KXP1oXliqbRPPzN0NvBcrBAmSpr37wlrB8GeRS 6zkJECZVNRKeuLfty1C+wc/zp7TD9jVQN7DUDq2vkUEzfAymIl9uZ5iL1B0U1Rw7surmc4SE/sUB E3KaDB8Wd1QS7hJQga4Kayow2aAsXiV0L458HE/jx9UbPi33CIf+ITwDSnxM/IcIcAGIrHzaH+BX 8Ky4awdq41nBZYsjG4/kEQLjg9Q5A9A1jJ7u3CJEa1OzmuvSKgubwPA24IT7WT7fJ5YmEtwbASWO 2AkP94871WpPOCc8vmYHaORhv5lf75VrV3bD+9nZIrUJamhXN9v9kMlu3wonYVlGe9msU1/cGTgK px0YmO2fsrKq66rMk8Bh7dd99sDIk+xxxsE5icqhqfsLflkz1pkbukSCBzI5bqG0EGrPGvfK2FeG DseRi1I5eVFuB8WvDp51FvsH13Fcz4+y6n86Oz8kfwPMD02INEiadQAAAABJRU5ErkJggg== "
preserveAspectRatio="none"
height="73.870087"
width="66.013809"
transform="matrix(0.98201428,-0.18880665,0.23736204,0.97142126,0,0)" />
</g>
</g>
<g
id="layer4"
inkscape:label="Flower"
inkscape:groupmode="layer"
transform="translate(-254.06,-602.12)" />
<metadata
id="metadata42">
<rdf:RDF>
<cc:Work>
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<cc:license
rdf:resource="http://creativecommons.org/publicdomain/zero/1.0/" />
<dc:publisher>
<cc:Agent
rdf:about="http://openclipart.org/">
<dc:title>Openclipart</dc:title>
</cc:Agent>
</dc:publisher>
<dc:title>Drawings Icon</dc:title>
<dc:date>2012-01-29T15:13:42</dc:date>
<dc:description>Icon for Drawings/Pictures folder.</dc:description>
<dc:source>https://openclipart.org/detail/167547/drawings-icon-by-andreibranescu</dc:source>
<dc:creator>
<cc:Agent>
<dc:title>andreibranescu</dc:title>
</cc:Agent>
</dc:creator>
<dc:subject>
<rdf:Bag>
<rdf:li>Inkscape</rdf:li>
<rdf:li>drawings</rdf:li>
<rdf:li>icon</rdf:li>
<rdf:li>pictures</rdf:li>
</rdf:Bag>
</dc:subject>
</cc:Work>
<cc:License
rdf:about="http://creativecommons.org/publicdomain/zero/1.0/">
<cc:permits
rdf:resource="http://creativecommons.org/ns#Reproduction" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#Distribution" />
<cc:permits
rdf:resource="http://creativecommons.org/ns#DerivativeWorks" />
</cc:License>
</rdf:RDF>
</metadata>
</svg>

142
base_multi_image/views/image_view.xml

@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="image_form_view" model="ir.ui.view">
<field name="name">Multi image form</field>
<field name="model">base_multi_image.image</field>
<field name="arch" type="xml">
<form string="Image">
<group>
<group
string="Owner"
attrs="{
'invisible': [
('show_technical', '=', False),
],
}">
<field name="show_technical" invisible="True"/>
<field name="owner_model"/>
<field name="owner_id"/>
<field name="sequence"/>
</group>
<group string="Name">
<field name="name"/>
<field name="storage"/>
<field name="extension"/>
</group>
</group>
<group>
<group string="Options">
<field
name="url"
attrs="{
'invisible': [('storage', '!=', 'url')],
'required': [('storage', '=', 'url')],
}"
widget="url"/>
<field
name="path"
attrs="{
'invisible': [('storage', '!=', 'file')],
'required': [('storage', '=', 'file')],
}"/>
<field name="filename" invisible="1"/>
<field
name="file_db_store"
attrs="{
'invisible': [('storage', '!=', 'db')],
'required': [('storage', '=', 'db')],
}"
filename="filename"/>
</group>
<group string="Preview">
<field name="image_medium"
widget="image"
readonly="True"
nolabel="1" />
</group>
</group>
<group string="Comments">
<field name="comments" nolabel="1" />
</group>
</form>
</field>
</record>
<record id="image_tree_view" model="ir.ui.view">
<field name="name">Multi image tree</field>
<field name="model">base_multi_image.image</field>
<field name="arch" type="xml">
<tree string="Images">
<field name="name"/>
<field name="storage"/>
<field name="owner_model"/>
<field name="owner_id"/>
<field name="sequence" invisible="True"/>
</tree>
</field>
</record>
<record id="image_kanban_view" model="ir.ui.view">
<field name="name">Product multi image kanban</field>
<field name="model">base_multi_image.image</field>
<field name="arch" type="xml">
<kanban string="Product Images">
<field name="name"/>
<field name="storage"/>
<field name="sequence"/>
<templates>
<t t-name="kanban-box">
<div style="position: relative">
<a t-if="! read_only_mode"
type="delete"
style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
<div class="oe_module_vignette">
<a type="open">
<img
t-att-src="kanban_image(
'base_multi_image.image',
'image_small',
record.id.value)"
class="oe_kanban_image"/>
</a>
<div class="oe_module_desc">
<div class="oe_kanban_box_content oe_kanban_color_bglight oe_kanban_color_border">
<table class="oe_kanban_table">
<tr>
<td class="oe_kanban_title1"
align="left"
valign="middle">
<h4>
<a type="open">
<field name="name"/>
</a>
</h4>
<field name="storage"/>
</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record id="image_action" model="ir.actions.act_window">
<field name="name">Multi images</field>
<field name="res_model">base_multi_image.image</field>
<field name="view_mode">kanban,tree,form</field>
</record>
<menuitem
id="image_menu"
action="image_action"
parent="base.menu_config"/>
</data>
</openerp>
Loading…
Cancel
Save