diff --git a/connector_wordpress/README.rst b/connector_wordpress/README.rst new file mode 100644 index 000000000..b1ccd7843 --- /dev/null +++ b/connector_wordpress/README.rst @@ -0,0 +1,67 @@ +=================== +Connector WordPress +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:e5d78a8cbeaa3c3c83de6a8d06ee85faf15bdcb3420c70075c2d53874b4cfa30 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-NuoBiT%2Fodoo--addons-lightgray.png?logo=github + :target: https://github.com/NuoBiT/odoo-addons/tree/18.0/connector_wordpress + :alt: NuoBiT/odoo-addons + +|badge1| |badge2| |badge3| + +- `NuoBiT `__: + + - Kilian Niubo kniubo@nuobit.com + - Deniz Gallo dgallo@nuobit.com + +**Table of contents** + +.. contents:: + :local: + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +------- + +* NuoBiT Solutions SL + +Contributors +------------ + +- `NuoBiT `__: + + - Kilian Niubo kniubo@nuobit.com + - Eric Antones eantones@nuobit.com + - Deniz Gallo dgallo@nuobit.com + +Maintainers +----------- + +This module is part of the `NuoBiT/odoo-addons `_ project on GitHub. + +You are welcome to contribute. 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..9e4774704 --- /dev/null +++ b/connector_wordpress/__manifest__.py @@ -0,0 +1,32 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# Copyright NuoBiT Solutions - Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + "name": "Connector WordPress", + "version": "18.0.1.0.0", + "author": "NuoBiT Solutions SL", + "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..8a557ae45 --- /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.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..59c8c814a --- /dev/null +++ b/connector_wordpress/components/exporter.py @@ -0,0 +1,40 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo +# 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.record.direct.exporter", + "base.wordpress.connector", + ] + + def _get_lock_name(self, relation): + lock_name = ( + f"export_record({self.backend_record._name}, " + f"{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.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..b4fae97e0 --- /dev/null +++ b/connector_wordpress/models/common/tools.py @@ -0,0 +1,129 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright NuoBiT Solutions - Kilian Niubo +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo +# 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 %(type)s") % { + "type": 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 %(type)s not supported for operator %(operator)s") + % { + "type": type(value), + "operator": 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..a8b00652e --- /dev/null +++ b/connector_wordpress/models/ir_attachment/adapter.py @@ -0,0 +1,37 @@ +# Copyright NuoBiT Solutions - Kilian Niubo +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo +# 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", f"media/{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 = f"media/{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", f"media/{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..ccfdc3f06 --- /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, relation): + return { + **super()._additional_external_binding_fields(external_data, relation), + "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..2685c8bf6 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/binding.py @@ -0,0 +1,36 @@ +# 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, + required=True, + ) + wordpress_source_url = fields.Char( + string="Source URL", + readonly=True, + required=True, + ) + + _sql_constraints = [ + ( + "external_uniq", + "unique(backend_id, wordpress_idattachment)", + "A binding already exists with the same External ID (idAttachment)", + ), + ] 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..ac7a9e61e --- /dev/null +++ b/connector_wordpress/models/ir_attachment/exporter.py @@ -0,0 +1,51 @@ +# Copyright NuoBiT Solutions - Eric Antones +# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo +# 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.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.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 ( + f"SELECT checksum FROM {record._table} WHERE " + f"CHECKSUM = '{record.sudo().checksum}' FOR UPDATE NOWAIT" + ) 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..e5385f342 --- /dev/null +++ b/connector_wordpress/models/ir_attachment/ir_attachment.py @@ -0,0 +1,29 @@ +# 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() + if not self.res_model or not self.res_id: + return {} + 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/pyproject.toml b/connector_wordpress/pyproject.toml new file mode 100644 index 000000000..4231d0ccc --- /dev/null +++ b/connector_wordpress/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/connector_wordpress/readme/CONTRIBUTORS.md b/connector_wordpress/readme/CONTRIBUTORS.md new file mode 100644 index 000000000..0355cecbc --- /dev/null +++ b/connector_wordpress/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- [NuoBiT](https://www.nuobit.com): + - Kilian Niubo + - Eric Antones + - Deniz Gallo diff --git a/connector_wordpress/readme/DESCRIPTION.md b/connector_wordpress/readme/DESCRIPTION.md new file mode 100644 index 000000000..5d535bfca --- /dev/null +++ b/connector_wordpress/readme/DESCRIPTION.md @@ -0,0 +1,3 @@ +- [NuoBiT](https://www.nuobit.com): + - Kilian Niubo + - Deniz Gallo 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 000000000..1cd641e79 Binary files /dev/null and b/connector_wordpress/static/description/icon.png differ diff --git a/connector_wordpress/static/description/index.html b/connector_wordpress/static/description/index.html new file mode 100644 index 000000000..96bfe904f --- /dev/null +++ b/connector_wordpress/static/description/index.html @@ -0,0 +1,427 @@ + + + + + +Connector WordPress + + + +
+

Connector WordPress

+ + +

Beta License: AGPL-3 NuoBiT/odoo-addons

+ +

Table of contents

+ +
+

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 to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • NuoBiT Solutions SL
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is part of the NuoBiT/odoo-addons project on GitHub.

+

You are welcome to contribute.

+
+
+
+ + 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..65d4ab20d --- /dev/null +++ b/connector_wordpress/views/ir_attachment_views.xml @@ -0,0 +1,43 @@ + + + + + wordpress.ir.attachment.view.form + wordpress.ir.attachment + +
+ + + + + + +
+
+
+ + wordpress.ir.attachment.view.list + wordpress.ir.attachment + + + + + + +