From 6531a4f988e2f7c52bd9226d297899ce412f6788 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Mon, 25 Aug 2025 12:48:06 +0200 Subject: [PATCH 1/8] [ADD] connector_wordpress --- connector_wordpress/README.rst | 30 + connector_wordpress/__init__.py | 2 + connector_wordpress/__manifest__.py | 31 + connector_wordpress/components/__init__.py | 5 + connector_wordpress/components/adapter.py | 16 + connector_wordpress/components/binder.py | 10 + connector_wordpress/components/core.py | 12 + .../components/export_mapper.py | 9 + connector_wordpress/components/exporter.py | 41 ++ connector_wordpress/i18n/es.po | 562 ++++++++++++++++++ connector_wordpress/models/__init__.py | 8 + .../models/backend/__init__.py | 2 + connector_wordpress/models/backend/adapter.py | 15 + connector_wordpress/models/backend/backend.py | 35 ++ .../models/binding/__init__.py | 1 + connector_wordpress/models/binding/binding.py | 27 + connector_wordpress/models/common/tools.py | 121 ++++ .../models/ir_attachment/__init__.py | 6 + .../models/ir_attachment/adapter.py | 36 ++ .../models/ir_attachment/binder.py | 45 ++ .../models/ir_attachment/binding.py | 26 + .../models/ir_attachment/export_mapper.py | 43 ++ .../models/ir_attachment/exporter.py | 50 ++ .../models/ir_attachment/ir_attachment.py | 27 + .../models/product/__init__.py | 1 + connector_wordpress/models/product/product.py | 20 + .../models/product_image/__init__.py | 1 + .../models/product_image/product_image.py | 34 ++ .../models/product_product/__init__.py | 1 + .../models/product_product/product_product.py | 20 + .../models/product_template/__init__.py | 1 + .../product_template/product_template.py | 20 + .../security/connector_wordpress.xml | 13 + .../security/ir.model.access.csv | 5 + .../static/description/icon.png | Bin 0 -> 6342 bytes .../views/connector_wordpress_menu.xml | 24 + .../views/ir_attachment_views.xml | 42 ++ .../views/product_image_views.xml | 16 + .../views/wordpress_backend_view.xml | 98 +++ .../odoo/addons/connector_wordpress | 1 + setup/connector_wordpress/setup.py | 6 + 41 files changed, 1463 insertions(+) create mode 100644 connector_wordpress/README.rst create mode 100644 connector_wordpress/__init__.py create mode 100644 connector_wordpress/__manifest__.py create mode 100644 connector_wordpress/components/__init__.py create mode 100644 connector_wordpress/components/adapter.py create mode 100644 connector_wordpress/components/binder.py create mode 100644 connector_wordpress/components/core.py create mode 100644 connector_wordpress/components/export_mapper.py create mode 100644 connector_wordpress/components/exporter.py create mode 100644 connector_wordpress/i18n/es.po create mode 100644 connector_wordpress/models/__init__.py create mode 100644 connector_wordpress/models/backend/__init__.py create mode 100644 connector_wordpress/models/backend/adapter.py create mode 100644 connector_wordpress/models/backend/backend.py create mode 100644 connector_wordpress/models/binding/__init__.py create mode 100644 connector_wordpress/models/binding/binding.py create mode 100644 connector_wordpress/models/common/tools.py create mode 100644 connector_wordpress/models/ir_attachment/__init__.py create mode 100644 connector_wordpress/models/ir_attachment/adapter.py create mode 100644 connector_wordpress/models/ir_attachment/binder.py create mode 100644 connector_wordpress/models/ir_attachment/binding.py create mode 100644 connector_wordpress/models/ir_attachment/export_mapper.py create mode 100644 connector_wordpress/models/ir_attachment/exporter.py create mode 100644 connector_wordpress/models/ir_attachment/ir_attachment.py create mode 100644 connector_wordpress/models/product/__init__.py create mode 100644 connector_wordpress/models/product/product.py create mode 100644 connector_wordpress/models/product_image/__init__.py create mode 100644 connector_wordpress/models/product_image/product_image.py create mode 100644 connector_wordpress/models/product_product/__init__.py create mode 100644 connector_wordpress/models/product_product/product_product.py create mode 100644 connector_wordpress/models/product_template/__init__.py create mode 100644 connector_wordpress/models/product_template/product_template.py create mode 100644 connector_wordpress/security/connector_wordpress.xml create mode 100644 connector_wordpress/security/ir.model.access.csv create mode 100644 connector_wordpress/static/description/icon.png create mode 100644 connector_wordpress/views/connector_wordpress_menu.xml create mode 100644 connector_wordpress/views/ir_attachment_views.xml create mode 100644 connector_wordpress/views/product_image_views.xml create mode 100644 connector_wordpress/views/wordpress_backend_view.xml create mode 120000 setup/connector_wordpress/odoo/addons/connector_wordpress create mode 100644 setup/connector_wordpress/setup.py diff --git a/connector_wordpress/README.rst b/connector_wordpress/README.rst new file mode 100644 index 000000000..b9df75ee0 --- /dev/null +++ b/connector_wordpress/README.rst @@ -0,0 +1,30 @@ +.. 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 + +=================== +WordPress Connector +=================== + +* WordPress connector + +Bug Tracker +=========== + +Bugs are tracked on `GitHub 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. + +Credits +======= + +Contributors +------------ + +* Eric Antones +* Kilian Niubo + + + + diff --git a/connector_wordpress/__init__.py b/connector_wordpress/__init__.py new file mode 100644 index 000000000..f24d3e242 --- /dev/null +++ b/connector_wordpress/__init__.py @@ -0,0 +1,2 @@ +from . import components +from . import models diff --git a/connector_wordpress/__manifest__.py b/connector_wordpress/__manifest__.py new file mode 100644 index 000000000..f0acf8cfc --- /dev/null +++ b/connector_wordpress/__manifest__.py @@ -0,0 +1,31 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# Copyright NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "Connector WordPress", + "version": "14.0.0.1.1", + "author": "NuoBiT Solutions, S.L.", + "license": "AGPL-3", + "category": "Connector", + "website": "https://github.com/nuobit/odoo-addons", + "external_dependencies": { + # Python magic is included because it can detect more mimetypes + # used to export the files to WordPress. + "python": ["python-magic"], + }, + "depends": [ + "connector_extension_wordpress", + "website_sale_product_document", + "tools_mimetypes_extension", + ], + "data": [ + "security/connector_wordpress.xml", + "security/ir.model.access.csv", + "views/ir_attachment_views.xml", + "views/product_image_views.xml", + "views/wordpress_backend_view.xml", + "views/connector_wordpress_menu.xml", + ], + "installable": True, +} diff --git a/connector_wordpress/components/__init__.py b/connector_wordpress/components/__init__.py new file mode 100644 index 000000000..870112f3a --- /dev/null +++ b/connector_wordpress/components/__init__.py @@ -0,0 +1,5 @@ +from . import core +from . import adapter +from . import binder +from . import exporter +from . import export_mapper diff --git a/connector_wordpress/components/adapter.py b/connector_wordpress/components/adapter.py new file mode 100644 index 000000000..8d78e6ee3 --- /dev/null +++ b/connector_wordpress/components/adapter.py @@ -0,0 +1,16 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +from odoo.addons.component.core import AbstractComponent + + +class WordPressAdapter(AbstractComponent): + _name = "wordpress.adapter" + _inherit = [ + "connector.extension.wordpress.adapter.crud", + "base.wordpress.connector", + ] + + _description = "WordPress Binding (abstract)" diff --git a/connector_wordpress/components/binder.py b/connector_wordpress/components/binder.py new file mode 100644 index 000000000..ecbce8545 --- /dev/null +++ b/connector_wordpress/components/binder.py @@ -0,0 +1,10 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from odoo.addons.component.core import AbstractComponent + + +class WordPressBinder(AbstractComponent): + _name = "wordpress.binder" + _inherit = ["connector.extension.generic.binder", "base.wordpress.connector"] + + _default_binding_field = "wordpress_bind_ids" diff --git a/connector_wordpress/components/core.py b/connector_wordpress/components/core.py new file mode 100644 index 000000000..312d96d7f --- /dev/null +++ b/connector_wordpress/components/core.py @@ -0,0 +1,12 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import AbstractComponent + + +class BaseWordPressConnector(AbstractComponent): + _name = "base.wordpress.connector" + _inherit = "base.connector" + _collection = "wordpress.backend" + + _description = "Base WordPress Connector Component" diff --git a/connector_wordpress/components/export_mapper.py b/connector_wordpress/components/export_mapper.py new file mode 100644 index 000000000..52a61dd56 --- /dev/null +++ b/connector_wordpress/components/export_mapper.py @@ -0,0 +1,9 @@ +# Copyright NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import AbstractComponent + + +class WordPressExportMapper(AbstractComponent): + _name = "wordpress.export.mapper" + _inherit = ["connector.extension.export.mapper", "base.wordpress.connector"] diff --git a/connector_wordpress/components/exporter.py b/connector_wordpress/components/exporter.py new file mode 100644 index 000000000..f670c4716 --- /dev/null +++ b/connector_wordpress/components/exporter.py @@ -0,0 +1,41 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import logging + +from odoo.addons.component.core import AbstractComponent + +_logger = logging.getLogger(__name__) + + +class WordPressRecordDirectExporter(AbstractComponent): + """Base Exporter for WordPress""" + + _name = "wordpress.record.direct.exporter" + _inherit = [ + "connector.extension.generic.record.direct.exporter", + "base.wordpress.connector", + ] + + def _get_lock_name(self, relation): + lock_name = "export_record({}, {}, {}, {})".format( + self.backend_record._name, + self.backend_record.id, + relation._name, + relation.checksum, + ) + return lock_name + + +class WordPressBatchExporter(AbstractComponent): + """The role of a BatchExporter is to search for a list of + items to export, then it can either export them directly or delay + the export of each item separately. + """ + + _name = "wordpress.batch.exporter" + _inherit = [ + "connector.extension.generic.batch.exporter", + "base.wordpress.connector", + ] diff --git a/connector_wordpress/i18n/es.po b/connector_wordpress/i18n/es.po new file mode 100644 index 000000000..9c2e9715c --- /dev/null +++ b/connector_wordpress/i18n/es.po @@ -0,0 +1,562 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * connector_wordpress +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2024-10-16 09:55+0000\n" +"PO-Revision-Date: 2024-10-16 09:55+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: connector_wordpress +#: model:ir.model.constraint,message:connector_wordpress.constraint_wordpress_binding_internal_uniq +#: model:ir.model.constraint,message:connector_wordpress.constraint_wordpress_ir_attachment_internal_uniq +msgid "A binding already exists with the same External (odoo_id) ID." +msgstr "Existe un enlace con el mismo ID externo (odoo_id)." + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__access_token +msgid "Access Token" +msgstr "Token de acceso" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__active +msgid "Active" +msgstr "Activo" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_product_image__alternate_text +msgid "Alternate Text" +msgstr "Texto alternativo" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.view_product_image_form +msgid "Alternate text" +msgstr "Texto alternativo" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__application_password +msgid "Application Password" +msgstr "Contraseña de la aplicación" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_backend_view_form +msgid "Archived" +msgstr "Archivado" + +#. module: connector_wordpress +#: model:ir.actions.act_window,name:connector_wordpress.wordpress_ir_attachment_action +#: model:ir.model,name:connector_wordpress.model_ir_attachment +msgid "Attachment" +msgstr "Archivo adjunto" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__local_url +msgid "Attachment URL" +msgstr "URL del archivo adjunto" + +#. module: connector_wordpress +#: model:ir.ui.menu,name:connector_wordpress.wordpress_backend_menu_action +msgid "Backends" +msgstr "" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_backend_view_form +msgid "Check Connection" +msgstr "Comprobar conexión" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__checksum +msgid "Checksum/SHA1" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__chunk_size +msgid "Chunk Size" +msgstr "Tamaño del Chunk" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__company_id +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__company_id +msgid "Company" +msgstr "Compañía" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__create_uid +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__create_date +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__db_datas +msgid "Database Data" +msgstr "Datos de la base de datos" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_backend_view_form +msgid "Debug" +msgstr "Depurar" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__description +msgid "Description" +msgstr "Descripción" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_ir_attachment__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_product_document__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_product_image__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_product_product__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_product_template__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_binding__display_name +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#: code:addons/connector_wordpress/models/common/tools.py:0 +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Duplicated field %s" +msgstr "Campo duplicado %s" + +#. module: connector_wordpress +#: model:ir.ui.menu,name:connector_wordpress.wordpress_external_objects_menu +msgid "External Objects" +msgstr "Objetos externos" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__datas +msgid "File Content (base64)" +msgstr "Contenido del archivo (base64)" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__raw +msgid "File Content (raw)" +msgstr "Contenido del archivo (raw)" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__file_size +msgid "File Size" +msgstr "Tamaño del archivo" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_ir_attachment__id +#: model:ir.model.fields,field_description:connector_wordpress.field_product_document__id +#: model:ir.model.fields,field_description:connector_wordpress.field_product_image__id +#: model:ir.model.fields,field_description:connector_wordpress.field_product_product__id +#: model:ir.model.fields,field_description:connector_wordpress.field_product_template__id +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__id +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_binding__id +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__id +msgid "ID" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__wordpress_idattachment +msgid "ID Attachment" +msgstr "ID del adjunto" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__test_database +msgid "" +"If a test database is being used, the attachments routes can be empty.This " +"check allow to avoid the error of not finding the image and don't raise an " +"error." +msgstr "" +"Si se está utilizando una base de datos de prueba, las rutas de los " +"adjuntos pueden estar vacías. Esta comprobación permite evitar el error de " +"no encontrar la imagen y no generar un error." + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__image_height +msgid "Image Height" +msgstr "Altura de la imagen" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__image_src +msgid "Image Src" +msgstr "Fuente de la imagen" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__image_width +msgid "Image Width" +msgstr "Ancho de la imagen" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__index_content +msgid "Indexed Content" +msgstr "Contenido indexado" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__odoo_id +msgid "Ir Attachment" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__public +msgid "Is public document" +msgstr "Es un documento público" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__key +msgid "Key" +msgstr "Clave" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Key %s already exists" +msgstr "La clave %s ya existe" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_ir_attachment____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_product_document____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_product_image____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_product_product____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_product_template____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_binding____last_update +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__write_uid +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__write_uid +msgid "Last Updated by" +msgstr "Ultima actualización por" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__write_date +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__write_date +msgid "Last Updated on" +msgstr "Última actualización el" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_binding__sync_date +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__sync_date +msgid "Last synchronization date" +msgstr "Última fecha de sincronización" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__mimetype +msgid "Mime Type" +msgstr "Tipo Mime" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__sync_offset +msgid "" +"Minutes to start the synchronization before(negative)/after(positive) the " +"last one" +msgstr "" +"Minutos para iniciar la sincronización antes(negativo)/después(positivo) de " +"la última" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Multiple values found for '%s'" +msgstr "Se encontraron varios valores para '%s'" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__name +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__name +msgid "Name" +msgstr "Nombre" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Not equal operation not supported for non boolean fields" +msgstr "Operación no igual no soportada para campos no booleanos" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Operator %s not supported" +msgstr "Operador %s no soportado" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Operator '%(OPERATOR)s' only supports tuples or lists, not %(TYPES)s" +msgstr "Operador '%(OPERATOR)s' solo soporta tuplas o listas, no %(TYPES)s" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__original_id +msgid "Original (unoptimized, unresized) attachment" +msgstr "Adjunto original (no optimizado, no redimensionado)" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__page_size +msgid "Page Size" +msgstr "Tamaño de página" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_product_product +msgid "Product" +msgstr "Producto" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_product_document +msgid "Product Document" +msgstr "Documento del producto" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_product_image +msgid "Product Image" +msgstr "Imagen del producto" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_product_template +msgid "Product Template" +msgstr "Plantilla de producto" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_backend_view_form +msgid "Reset To draft Connection" +msgstr "Restablecer a conexión de borrador" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__res_field +msgid "Resource Field" +msgstr "Campo de recurso" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__res_id +msgid "Resource ID" +msgstr "ID de recurso" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__res_model +msgid "Resource Model" +msgstr "Modelo de recurso" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__res_name +msgid "Resource Name" +msgstr "Nombre de recurso" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_ir_attachment_view_tree +msgid "Resynchronize" +msgstr "Resincronizar" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__wordpress_source_url +msgid "Source URL" +msgstr "URL de origen" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__state +msgid "State" +msgstr "Estado" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__store_fname +msgid "Stored Filename" +msgstr "Nombre de archivo almacenado" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__sync_offset +msgid "Sync Offset" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_ir_attachment__key +msgid "" +"Technical field used to resolve multiple attachments in a multi-website " +"environment." +msgstr "" +"Campo técnico usado para resolver múltiples adjuntos en un entorno multi-" +"sitio web." + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__test_database +msgid "Test Database" +msgstr "Testear base de datos" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_ir_attachment__res_model +msgid "The database object this attachment will be attached to." +msgstr "La base de datos del objeto al que se adjuntará este adjunto." + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_ir_attachment__res_id +msgid "The record id this is attached to." +msgstr "El ID de registro al que está adjunto." + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__theme_template_id +msgid "Theme Template" +msgstr "Plantilla de tema" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__page_size +msgid "" +"This field is used in order to define the number of records imported at the " +"same time." +msgstr "" +"Este campo se usa para definir el número de registros importados al mismo " +"tiempo." + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__tz +msgid "This field is used to define in which timezone the backend will work." +msgstr "Este campo se usa para definir en qué zona horaria trabajará el backend." + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__chunk_size +msgid "" +"This field is used to define the chunk size to import from the backend." +msgstr "" +"Este campo se usa para definir el tamaño del fragmento a importar desde el " + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__tz +msgid "Timezone" +msgstr "Zona horaria" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_product_image__title +msgid "Title" +msgstr "Título" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.view_product_image_form +msgid "Tittle" +msgstr "Título" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__type +msgid "Type" +msgstr "Tipo" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Type {} not supported for operator {}" +msgstr "Tipo {} no soportado para el operador {}" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__url +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__url +msgid "Url" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__username +msgid "Username" +msgstr "Nombre de usuario" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__verify_ssl +msgid "Verify SSL certificate of the WordPress server." +msgstr "Verificar el certificado SSL del servidor de WordPress." + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__verify_ssl +msgid "Verify Ssl" +msgstr "Verificar SSL" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_backend__version +msgid "Version" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__website_id +msgid "Website" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__website_url +msgid "Website URL" +msgstr "" + +#. module: connector_wordpress +#: model:ir.ui.menu,name:connector_wordpress.wordpress_menu_root +msgid "WordPress" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_wordpress_backend +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_binding__backend_id +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__backend_id +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_backend_view_form +msgid "WordPress Backend" +msgstr "" + +#. module: connector_wordpress +#: model:ir.actions.act_window,name:connector_wordpress.wordpress_backend_action +msgid "WordPress Backends" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_wordpress_binding +msgid "WordPress Binding" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,field_description:connector_wordpress.field_ir_attachment__wordpress_bind_ids +#: model:ir.model.fields,field_description:connector_wordpress.field_mrp_document__wordpress_bind_ids +#: model:ir.model.fields,field_description:connector_wordpress.field_wordpress_ir_attachment__wordpress_bind_ids +msgid "WordPress Bindings" +msgstr "" + +#. module: connector_wordpress +#: model_terms:ir.ui.view,arch_db:connector_wordpress.wordpress_ir_attachment_view_form +msgid "WordPress Ir Attachment" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model,name:connector_wordpress.model_wordpress_ir_attachment +msgid "WordPress Ir Attachment Binding" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__url +msgid "WordPress URL" +msgstr "" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_backend__username +msgid "WordPress Username" +msgstr "WordPress Nombre de usuario" + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/common/tools.py:0 +#, python-format +msgid "Wrong domain clause format %s" +msgstr "Incorrecto formato de cláusula de dominio %s" + +#. module: connector_wordpress +#: model:ir.model.fields,help:connector_wordpress.field_wordpress_ir_attachment__type +msgid "" +"You can either upload a file from your computer or copy/paste an internet " +"link to your file." +msgstr "" +"Puede cargar un archivo desde su computadora o copiar/pegar un enlace de " +"internet a su archivo." + +#. module: connector_wordpress +#: code:addons/connector_wordpress/models/ir_attachment/ir_attachment.py:0 +#, python-format +msgid "it should always return a dictionary" +msgstr "debería siempre devolver un diccionario" diff --git a/connector_wordpress/models/__init__.py b/connector_wordpress/models/__init__.py new file mode 100644 index 000000000..00be3eb1c --- /dev/null +++ b/connector_wordpress/models/__init__.py @@ -0,0 +1,8 @@ +from . import backend +from . import binding +from . import common +from . import ir_attachment +from . import product_image +from . import product_template +from . import product_product +from . import product diff --git a/connector_wordpress/models/backend/__init__.py b/connector_wordpress/models/backend/__init__.py new file mode 100644 index 000000000..cf5195bf7 --- /dev/null +++ b/connector_wordpress/models/backend/__init__.py @@ -0,0 +1,2 @@ +from . import adapter +from . import backend diff --git a/connector_wordpress/models/backend/adapter.py b/connector_wordpress/models/backend/adapter.py new file mode 100644 index 000000000..368f81522 --- /dev/null +++ b/connector_wordpress/models/backend/adapter.py @@ -0,0 +1,15 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import logging + +from odoo.addons.component.core import Component + +_logger = logging.getLogger(__name__) + + +class WordPressBackendAdapter(Component): + _name = "wordpress.backend.adapter" + _inherit = "wordpress.adapter" + _description = "WordPress Backend Adapter" + + _apply_on = "wordpress.backend" diff --git a/connector_wordpress/models/backend/backend.py b/connector_wordpress/models/backend/backend.py new file mode 100644 index 000000000..b5a040941 --- /dev/null +++ b/connector_wordpress/models/backend/backend.py @@ -0,0 +1,35 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging + +from odoo import fields, models + +_logger = logging.getLogger(__name__) + + +class WordPressBackend(models.Model): + _name = "wordpress.backend" + _inherit = "connector.extension.backend" + _description = "WordPress Backend" + + url = fields.Char( + help="WordPress URL", + required=True, + ) + username = fields.Char( + help="WordPress Username", + required=True, + ) + application_password = fields.Char( + required=True, + ) + verify_ssl = fields.Boolean( + default=True, + help="Verify SSL certificate of the WordPress server.", + ) + test_database = fields.Boolean( + default=False, + help="If a test database is being used, the attachments routes can be empty." + "This check allow to avoid the error of not " + "finding the image and don't raise an error.", + ) diff --git a/connector_wordpress/models/binding/__init__.py b/connector_wordpress/models/binding/__init__.py new file mode 100644 index 000000000..0fec82e8a --- /dev/null +++ b/connector_wordpress/models/binding/__init__.py @@ -0,0 +1 @@ +from . import binding diff --git a/connector_wordpress/models/binding/binding.py b/connector_wordpress/models/binding/binding.py new file mode 100644 index 000000000..52a753049 --- /dev/null +++ b/connector_wordpress/models/binding/binding.py @@ -0,0 +1,27 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class WordPressBinding(models.AbstractModel): + _name = "wordpress.binding" + _inherit = "connector.extension.external.binding" + _description = "WordPress Binding" + + # binding fields + backend_id = fields.Many2one( + comodel_name="wordpress.backend", + string="WordPress Backend", + required=True, + ondelete="restrict", + ) + + _sql_constraints = [ + ( + "internal_uniq", + "unique(backend_id, odoo_id)", + "A binding already exists with the same External (odoo_id) ID.", + ), + ] diff --git a/connector_wordpress/models/common/tools.py b/connector_wordpress/models/common/tools.py new file mode 100644 index 000000000..5ab52ed5b --- /dev/null +++ b/connector_wordpress/models/common/tools.py @@ -0,0 +1,121 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +import datetime +import hashlib +import unicodedata + +from odoo import _ +from odoo.exceptions import ValidationError + + +def list2hash(_list): + _hash = hashlib.sha256() + for e in _list: + if isinstance(e, int): + e9 = str(e) + elif isinstance(e, str): + e9 = e + elif isinstance(e, float): + e9 = str(e) + elif e is None: + e9 = "" + else: + raise Exception("Unexpected type for a key: type %s" % type(e)) + _hash.update(e9.encode("utf8")) + return _hash.hexdigest() + + +def domain_to_normalized_dict(self, domain): + """Convert, if possible, standard Odoo domain to a dictionary. + To do so it is necessary to convert all operators to + equal '=' operator. + """ + res = {} + for elem in domain: + if len(elem) != 3: + raise ValidationError(_("Wrong domain clause format %s") % elem) + field, op, value = elem + if op == "=": + if field in res: + raise ValidationError(_("Duplicated field %s") % field) + res[field] = self._normalize_value(value) + elif op == "!=": + if not isinstance(value, bool): + raise ValidationError( + _("Not equal operation not supported for non boolean fields") + ) + if field in res: + raise ValidationError(_("Duplicated field %s") % field) + res[field] = self._normalize_value(not value) + elif op == "in": + if not isinstance(value, (tuple, list)): + raise ValidationError( + _( + "Operator '%(OPERATOR)s' only supports tuples or lists, not %(TYPES)s" + ) + % { + "OPERATOR": op, + "TYPES": type(value), + } + ) + if field in res: + raise ValidationError(_("Duplicated field %s") % field) + res[field] = self._normalize_value(value) + elif op in (">", ">=", "<", "<="): + if not isinstance(value, (datetime.date, datetime.datetime, int)): + raise ValidationError( + _("Type {} not supported for operator {}").format(type(value), op) + ) + if op in (">", "<"): + adj = 1 + if isinstance(value, (datetime.date, datetime.datetime)): + adj = datetime.timedelta(days=adj) + if op == "<": + op, value = "<=", value - adj + else: + op, value = ">=", value + adj + + res[field] = self._normalize_value(value) + else: + raise ValidationError(_("Operator %s not supported") % op) + + return res + + +def convert_item_to_json(item, ct, namespace): + jitem = {} + for path, func, key, multi in ct: + if key in jitem: + raise ValidationError(_("Key %s already exists") % key) + value = item.xpath(path, namespaces=namespace) + if not value: + jitem[key] = None + else: + if multi: + jitem[key] = func(value) + else: + if len(value) > 1: + raise ValidationError(_("Multiple values found for '%s'") % path) + else: + jitem[key] = func(value[0]) + return jitem + + +def convert_to_json(data, ct, namespace): + res = [] + for d in data: + res.append(convert_item_to_json(d, ct, namespace)) + return res + + +def slugify(value): + if not value: + return None + return ( + unicodedata.normalize("NFKD", value) + .encode("ascii", "ignore") + .decode("ascii") + .lower() + .replace(" ", "") + ) diff --git a/connector_wordpress/models/ir_attachment/__init__.py b/connector_wordpress/models/ir_attachment/__init__.py new file mode 100644 index 000000000..34fcac5bc --- /dev/null +++ b/connector_wordpress/models/ir_attachment/__init__.py @@ -0,0 +1,6 @@ +from . import adapter +from . import binder +from . import binding +from . import export_mapper +from . import exporter +from . import ir_attachment diff --git a/connector_wordpress/models/ir_attachment/adapter.py b/connector_wordpress/models/ir_attachment/adapter.py new file mode 100644 index 000000000..692e0c4b9 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/adapter.py @@ -0,0 +1,36 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import Component + + +class WordPressIrAttachment(Component): + _name = "wordpress.ir.attachment.adapter" + _inherit = "wordpress.adapter" + + _apply_on = "wordpress.ir.attachment" + + def create(self, data): # pylint: disable=W8106 + return self._exec("post", "media", data=data) + + def read(self, external_id): # pylint: disable=W8106 + # TODO: REVIEW: Check external_id_values, external_id and + # external_id_values["id] nullability + external_id_values = self.binder_for().id2dict(external_id, in_field=False) + return self._exec("get", "media/%s" % external_id_values["id"]) + + def search_read(self, domain=None): + binder = self.binder_for() + domain_dict = self._domain_to_normalized_dict(domain) + external_id_fields = binder.get_id_fields(in_field=False) + _, common_domain = self._extract_domain_clauses(domain, external_id_fields) + external_id_values = binder.dict2id2dict(domain_dict, in_field=False) + if external_id_values: + url = "media/%s" % external_id_values["id"] + else: + url = "media" + return self._exec("get", url, domain=common_domain) + + def write(self, external_id, data): # pylint: disable=W8106 + external_id_values = self.binder_for().id2dict(external_id, in_field=False) + return self._exec("put", "media/%s" % external_id_values["id"], data=data) diff --git a/connector_wordpress/models/ir_attachment/binder.py b/connector_wordpress/models/ir_attachment/binder.py new file mode 100644 index 000000000..58142a29f --- /dev/null +++ b/connector_wordpress/models/ir_attachment/binder.py @@ -0,0 +1,45 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo.addons.component.core import Component + + +class WordPressIrAttachmentBinder(Component): + _name = "wordpress.ir.attachment.binder" + _inherit = "wordpress.binder" + + _apply_on = "wordpress.ir.attachment" + + @property + def external_id(self): + return ["id"] + + @property + def internal_id(self): + return ["wordpress_idattachment"] + + @property + def external_alt_id(self): + return [] + + def _get_external_record_domain(self, relation, values): + equivalent_binding_attachment = self.env["wordpress.ir.attachment"].search( + [ + # I add sudo() here because only superuser and users belonging to + # the 'settings' group can access information from ir.attachment + # if it has a linked res_field + ("checksum", "=", relation.sudo().checksum), + ("backend_id", "=", self.backend_record.id), + ], + limit=1, + ) + if equivalent_binding_attachment: + return [("id", "=", equivalent_binding_attachment.wordpress_idattachment)] + else: + return None + + def _additional_external_binding_fields(self, external_data): + return { + **super()._additional_external_binding_fields(external_data), + "wordpress_source_url": external_data["source_url"], + } diff --git a/connector_wordpress/models/ir_attachment/binding.py b/connector_wordpress/models/ir_attachment/binding.py new file mode 100644 index 000000000..f7ac68dd4 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/binding.py @@ -0,0 +1,26 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class WordPressIrAttachment(models.Model): + _name = "wordpress.ir.attachment" + _inherit = "wordpress.binding" + _inherits = {"ir.attachment": "odoo_id"} + _description = "WordPress Ir Attachment Binding" + + odoo_id = fields.Many2one( + comodel_name="ir.attachment", + string="Ir Attachment", + required=True, + ondelete="cascade", + ) + wordpress_idattachment = fields.Integer( + string="ID Attachment", + readonly=True, + ) + wordpress_source_url = fields.Char( + string="Source URL", + readonly=True, + ) diff --git a/connector_wordpress/models/ir_attachment/export_mapper.py b/connector_wordpress/models/ir_attachment/export_mapper.py new file mode 100644 index 000000000..8e10de822 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/export_mapper.py @@ -0,0 +1,43 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import os + +from odoo.tools import mimetypes + +from odoo.addons.component.core import Component +from odoo.addons.connector.components.mapper import mapping + + +class WordPressIrAttachmentExportMapper(Component): + _name = "wordpress.ir.attachment.export.mapper" + _inherit = "wordpress.export.mapper" + + _apply_on = "wordpress.ir.attachment" + + # TODO: create header in adapter, sending mimetype and file name + @mapping + def name(self, record): + attachment_path = record._full_path(record.store_fname) + file_name = os.path.basename(attachment_path) + # We need to concatenate the filename with the extension + # because odoo does not store the extension + # and wordpress needs it to recognize the file type + file_extension = mimetypes.guess_extension(record.mimetype) + headers = { + "content-disposition": "attachment; filename=%s" + % (file_name + file_extension), + "content-type": record.mimetype, + } + data = open(attachment_path, "rb").read() + return { + # "id": record.id, + # "filename": file_name, + # "mimetype": record.mimetype, + "headers": headers, + "data": data, + } + + @mapping + def seo_meta_data(self, record): + return record._get_seo_meta_data() diff --git a/connector_wordpress/models/ir_attachment/exporter.py b/connector_wordpress/models/ir_attachment/exporter.py new file mode 100644 index 000000000..29bef2bb3 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/exporter.py @@ -0,0 +1,50 @@ +# Copyright NuoBiT Solutions - Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from pathlib import Path + +from odoo.addons.component.core import Component + + +class WordPressIrAttachmentBatchDirectExporter(Component): + """Export the WordPress Ir Attachment. + + For every Ir Attachment in the list, execute inmediately. + """ + + _name = "wordpress.ir.attachment.batch.direct.exporter" + _inherit = "connector.extension.generic.batch.direct.exporter" + + _apply_on = "wordpress.ir.attachment" + + +class WordPressIrAttachmentBatchDelayedExporter(Component): + """Export the WordPress Ir Attachment. + + For every Ir Attachment in the list, a delayed job is created. + """ + + _name = "wordpress.ir.attachment.batch.delayed.exporter" + _inherit = "connector.extension.generic.batch.delayed.exporter" + + _apply_on = "wordpress.ir.attachment" + + +class WordPressIrAttachmentExporter(Component): + _name = "wordpress.ir.attachment.record.direct.exporter" + _inherit = "wordpress.record.direct.exporter" + + _apply_on = "wordpress.ir.attachment" + + def _has_to_skip(self, relation): + res = super()._has_to_skip(relation) + if self.backend_record.test_database: + if not Path(relation._full_path(relation.store_fname)).is_file(): + return True + return res + + def _get_sql_lock(self, record): + return "SELECT checksum FROM %s WHERE CHECKSUM = '%s' FOR UPDATE NOWAIT" % ( + record._table, + record.sudo().checksum, + ) diff --git a/connector_wordpress/models/ir_attachment/ir_attachment.py b/connector_wordpress/models/ir_attachment/ir_attachment.py new file mode 100644 index 000000000..d90d39d36 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/ir_attachment.py @@ -0,0 +1,27 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import _, fields, models +from odoo.exceptions import ValidationError + + +class IrAttachment(models.Model): + _inherit = "ir.attachment" + + wordpress_bind_ids = fields.One2many( + comodel_name="wordpress.ir.attachment", + inverse_name="odoo_id", + string="WordPress Bindings", + ) + + # TODO:REVIEW: We can move this method to another module + def _get_seo_meta_data(self): + self.ensure_one() + model_obj = self.env[self.res_model].browse(self.res_id) + if hasattr(model_obj, "_get_seo_meta_data"): + meta_data = model_obj._get_seo_meta_data() + if not isinstance(meta_data, dict): + raise ValidationError(_("it should always return a dictionary")) + return meta_data + else: + return {} diff --git a/connector_wordpress/models/product/__init__.py b/connector_wordpress/models/product/__init__.py new file mode 100644 index 000000000..9649db77a --- /dev/null +++ b/connector_wordpress/models/product/__init__.py @@ -0,0 +1 @@ +from . import product diff --git a/connector_wordpress/models/product/product.py b/connector_wordpress/models/product/product.py new file mode 100644 index 000000000..ce8578e65 --- /dev/null +++ b/connector_wordpress/models/product/product.py @@ -0,0 +1,20 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models + + +class ProductDocumentMixin(models.AbstractModel): + _inherit = "product.document" + + def write(self, vals): + if "datas" in vals: + attachment = self.env["ir.attachment"].search( + [ + ("res_model", "=", self._name), + ("res_id", "=", self.id), + ("res_field", "=", "datas"), + ] + ) + attachment.sudo().wordpress_bind_ids.unlink() + return super().write(vals) diff --git a/connector_wordpress/models/product_image/__init__.py b/connector_wordpress/models/product_image/__init__.py new file mode 100644 index 000000000..702d8f34c --- /dev/null +++ b/connector_wordpress/models/product_image/__init__.py @@ -0,0 +1 @@ +from . import product_image diff --git a/connector_wordpress/models/product_image/product_image.py b/connector_wordpress/models/product_image/product_image.py new file mode 100644 index 000000000..9ad28353b --- /dev/null +++ b/connector_wordpress/models/product_image/product_image.py @@ -0,0 +1,34 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class ProductImage(models.Model): + _inherit = "product.image" + + title = fields.Char( + translate=True, + ) + alternate_text = fields.Text( + translate=True, + ) + + def _get_seo_meta_data(self): + self.ensure_one() + return { + "title": self.title, + "alt_text": self.alternate_text, + } + + def write(self, vals): + if "image_1920" in vals: + attachment = self.env["ir.attachment"].search( + [ + ("res_model", "=", self._name), + ("res_id", "=", self.id), + ("res_field", "=", "image_1920"), + ] + ) + attachment.sudo().wordpress_bind_ids.unlink() + return super().write(vals) diff --git a/connector_wordpress/models/product_product/__init__.py b/connector_wordpress/models/product_product/__init__.py new file mode 100644 index 000000000..5c74c8c30 --- /dev/null +++ b/connector_wordpress/models/product_product/__init__.py @@ -0,0 +1 @@ +from . import product_product diff --git a/connector_wordpress/models/product_product/product_product.py b/connector_wordpress/models/product_product/product_product.py new file mode 100644 index 000000000..5e745275f --- /dev/null +++ b/connector_wordpress/models/product_product/product_product.py @@ -0,0 +1,20 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + def write(self, vals): + if "image_1920" in vals: + attachment = self.env["ir.attachment"].search( + [ + ("res_model", "=", self._name), + ("res_id", "=", self.id), + ("res_field", "=", "image_variant_1920"), + ] + ) + attachment.sudo().wordpress_bind_ids.unlink() + return super().write(vals) diff --git a/connector_wordpress/models/product_template/__init__.py b/connector_wordpress/models/product_template/__init__.py new file mode 100644 index 000000000..e8fa8f6bf --- /dev/null +++ b/connector_wordpress/models/product_template/__init__.py @@ -0,0 +1 @@ +from . import product_template diff --git a/connector_wordpress/models/product_template/product_template.py b/connector_wordpress/models/product_template/product_template.py new file mode 100644 index 000000000..ac9e62ae4 --- /dev/null +++ b/connector_wordpress/models/product_template/product_template.py @@ -0,0 +1,20 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + def write(self, vals): + if "image_1920" in vals: + attachment = self.env["ir.attachment"].search( + [ + ("res_model", "=", self._name), + ("res_id", "=", self.id), + ("res_field", "=", "image_1920"), + ] + ) + attachment.sudo().wordpress_bind_ids.unlink() + return super().write(vals) diff --git a/connector_wordpress/security/connector_wordpress.xml b/connector_wordpress/security/connector_wordpress.xml new file mode 100644 index 000000000..34afa6e45 --- /dev/null +++ b/connector_wordpress/security/connector_wordpress.xml @@ -0,0 +1,13 @@ + + + + + Connector WordPress backend multi-company rule + + ['|', ('company_id', '=', False), ('company_id', 'in', company_ids)] + + + diff --git a/connector_wordpress/security/ir.model.access.csv b/connector_wordpress/security/ir.model.access.csv new file mode 100644 index 000000000..2a8a89be0 --- /dev/null +++ b/connector_wordpress/security/ir.model.access.csv @@ -0,0 +1,5 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +"access_wordpress_backend_manager","wordpress_backend connector manager","model_wordpress_backend","connector.group_connector_manager",1,1,1,1 +"access_wordpress_backend_user","wordpress_backend connector user","model_wordpress_backend","base.group_user",1,0,0,0 +"access_wordpress_ir_attachment_manager","wordpress_ir_attachment connector manager","model_wordpress_ir_attachment","connector.group_connector_manager",1,1,1,1 +"access_wordpress_ir_attachment_user","wordpress_ir_attachment connector user","model_wordpress_ir_attachment","base.group_user",1,0,0,0 diff --git a/connector_wordpress/static/description/icon.png b/connector_wordpress/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd641e792c30455187ca30940bc0f329ce8bbb0 GIT binary patch literal 6342 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 literal 0 HcmV?d00001 diff --git a/connector_wordpress/views/connector_wordpress_menu.xml b/connector_wordpress/views/connector_wordpress_menu.xml new file mode 100644 index 000000000..f2b231dd6 --- /dev/null +++ b/connector_wordpress/views/connector_wordpress_menu.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/connector_wordpress/views/ir_attachment_views.xml b/connector_wordpress/views/ir_attachment_views.xml new file mode 100644 index 000000000..08d32651c --- /dev/null +++ b/connector_wordpress/views/ir_attachment_views.xml @@ -0,0 +1,42 @@ + + + + + wordpress.ir.attachment.view.form + wordpress.ir.attachment + +
+ + + + + + +
+
+
+ + wordpress.ir.attachment.view.tree + wordpress.ir.attachment + + + + + + +