Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions connector_extension_wordpress/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
=============================
Connector Extension Wordpress
=============================

..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5acc311916374a5c8b68547091798eb4307996ed461862d35d5563b1c1b6bdd1
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

.. |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-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-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_extension_wordpress
:alt: NuoBiT/odoo-addons

|badge1| |badge2| |badge3|

This module extends the connector extension module to add support for
Wordpress

**Table of contents**

.. contents::
:local:

Bug Tracker
===========

Bugs are tracked on `GitHub Issues <https://github.com/NuoBiT/odoo-addons/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 <https://github.com/NuoBiT/odoo-addons/issues/new?body=module:%20connector_extension_wordpress%0Aversion:%2018.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.

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

Credits
=======

Authors
-------

* NuoBiT Solutions SL

Contributors
------------

- `NuoBiT <https://www.nuobit.com>`__:

- 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 <https://github.com/NuoBiT/odoo-addons/tree/18.0/connector_extension_wordpress>`_ project on GitHub.

You are welcome to contribute.
1 change: 1 addition & 0 deletions connector_extension_wordpress/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import components
16 changes: 16 additions & 0 deletions connector_extension_wordpress/__manifest__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Copyright NuoBiT Solutions - Kilian Niubo <kniubo@nuobit.com>
# Copyright NuoBiT Solutions - Eric Antones <eantones@nuobit.com>
# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo <dgallo@nuobit.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

{
"name": "Connector Extension Wordpress",
"summary": "This module extends the connector extension module "
"to add support for Wordpress",
"version": "18.0.1.0.0",
"author": "NuoBiT Solutions SL",
"license": "LGPL-3",
"category": "Connector",
"website": "https://github.com/NuoBiT/odoo-addons",
"depends": ["connector_extension"],
}
1 change: 1 addition & 0 deletions connector_extension_wordpress/components/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import adapter
230 changes: 230 additions & 0 deletions connector_extension_wordpress/components/adapter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
# Copyright NuoBiT Solutions - Kilian Niubo <kniubo@nuobit.com>
# Copyright 2026 NuoBiT Solutions SL - Deniz Gallo <dgallo@nuobit.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)

import json
import logging

import requests

from odoo import _
from odoo.exceptions import ValidationError

from odoo.addons.component.core import AbstractComponent
from odoo.addons.connector.exception import RetryableJobError

_logger = logging.getLogger(__name__)


class ConnectorExtensionWordpressAdapterCRUD(AbstractComponent):
_name = "connector.extension.wordpress.adapter.crud"
_inherit = "connector.extension.adapter.crud"

def _exec(self, op, resource, *args, **kwargs):
func = getattr(self, f"_exec_{op}")
return func(resource, *args, **kwargs)

def _manage_error_codes(
self, op, res_data, res, resource, *args, raise_on_error=True, **kwargs
):
if not res.ok:
error_message = None
if res.status_code == 404:
if res_data.get("code") == "rest_post_invalid_id":
error_message = _(
"Error: '%(message)s'. Probably the %(resource)s has been "
"removed from WordPress. "
"If it's the case, try to remove the binding of the %(model)s."
) % {
"message": res_data.get("message"),
"resource": resource,
"model": self.model._name,
}
elif res.status_code == 500:
if res_data.get("code") == "rest_upload_sideload_error":
error_message = _(
"Error: '%(message)s'. Probably the image or document "
"is uploaded with bad format. "
"Please, review on database: %(resource)s"
) % {
"message": res_data["message"],
"resource": kwargs["headers"],
}
if not error_message:
error_message = _("Error: %(message)s, Resource: %(resource)s") % {
"message": res_data,
"resource": resource,
}
if raise_on_error:
raise ValidationError(error_message)
return error_message
return res_data

def _exec_wp_call(self, op, resource, *args, **kwargs):
url = self.backend_record.url + "/wp-json/wp/v2/" + resource
func = getattr(requests, op)
try:
res = func(url, *args, **kwargs)
res_data = res.json()
res_data = self._manage_error_codes(
op, res_data, res, resource, *args, **kwargs
)
result = {
"ok": res.ok,
"status_code": res.status_code,
"headers": res.headers,
"data": res_data,
}
except requests.exceptions.ConnectionError as e:
raise RetryableJobError(_("Error connecting to WordPress: %s") % e) from e
except json.JSONDecodeError as e:
raise ValidationError(
_("Error decoding json WordPress response: %(message)s\n%(response)s")
% {
"message": e,
"response": res.text if res else _("No response"),
}
) from e
return result

def _get_search_fields(self):
return ["modified_after", "offset", "per_page", "page"]

def get_total_items(self, resource, domain=None):
filters_values = self._get_search_fields()
real_domain, common_domain = self._extract_domain_clauses(
domain, filters_values
)
params = self._domain_to_normalized_dict(real_domain)
# TODO: make an optimization to get the total items and use the result
params["per_page"] = 1
result = self._exec_wp_call(
"get",
resource,
auth=(
self.backend_record.username,
self.backend_record.application_password,
),
params=params,
verify=self.backend_record.verify_ssl,
)

total_items_header = result["headers"]._store.get("x-wp-total")
if total_items_header:
total_items_header = int(total_items_header[1])
else:
# WordPress returns a dict if the response is a single item
if not isinstance(result["data"], list):
result["data"] = [result["data"]]
total_items_header = len(result["data"])
return total_items_header

# TODO: REVIEW: Unify with connector_extension_woocommerce
def _exec_get(self, resource, *args, **kwargs):
if resource == "system_status":
return self._exec_wp_call(
"get",
resource,
*args,
auth=(
self.backend_record.username,
self.backend_record.application_password,
),
verify=self.backend_record.verify_ssl,
**kwargs,
)
# WooCommerce has the parameter next on the response headers
# to get the next page but we can't use it because if we use
# the offset, the next page will have the same items as the first page.
# It looks like a bug in WooCommerce API.
domain = []
if "domain" in kwargs:
domain = kwargs.pop("domain")
search_fields = self._get_search_fields()
real_domain, common_domain = self._extract_domain_clauses(domain, search_fields)
params = self._domain_to_normalized_dict(real_domain)
if "limit" in kwargs:
limit = kwargs.pop("limit")
else:
limit = self.get_total_items(resource, domain)
params["offset"] = (
kwargs.pop("offset") if "offset" in kwargs and "offset" not in params else 0
)
page_size = self.backend_record.page_size
params["per_page"] = page_size if page_size > 0 else 100
data = []
while len(data) < limit:
if page_size > limit - len(data):
params["per_page"] = limit - len(data)
res = self._exec_wp_call(
"get",
resource,
*args,
params=params,
auth=(
self.backend_record.username,
self.backend_record.application_password,
),
verify=self.backend_record.verify_ssl,
**kwargs,
)
# WooCommerce returns a dict if the response is a single item
if not isinstance(res["data"], list):
res["data"] = [res["data"]]
data += res["data"]
params["offset"] += len(res["data"])
return self._filter(data, common_domain)

def _exec_post(self, resource, *args, **kwargs):
auth = (self.backend_record.username, self.backend_record.application_password)
if "wordpress_backend_id" in self.backend_record:
backend = self.backend_record.wordpress_backend_id
auth = (backend.username, backend.application_password)
data_aux = kwargs.pop("data", {})
headers = data_aux.pop("headers", {})
data = data_aux.pop("data", {})
res = self._exec_wp_call(
"post",
resource,
data=data,
params=data_aux,
headers=headers,
auth=auth,
verify=self.backend_record.verify_ssl,
)

return res["data"]

def _exec_put(self, resource, *args, **kwargs):
auth = (self.backend_record.username, self.backend_record.application_password)
if "wordpress_backend_id" in self.backend_record:
backend = self.backend_record.wordpress_backend_id
auth = (backend.username, backend.application_password)
data_aux = kwargs.pop("data", {})
headers = data_aux.pop("headers", {})
data = data_aux.pop("data", {})
res = self._exec_wp_call(
"put",
resource,
*args,
data=data,
params=data_aux,
headers=headers,
auth=auth,
verify=self.backend_record.verify_ssl,
**kwargs,
)
return res["data"]

def _exec_delete(self, resource, *args, **kwargs):
raise NotImplementedError()

def _exec_options(self, resource, *args, **kwargs):
raise NotImplementedError()

def get_version(self):
settings = self._exec("get", "settings")
if settings and settings[0].get("title"):
return "Wordpress '{}' connected".format(settings[0].get("title"))
else:
raise ValidationError(_("Wordpress not connected"))
64 changes: 64 additions & 0 deletions connector_extension_wordpress/i18n/ca.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * connector_extension_wordpress
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2024-10-22 12:56+0000\n"
"PO-Revision-Date: 2024-10-22 12:56+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_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid "Error connecting to WordPress: %s"
msgstr "Error al conectar amb WordPress: %s"

#. module: connector_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid ""
"Error decoding json WordPress response: %s\n"
"%s"
msgstr ""
"Error al descodificar la resposta json de WordPress: %s\n"
"%s"

#. module: connector_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid "Error: %s, Resource: %s"
msgstr "Error: %s, Recurs: %s"

#. module: connector_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid ""
"Error: '%s'. Probably the %s has been removed from WordPress. If it's the "
"case, try to remove the binding of the %s."
msgstr ""
"Error: '%s'. Probablement el %s ha estat eliminat de WordPress. Si és el "
"cas, intenta eliminar la vinculació del %s."

#. module: connector_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid ""
"Error: '%s'. Probably the image or document is uploaded with bad format. "
"Please, review on database: %s"
msgstr ""
"Error: '%s'. Probablement la imatge o el document s'ha pujat amb un format "
"incorrecte. Si us plau, revisa a la base de dades: %s"

#. module: connector_extension_wordpress
#: code:addons/connector_extension_wordpress/components/adapter.py:0
#, python-format
msgid "Wordpress not connected"
msgstr "WordPress no connectat"
Loading
Loading