Browse Source
Merge pull request #374 from Yajo/multi_image
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
14 changed files with 941 additions and 0 deletions
-
109base_multi_image/README.rst
-
7base_multi_image/__init__.py
-
27base_multi_image/__openerp__.py
-
33base_multi_image/hooks.py
-
22base_multi_image/i18n/sv.po
-
BINbase_multi_image/images/form.png
-
BINbase_multi_image/images/kanban.png
-
5base_multi_image/models/__init__.py
-
176base_multi_image/models/image.py
-
98base_multi_image/models/owner.py
-
2base_multi_image/security/ir.model.access.csv
-
BINbase_multi_image/static/description/icon.png
-
320base_multi_image/static/description/icon.svg
-
142base_multi_image/views/image_view.xml
@ -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. |
@ -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 |
@ -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", |
|||
], |
|||
} |
@ -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] |
@ -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" |
|||
|
After Width: 1212 | Height: 479 | Size: 49 KiB |
After Width: 1131 | Height: 501 | Size: 139 KiB |
@ -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 |
@ -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.') |
@ -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 |
@ -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 |
After Width: 128 | Height: 128 | Size: 15 KiB |
@ -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> |
@ -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> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue