diff --git a/base_multi_image/README.rst b/base_multi_image/README.rst new file mode 100644 index 000000000..e45a4b160 --- /dev/null +++ b/base_multi_image/README.rst @@ -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 <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. diff --git a/base_multi_image/__init__.py b/base_multi_image/__init__.py new file mode 100644 index 000000000..ef1af5949 --- /dev/null +++ b/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 diff --git a/base_multi_image/__openerp__.py b/base_multi_image/__openerp__.py new file mode 100644 index 000000000..e7fefd8e8 --- /dev/null +++ b/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", + ], +} diff --git a/base_multi_image/hooks.py b/base_multi_image/hooks.py new file mode 100644 index 000000000..8d11fefe6 --- /dev/null +++ b/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] diff --git a/base_multi_image/i18n/sv.po b/base_multi_image/i18n/sv.po new file mode 100644 index 000000000..ba538d6ff --- /dev/null +++ b/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" + diff --git a/base_multi_image/images/form.png b/base_multi_image/images/form.png new file mode 100644 index 000000000..62f619b6a Binary files /dev/null and b/base_multi_image/images/form.png differ diff --git a/base_multi_image/images/kanban.png b/base_multi_image/images/kanban.png new file mode 100644 index 000000000..067cd4088 Binary files /dev/null and b/base_multi_image/images/kanban.png differ diff --git a/base_multi_image/models/__init__.py b/base_multi_image/models/__init__.py new file mode 100644 index 000000000..ec7df8c77 --- /dev/null +++ b/base_multi_image/models/__init__.py @@ -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 diff --git a/base_multi_image/models/image.py b/base_multi_image/models/image.py new file mode 100644 index 000000000..5ba11544e --- /dev/null +++ b/base_multi_image/models/image.py @@ -0,0 +1,181 @@ +# -*- 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 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.') diff --git a/base_multi_image/models/owner.py b/base_multi_image/models/owner.py new file mode 100644 index 000000000..1d6f74416 --- /dev/null +++ b/base_multi_image/models/owner.py @@ -0,0 +1,101 @@ +# -*- 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 a 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: + s.image_main = False + s.image_main_medium = False + s.image_main_small = False + if s.image_ids: + s.image_main = s.image_ids[0].image_main + s.image_main_medium = s.image_ids[0].image_medium + s.image_main_small = s.image_ids[0].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 diff --git a/base_multi_image/security/ir.model.access.csv b/base_multi_image/security/ir.model.access.csv new file mode 100644 index 000000000..0b4130359 --- /dev/null +++ b/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 diff --git a/base_multi_image/static/description/icon.png b/base_multi_image/static/description/icon.png new file mode 100644 index 000000000..c11adb164 Binary files /dev/null and b/base_multi_image/static/description/icon.png differ diff --git a/base_multi_image/static/description/icon.svg b/base_multi_image/static/description/icon.svg new file mode 100644 index 000000000..8d552fcb9 --- /dev/null +++ b/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> diff --git a/base_multi_image/views/image_view.xml b/base_multi_image/views/image_view.xml new file mode 100644 index 000000000..dd4e31424 --- /dev/null +++ b/base_multi_image/views/image_view.xml @@ -0,0 +1,139 @@ +<?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"/> + </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"/> + </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"/> + <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>