56 Commits

Author SHA1 Message Date
18dd9c7a1f Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:29 +00:00
1c6d6b1dcc Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:29 +00:00
b3d78f3f06 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:28 +00:00
5d5fbb835e Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:28 +00:00
f259d7da1b Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:27 +00:00
433f68b5a4 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:27 +00:00
3729ee8cd6 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:26 +00:00
261e8aea62 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:25 +00:00
a1dd66ec6a Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:25 +00:00
f579fbc83f Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:24 +00:00
bd2cfbcc3d Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:24 +00:00
9c009dddb5 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:23 +00:00
fd94630e79 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:22 +00:00
c8274bd0a6 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:22 +00:00
4bea3edbeb Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:21 +00:00
3aa73a29a5 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:20 +00:00
5934b7cf4d Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:20 +00:00
39f0b6d406 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:19 +00:00
1c1a16a55a Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:18 +00:00
991507c29a Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:18 +00:00
553f5fa25f Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:17 +00:00
8c5ef8bfd2 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:17 +00:00
4e0580a2b4 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:16 +00:00
451e109b7f Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:15 +00:00
fa79d8c15d Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:15 +00:00
55800608ec Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:14 +00:00
63e66334af Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:14 +00:00
4b7d2f2efc Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:13 +00:00
a7b02a742a Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:12 +00:00
825ad03236 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:12 +00:00
484763b809 Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:11 +00:00
b0d2c5668c Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:11 +00:00
444278accb Tower: upload web_notify 16.0.3.2.0 (via marketplace) 2026-04-27 08:47:10 +00:00
c8b19a8c62 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:09 +00:00
1a3285cdc4 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:08 +00:00
cd55fd9f19 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:08 +00:00
d75d397e6a Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:07 +00:00
4e95aa47de Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:06 +00:00
0911b0d951 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:06 +00:00
1ea59d44f0 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:05 +00:00
b4fcbfdf2a Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:04 +00:00
cca99e065a Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:04 +00:00
ec6e3c8fd2 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:03 +00:00
2c1d9c3ef2 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:03 +00:00
583dd0dd15 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:02 +00:00
66ae014a38 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:01 +00:00
b2f175536a Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:01 +00:00
6794a1b842 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:47:00 +00:00
191f857aff Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:59 +00:00
bf6065aeb7 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:59 +00:00
00e6ff7e78 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:58 +00:00
1f5b011fce Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:58 +00:00
61db219e01 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:57 +00:00
771994f944 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:56 +00:00
def74bd656 Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:56 +00:00
6e4be30e3a Tower: upload rpc_helper 16.0.1.0.0 (via marketplace) 2026-04-27 08:46:55 +00:00
56 changed files with 4276 additions and 0 deletions

View File

@@ -0,0 +1,139 @@
===========
Disable RPC
===========
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3e95cc80fbae1cb569a46d799e19ecc6edf4f605f8d131a25bed6daf5057b512
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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-OCA%2Fserver--tools-lightgray.png?logo=github
:target: https://github.com/OCA/server-tools/tree/16.0/rpc_helper
:alt: OCA/server-tools
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-rpc_helper
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/server-tools&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
Provide helpers to authorize RPC calls.
**Table of contents**
.. contents::
:local:
Configuration
=============
Enable debug mode and go to "Technical -> Database Structure -> Models".
Open the model that you like to configure and go to the tab "RPC config".
There you see a text field which supports JSON configuration.
The configuration is the same you can pass via decorator.
The only difference is that you have to wrap values in a dictionary
like `{"disable": [...values...]}`.
To disable all calls::
{
"disable": ["all"],
}
To disable only some methods::
{
"disable": ["create", "write", "another_method"],
}
NOTE: on the resulting JSON will be automatically formatted on save for better readability.
Usage
=====
Via code
~~~~~~~~
Decorate an Odoo model class like this::
from odoo.addons.rpc_helper.decorator import disable_rpc
@disable_rpc()
class AverageModel(models.Model):
_inherit = "avg.model"
This will disable ALL calls.
To selectively disable only some methods::
@disable_rpc("create", "write", "any_method")
class AverageModel(models.Model):
_inherit = "avg.model"
Via `ir.model` configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See "Configuration" section.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/server-tools/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/server-tools/issues/new?body=module:%20rpc_helper%0Aversion:%2016.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
~~~~~~~
* Camptocamp
Contributors
~~~~~~~~~~~~
* Simone Orsi <simone.orsi@camptocamp.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-simahawk| image:: https://github.com/simahawk.png?size=40px
:target: https://github.com/simahawk
:alt: simahawk
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-simahawk|
This module is part of the `OCA/server-tools <https://github.com/OCA/server-tools/tree/16.0/rpc_helper>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,2 @@
from . import models
from .hooks import post_load_hook

View File

@@ -0,0 +1,17 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
{
"name": "Disable RPC",
"summary": """Helpers for disabling RPC calls""",
"version": "16.0.1.0.0",
"development_status": "Beta",
"license": "LGPL-3",
"website": "https://github.com/OCA/server-tools",
"author": "Camptocamp, Odoo Community Association (OCA)",
"maintainers": ["simahawk"],
"depends": ["base_sparse_field"],
"data": ["views/ir_model_views.xml"],
"post_load": "post_load_hook",
}

View File

@@ -0,0 +1,19 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
def disable_rpc(*config):
"""Decorate classes to disable RPC calls.
Possible values:
* none, block all methods
* *("$method_name1", "$method_name2"), blocks calls to specific methods
"""
def _decorator(target):
target._disable_rpc = ("all",) if len(config) == 0 else config
return target
return _decorator

View File

@@ -0,0 +1,22 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import logging
from odoo.service import model
from .patch import protected__execute_cr
_logger = logging.getLogger(__name__)
def patch__model_execute_cr():
"""Patch rpc model handler."""
protected__execute_cr._orig__execute_cr = model.execute_cr
model.execute_cr = protected__execute_cr
_logger.info("PATCHED odoo.service.model.execute")
def post_load_hook():
patch__model_execute_cr()

View File

@@ -0,0 +1,113 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * rpc_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-09-03 00:15+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids
msgid "Changeset Changes"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids
msgid "Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit
msgid ""
"Configure RPC config via JSON. Value must be a list of methods to disable "
"wrapped by a dict with key `disable`. Eg: {'disable': ['search', 'do_this']}"
"To disable all methods, use `{'disable: ['all']}`"
msgstr ""
"Configurar RPC vía JSON. El valor debe ser una lista de métodos a "
"deshabilitar envueltos por un dict con la clave `disable`. Ej: {'disable': "
"['search', 'do_this']}Para deshabilitar todos los métodos, usa `{'disable: "
"['all']}`"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_changesets
msgid "Count Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "Count Pending Changeset Changes"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets
msgid "Count Pending Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model,name:rpc_helper.model_ir_model
msgid "Models"
msgstr "Modelos"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "Object %s doesn't exist"
msgstr "El objeto %s no existe"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "RPC call on %s is not allowed"
msgstr "La llamada RPC en %s no está permitida"
#. module: rpc_helper
#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form
msgid "RPC config"
msgstr "Configuración RPC"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config
msgid "Rpc Config"
msgstr "Configuración Rpc"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit
msgid "Rpc Config Edit"
msgstr "Editar configuración Rpc"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search
msgid "Smart Search"
msgstr "Búsqueda inteligente"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "The number of pending changes of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changesets
msgid "The number of pending changesets of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_changesets
msgid "The overall number of changesets of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset
msgid "User Can See Changeset"
msgstr ""

View File

@@ -0,0 +1,122 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * rpc_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-08-13 04:58+0000\n"
"Last-Translator: Ignacio Buioli <ibuioli@gmail.com>\n"
"Language-Team: none\n"
"Language: es_AR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids
msgid "Changeset Changes"
msgstr "Cambios del Conjunto de Cambios"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids
msgid "Changesets"
msgstr "Conjunto de Cambios"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit
msgid ""
"Configure RPC config via JSON. Value must be a list of methods to disable "
"wrapped by a dict with key `disable`. Eg: {'disable': ['search', 'do_this']}"
"To disable all methods, use `{'disable: ['all']}`"
msgstr ""
"Configure los ajustes de RPC a través de JSON. El valor debe ser una lista "
"de métodos para deshabilitar envueltos por un dict con la clave "
"`deshabilitar`. Por ejemplo: {'disable': ['search', 'do_this']} Para "
"deshabilitar todos los métodos, use `{'disable: ['all']}`"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_changesets
msgid "Count Changesets"
msgstr "Cuenta de Configuración de Cambios"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "Count Pending Changeset Changes"
msgstr "Cuenta de los Cambios Pendientes de los Conjuntos de Cambios"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets
msgid "Count Pending Changesets"
msgstr "Cuenta de los Conjuntos de Cambios Pendientes"
#. module: rpc_helper
#: model:ir.model,name:rpc_helper.model_ir_model
msgid "Models"
msgstr "Modelos"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "Object %s doesn't exist"
msgstr "El Objeto %s no existe"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "RPC call on %s is not allowed"
msgstr "Las llamadas RPC en %s no están permitidas"
#. module: rpc_helper
#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form
msgid "RPC config"
msgstr "Configuración RPC"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config
msgid "Rpc Config"
msgstr "Configuración Rpc"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit
msgid "Rpc Config Edit"
msgstr "Editar la Configuración Rpc"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search
msgid "Smart Search"
msgstr "Búsqueda Inteligente"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "The number of pending changes of this record"
msgstr "El número de los cambios pendientes de este registro"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changesets
msgid "The number of pending changesets of this record"
msgstr "El número de las configuraciones de cambios pendientes de este registro"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_changesets
msgid "The overall number of changesets of this record"
msgstr "El número total de conjuntos de cambios de este registro"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset
msgid "User Can See Changeset"
msgstr "El Usuario Puede ver Conjuntos de Cambios"
#~ msgid "Display Name"
#~ msgstr "Mostrar Nombre"
#~ msgid "ID"
#~ msgstr "ID"
#~ msgid "Last Modified on"
#~ msgstr "Última modificación en"

View File

@@ -0,0 +1,113 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * rpc_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-01-16 16:06+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids
msgid "Changeset Changes"
msgstr "Modifiche dell'insieme di modifiche"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids
msgid "Changesets"
msgstr "Insiemi di modifiche"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit
msgid ""
"Configure RPC config via JSON. Value must be a list of methods to disable "
"wrapped by a dict with key `disable`. Eg: {'disable': ['search', 'do_this']}"
"To disable all methods, use `{'disable: ['all']}`"
msgstr ""
"Impostare configurazione RPC con JSON. Il valore deve essere un elenco di "
"metodi da disabilitare inseriti in un dizionario con chiave `disable`. Es. "
"{'disable': ['search', 'do_this']} Pr disabilitare tutti i metodi usare "
"`{'disable: ['all']}`"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_changesets
msgid "Count Changesets"
msgstr "Conta insiemi di modifiche"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "Count Pending Changeset Changes"
msgstr "Conteggio modifiche dell'insieme di modifiche in attesa"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets
msgid "Count Pending Changesets"
msgstr "Conteggio insieme di modifiche in attesa"
#. module: rpc_helper
#: model:ir.model,name:rpc_helper.model_ir_model
msgid "Models"
msgstr "Modelli"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "Object %s doesn't exist"
msgstr "L'oggetto %s non esiste"
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "RPC call on %s is not allowed"
msgstr "La chiamata RPC su %s non è consentita"
#. module: rpc_helper
#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form
msgid "RPC config"
msgstr "Configurazione RPC"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config
msgid "Rpc Config"
msgstr "Configurazione RPC"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit
msgid "Rpc Config Edit"
msgstr "Mdifica configurazione RPC"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search
msgid "Smart Search"
msgstr "Ricerca intelligente"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "The number of pending changes of this record"
msgstr "Numero di modifiche di questo record in attesa"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changesets
msgid "The number of pending changesets of this record"
msgstr "Numero di insiemi di modifiche in attesa di questo record"
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_changesets
msgid "The overall number of changesets of this record"
msgstr "Numero totale di insiemi di modifiche di questo record"
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset
msgid "User Can See Changeset"
msgstr "L'utente può vedere l'insieme delle modifiche"

View File

@@ -0,0 +1,106 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * rpc_helper
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \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: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_change_ids
msgid "Changeset Changes"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__changeset_ids
msgid "Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__rpc_config_edit
msgid ""
"Configure RPC config via JSON. Value must be a list of methods to disable "
"wrapped by a dict with key `disable`. Eg: {'disable': ['search', "
"'do_this']}To disable all methods, use `{'disable: ['all']}`"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_changesets
msgid "Count Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "Count Pending Changeset Changes"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__count_pending_changesets
msgid "Count Pending Changesets"
msgstr ""
#. module: rpc_helper
#: model:ir.model,name:rpc_helper.model_ir_model
msgid "Models"
msgstr ""
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "Object %s doesn't exist"
msgstr ""
#. module: rpc_helper
#. odoo-python
#: code:addons/rpc_helper/patch.py:0
#, python-format
msgid "RPC call on %s is not allowed"
msgstr ""
#. module: rpc_helper
#: model_terms:ir.ui.view,arch_db:rpc_helper.view_model_form
msgid "RPC config"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config
msgid "Rpc Config"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__rpc_config_edit
msgid "Rpc Config Edit"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__smart_search
msgid "Smart Search"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changeset_changes
msgid "The number of pending changes of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_pending_changesets
msgid "The number of pending changesets of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,help:rpc_helper.field_ir_model__count_changesets
msgid "The overall number of changesets of this record"
msgstr ""
#. module: rpc_helper
#: model:ir.model.fields,field_description:rpc_helper.field_ir_model__user_can_see_changeset
msgid "User Can See Changeset"
msgstr ""

View File

@@ -0,0 +1 @@
from . import ir_model

View File

@@ -0,0 +1,44 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import json
from odoo import api, fields, models, tools
from odoo.addons.base_sparse_field.models.fields import Serialized
class IrModel(models.Model):
_inherit = "ir.model"
rpc_config = Serialized(compute="_compute_rpc_config", default={})
# Allow editing via UI
rpc_config_edit = fields.Text(
help="Configure RPC config via JSON. "
"Value must be a list of methods to disable "
"wrapped by a dict with key `disable`. "
"Eg: {'disable': ['search', 'do_this']}"
"To disable all methods, use `{'disable: ['all']}`",
inverse="_inverse_rpc_config_edit",
)
@api.depends("rpc_config_edit")
def _compute_rpc_config(self):
for rec in self:
rec.rpc_config = rec._load_rpc_config()
def _inverse_rpc_config_edit(self):
for rec in self:
# Make sure options_edit is always readable
rec.rpc_config_edit = json.dumps(
rec.rpc_config or {}, indent=4, sort_keys=True
)
def _load_rpc_config(self):
return json.loads(self.rpc_config_edit or "{}")
@tools.ormcache("model")
def _get_rpc_config(self, model):
rec = self._get(model)
return rec.rpc_config or {}

View File

@@ -0,0 +1,30 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import odoo
from odoo.exceptions import UserError
from odoo.tools.translate import _
def protected__execute_cr(cr, uid, obj, method, *args, **kw):
# Same as original func in odoo.service.model.execute_cr
cr.reset()
recs = odoo.api.Environment(cr, uid, {}).get(obj)
if recs is None:
raise UserError(_("Object %s doesn't exist", obj))
# custom code starts here
if not _rpc_allowed(recs, method):
raise UserError(_("RPC call on %s is not allowed", obj))
return protected__execute_cr._orig__execute_cr(cr, uid, obj, method, *args, **kw)
def _rpc_allowed(recordset, method):
config = getattr(recordset, "_disable_rpc", None)
if config is None:
config = (
recordset.env["ir.model"]._get_rpc_config(recordset._name).get("disable")
)
if config is None:
return True
return "all" not in config and method not in config

View File

@@ -0,0 +1,23 @@
Enable debug mode and go to "Technical -> Database Structure -> Models".
Open the model that you like to configure and go to the tab "RPC config".
There you see a text field which supports JSON configuration.
The configuration is the same you can pass via decorator.
The only difference is that you have to wrap values in a dictionary
like `{"disable": [...values...]}`.
To disable all calls::
{
"disable": ["all"],
}
To disable only some methods::
{
"disable": ["create", "write", "another_method"],
}
NOTE: on the resulting JSON will be automatically formatted on save for better readability.

View File

@@ -0,0 +1 @@
* Simone Orsi <simone.orsi@camptocamp.com>

View File

@@ -0,0 +1 @@
Provide helpers to authorize RPC calls.

View File

@@ -0,0 +1,24 @@
Via code
~~~~~~~~
Decorate an Odoo model class like this::
from odoo.addons.rpc_helper.decorator import disable_rpc
@disable_rpc()
class AverageModel(models.Model):
_inherit = "avg.model"
This will disable ALL calls.
To selectively disable only some methods::
@disable_rpc("create", "write", "any_method")
class AverageModel(models.Model):
_inherit = "avg.model"
Via `ir.model` configuration
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
See "Configuration" section.

View File

@@ -0,0 +1,15 @@
"""Basic example script you can use to test your own models for real.
"""
from xmlrpc import client
HOST = "127.0.0.1"
PORT = 8069
DB_NAME = "odoodb"
url = "http://%s:%d/xmlrpc/2/" % (HOST, PORT)
xmlrpc_common = client.ServerProxy(url + "common")
xmlrpc_db = client.ServerProxy(url + "db")
xmlrpc_object = client.ServerProxy(url + "object")
uid = xmlrpc_common.login(DB_NAME, "admin", "admin")
res = xmlrpc_object.execute(DB_NAME, uid, "admin", "res.partner", "search", [])

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,476 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>Disable RPC</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="disable-rpc">
<h1 class="title">Disable RPC</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:3e95cc80fbae1cb569a46d799e19ecc6edf4f605f8d131a25bed6daf5057b512
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/server-tools/tree/16.0/rpc_helper"><img alt="OCA/server-tools" src="https://img.shields.io/badge/github-OCA%2Fserver--tools-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/server-tools-16-0/server-tools-16-0-rpc_helper"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/server-tools&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Provide helpers to authorize RPC calls.</p>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#configuration" id="toc-entry-1">Configuration</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a><ul>
<li><a class="reference internal" href="#via-code" id="toc-entry-3">Via code</a></li>
<li><a class="reference internal" href="#via-ir-model-configuration" id="toc-entry-4">Via <cite>ir.model</cite> configuration</a></li>
</ul>
</li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-5">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-6">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-7">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-8">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-9">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="configuration">
<h1><a class="toc-backref" href="#toc-entry-1">Configuration</a></h1>
<p>Enable debug mode and go to “Technical -&gt; Database Structure -&gt; Models”.</p>
<p>Open the model that you like to configure and go to the tab “RPC config”.</p>
<p>There you see a text field which supports JSON configuration.</p>
<p>The configuration is the same you can pass via decorator.
The only difference is that you have to wrap values in a dictionary
like <cite>{“disable”: […values…]}</cite>.</p>
<p>To disable all calls:</p>
<pre class="literal-block">
{
&quot;disable&quot;: [&quot;all&quot;],
}
</pre>
<p>To disable only some methods:</p>
<pre class="literal-block">
{
&quot;disable&quot;: [&quot;create&quot;, &quot;write&quot;, &quot;another_method&quot;],
}
</pre>
<p>NOTE: on the resulting JSON will be automatically formatted on save for better readability.</p>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
<div class="section" id="via-code">
<h2><a class="toc-backref" href="#toc-entry-3">Via code</a></h2>
<p>Decorate an Odoo model class like this:</p>
<pre class="literal-block">
from odoo.addons.rpc_helper.decorator import disable_rpc
&#64;disable_rpc()
class AverageModel(models.Model):
_inherit = &quot;avg.model&quot;
</pre>
<p>This will disable ALL calls.</p>
<p>To selectively disable only some methods:</p>
<pre class="literal-block">
&#64;disable_rpc(&quot;create&quot;, &quot;write&quot;, &quot;any_method&quot;)
class AverageModel(models.Model):
_inherit = &quot;avg.model&quot;
</pre>
</div>
<div class="section" id="via-ir-model-configuration">
<h2><a class="toc-backref" href="#toc-entry-4">Via <cite>ir.model</cite> configuration</a></h2>
<p>See “Configuration” section.</p>
</div>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-5">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/server-tools/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/server-tools/issues/new?body=module:%20rpc_helper%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#toc-entry-6">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-7">Authors</a></h2>
<ul class="simple">
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-8">Contributors</a></h2>
<ul class="simple">
<li>Simone Orsi &lt;<a class="reference external" href="mailto:simone.orsi&#64;camptocamp.com">simone.orsi&#64;camptocamp.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#toc-entry-9">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external image-reference" href="https://github.com/simahawk"><img alt="simahawk" src="https://github.com/simahawk.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/server-tools/tree/16.0/rpc_helper">OCA/server-tools</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,2 @@
from . import test_xmlrpc
from . import test_decorator

View File

@@ -0,0 +1,34 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import unittest
from ..decorator import disable_rpc
@disable_rpc()
class All:
pass
@disable_rpc("create")
class One:
pass
@disable_rpc("create", "write")
class Multi:
pass
class TestDecorator(unittest.TestCase):
def test_all(self):
self.assertEqual(All._disable_rpc, ("all",))
def test_one(self):
self.assertEqual(One._disable_rpc, ("create",))
def test_multi(self):
self.assertEqual(Multi._disable_rpc, ("create", "write"))

View File

@@ -0,0 +1,77 @@
# Copyright 2022 Camptocamp SA
# @author: Simone Orsi <simone.orsi@camptocamp.com>
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import json
import xmlrpc
from odoo.tests import common
@common.tagged("post_install", "-at_install")
class TestXMLRPC(common.HttpCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.admin_uid = cls.env.ref("base.user_admin").id
def _set_disable(self, val):
type(self.env["res.partner"])._disable_rpc = val
def _set_disable_on_model(self, val):
self.env["ir.model"]._get("res.partner").rpc_config_edit = json.dumps(
{"disable": val}
)
self.env.flush_all()
def tearDown(self):
klass = type(self.env["res.partner"])
if hasattr(klass, "_disable_rpc"):
delattr(klass, "_disable_rpc")
super().tearDown()
def _rpc_call(self, method, vals=None):
o = self.xmlrpc_object
db_name = common.get_db_name()
return o.execute(
db_name, self.admin_uid, "admin", "res.partner", method, vals or []
)
def test_xmlrpc_search_normal(self):
res = self._rpc_call("search")
self.assertTrue(isinstance(res, list))
def test_xmlrpc_all_blocked(self):
self._set_disable(("all",))
msg = "RPC call on res.partner is not allowed"
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("search")
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("create", vals=[{"name": "Foo"}])
def test_xmlrpc_can_search_create_blocked(self):
self._set_disable(("create",))
self._rpc_call("search")
msg = "RPC call on res.partner is not allowed"
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("create", vals=[{"name": "Foo"}])
def test_xmlrpc_all_blocked__ir_model(self):
self._set_disable_on_model(("all",))
msg = "RPC call on res.partner is not allowed"
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("search")
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("create", vals=[{"name": "Foo"}])
def test_xmlrpc_can_search_create_blocked__ir_model(self):
self._set_disable_on_model(("create",))
self._rpc_call("search")
msg = "RPC call on res.partner is not allowed"
with self.assertRaisesRegex(xmlrpc.client.Fault, msg):
self._rpc_call("create", vals=[{"name": "Foo"}])

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright 2022 Camptocamp SA
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html).
-->
<odoo>
<record id="view_model_form" model="ir.ui.view">
<field name="name">rpc_helper view_model_form</field>
<field name="model">ir.model</field>
<field name="inherit_id" ref="base.view_model_form" />
<field name="arch" type="xml">
<xpath expr="//notebook/page[@name='views']" position="after">
<page name="rpc_config" string="RPC config">
<field
name="rpc_config_edit"
groups="base.group_no_one"
widget="ace"
/>
</page>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,186 @@
.. image:: https://odoo-community.org/readme-banner-image
:target: https://odoo-community.org/get-involved?utm_source=readme
:alt: Odoo Community Association
==========
Web Notify
==========
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:20617e7f2a08e151432917686de12fb06a9644a8e086498ffb8b3bcf2d0d57b0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Production%2FStable-green.png
:target: https://odoo-community.org/page/development-status
:alt: Production/Stable
.. |badge2| image:: https://img.shields.io/badge/license-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-OCA%2Fweb-lightgray.png?logo=github
:target: https://github.com/OCA/web/tree/16.0/web_notify
:alt: OCA/web
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_notify
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/web&target_branch=16.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
Send instant notification messages to the user in live.
This technical module allows you to send instant notification messages from the server to the user in live.
Two kinds of notification are supported.
* Success: Displayed in a `success` theme color flying popup div
* Danger: Displayed in a `danger` theme color flying popup div
* Warning: Displayed in a `warning` theme color flying popup div
* Information: Displayed in a `info` theme color flying popup div
* Default: Displayed in a `default` theme color flying popup div
**Table of contents**
.. contents::
:local:
Installation
============
This module is based on the Instant Messaging Bus. To work properly, the server must be launched in gevent mode.
Usage
=====
To send a notification to the user you just need to call one of the new methods defined on res.users:
.. code-block:: python
self.env.user.notify_success(message='My success message')
or
.. code-block:: python
self.env.user.notify_danger(message='My danger message')
or
.. code-block:: python
self.env.user.notify_warning(message='My warning message')
or
.. code-block:: python
self.env.user.notify_info(message='My information message')
or
.. code-block:: python
self.env.user.notify_default(message='My default message')
You can also add sound to your notifications by using the sound parameter. The sound parameter expects a string containing the URL path to the audio file that should be played when the notification is displayed.
Example:
.. code-block:: python
self.env.user.notify_success(message='My success message', sound='/<YOUR_MODULE>/static/audio/success.mp3' )
or
.. code-block:: python
self.env.user.notify_info( message='My information message', sound='/<YOUR_MODULE>/static/audio/info.mp3' )
The sound parameter can be used with any notification type (success, danger, warning, info, or default). If the sound parameter is not provided, the notification will be displayed without any sound.
The notifications can bring interactivity with some buttons.
* One allowing to refresh the active view
* Another allowing to send a window / client action
The reload button is activated when sending the notification with:
The action can be used using the ``action`` keyword and we can choose which name to
give to our button with the ``button_name`` key in the action context `params` key:
.. code-block:: python
action = self.env["ir.actions.act_window"]._for_xml_id('sale.action_orders')
action.update({
'res_id': self.id,
'views': [(False, 'form')],
})
action["context"].setdefault("params", {})
action["context"]["params"]["button_name"] = "Sales"
action["context"]["params"]["button_icon"] = "fa-eye"
self.env.user.notify_info('My information message', action=action)
.. figure:: https://raw.githubusercontent.com/OCA/web/16.0/web_notify/static/img/notifications_screenshot.gif
:alt: Sample notifications
You can test the behaviour of the notifications by installing this module in a demo database.
Access the users form through Settings -> Users & Companies. You'll see a tab called "Test web notify", here you'll find two buttons that'll allow you test the module.
.. figure:: https://raw.githubusercontent.com/OCA/web/16.0/web_notify/static/img/test_notifications_demo.png
:alt: Sample notifications
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/web/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/OCA/web/issues/new?body=module:%20web_notify%0Aversion:%2016.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
~~~~~~~
* ACSONE SA/NV
* AdaptiveCity
Contributors
~~~~~~~~~~~~
* Laurent Mignon <laurent.mignon@acsone.eu>
* Serpent Consulting Services Pvt. Ltd.<jay.vora@serpentcs.com>
* Aitor Bouzas <aitor.bouzas@adaptivecity.com>
* Shepilov Vladislav <shepilov.v@protonmail.com>
* Kevin Khao <kevin.khao@akretion.com>
* `Tecnativa <https://www.tecnativa.com>`_:
* David Vidal
* Cetmix OÜ <https://cetmix.com/>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
This module is part of the `OCA/web <https://github.com/OCA/web/tree/16.0/web_notify>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,4 @@
# pylint: disable=missing-docstring
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
from . import models

View File

@@ -0,0 +1,23 @@
# pylint: disable=missing-docstring
# Copyright 2016 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
{
"name": "Web Notify",
"summary": """
Send notification messages to user""",
"version": "16.0.3.2.0",
"license": "LGPL-3",
"author": "ACSONE SA/NV," "AdaptiveCity," "Odoo Community Association (OCA)",
"development_status": "Production/Stable",
"website": "https://github.com/OCA/web",
"depends": ["web", "bus", "base", "mail"],
"assets": {
"web.assets_backend": [
"web_notify/static/src/**/*.js",
"web_notify/static/src/**/*.xml",
]
},
"demo": ["views/res_users_demo.xml"],
"installable": True,
}

View File

@@ -0,0 +1,129 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2018-09-02 05:11+0000\n"
"Last-Translator: Hans Henrik Gabelgaard <hhg@gabelgaard.org>\n"
"Language-Team: none\n"
"Language: da\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 3.1.1\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr ""
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr ""
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Advarsel"
#~ msgid "Users"
#~ msgstr "Brugere"

View File

@@ -0,0 +1,132 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
# Translators:
# Niki Waibel <niki.waibel@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-02-03 01:37+0000\n"
"PO-Revision-Date: 2025-09-18 11:42+0000\n"
"Last-Translator: Bastian Günther <mail@bastian-guenther.de>\n"
"Language-Team: German (https://www.transifex.com/oca/teams/23907/de/)\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.10.4\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Gefahr"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Standard"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Information"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "Name Gefahrenkanal Benachritigung"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "Name Standardkanal Benachritigung"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "Name Informationskanal Benachritigung"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "Name Erfolgskanal Benachrichtigung"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "Name Warnungskanal Benachritigung"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr "Öffnen"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "Es ist verboten eine Nachricht an einem anderen Benutzer zu senden."
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Erfolg"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "Test Gefahrenmeldung"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "Standardbenachrichtigung testen"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "Test Info Benachrichtigung"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "Test Erfolgsbenarichtigung"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "Test Warnungsbenachritigung"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "Test Webbenachrichtigung"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr "Benutzer"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Warnung"
#~ msgid "Users"
#~ msgstr "Benutzer"

View File

@@ -0,0 +1,132 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
# Translators:
# Pedro M. Baeza <pedro.baeza@gmail.com>, 2016
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2016-12-23 02:13+0000\n"
"PO-Revision-Date: 2023-09-02 20:35+0000\n"
"Last-Translator: Ivorra78 <informatica@totmaterial.es>\n"
"Language-Team: Spanish (https://www.transifex.com/oca/teams/23907/es/)\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Peligro"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Predeterminado"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Información"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "Notificar el nombre del canal de peligro"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "Notificar Nombre de Canal por Defecto"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "Notificar información Nombre del canal"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "Notificar con éxito Nombre del canal"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "Notificar advertencia Nombre del canal"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "Está prohibido enviar una notificación a otro usuario."
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Éxito"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "Notificación de peligro de prueba"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "Probar notificación predeterminada"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "Notificación de información de prueba"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "Notificación de éxito de la prueba"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "Notificación de advertencia de prueba"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "Notificación web de prueba"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr "Usuario"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Aviso"
#~ msgid "Users"
#~ msgstr "Usuarios"

View File

@@ -0,0 +1,137 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
# Translators:
# Bole <bole@dajmi5.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-04-28 18:03+0000\n"
"PO-Revision-Date: 2017-04-28 18:03+0000\n"
"Last-Translator: Bole <bole@dajmi5.com>, 2017\n"
"Language-Team: Croatian (https://www.transifex.com/oca/teams/23907/hr/)\n"
"Language: hr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Informacija"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
#, fuzzy
msgid "Notify Danger Channel Name"
msgstr "Naziv kanala upozorenja"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
#, fuzzy
msgid "Notify Default Channel Name"
msgstr "Naziv kanala informacija"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
#, fuzzy
msgid "Notify Info Channel Name"
msgstr "Naziv kanala informacija"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
#, fuzzy
msgid "Notify Success Channel Name"
msgstr "Naziv kanala informacija"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
#, fuzzy
msgid "Notify Warning Channel Name"
msgstr "Naziv kanala upozorenja"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr ""
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Upozorenje"
#~ msgid "Users"
#~ msgstr "Korisnici"

View File

@@ -0,0 +1,129 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 15.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2024-04-02 11:35+0000\n"
"Last-Translator: mymage <stefano.consolaro@mymage.it>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Pericolo"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Predefinito"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Informazione"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "Nome Canale Notifica Pericolo"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "Nome Canale Notifica Predefinito"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "Nome Canale Notifica Informazione"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "Nome Canale Notifica Successo"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "Nome Canale Notifica Avviso"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr "Apri"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "L'invio di una notifica ad un altro utente è vietato."
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Successo"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "Test notifica pericolo"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "Test notifica predefinito"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "Test notifica informazione"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "Test notifica successo"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "Test notifica avviso"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "Test notifica web"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr "Utente"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Attenzione"
#~ msgid "Users"
#~ msgstr "Utenti"

View File

@@ -0,0 +1,138 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2021-05-17 20:47+0000\n"
"Last-Translator: Bosd <c5e2fd43-d292-4c90-9d1f-74ff3436329a@anonaddy.me>\n"
"Language-Team: none\n"
"Language: nl\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.3.2\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Gevaar"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Standaard"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Informatie"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr ""
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Success"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr ""
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Waarschuwing"
#~ msgid "Users"
#~ msgstr "Gebruikers"
#~ msgid "Display Name"
#~ msgstr "Weergavenaam"
#~ msgid "ID"
#~ msgstr "ID"
#~ msgid "Last Modified on"
#~ msgstr "Laatst Gewijzigd op"

View File

@@ -0,0 +1,132 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
# Translators:
# Peter Hageman <hageman.p@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-07-13 16:07+0000\n"
"PO-Revision-Date: 2017-07-13 16:07+0000\n"
"Last-Translator: Peter Hageman <hageman.p@gmail.com>, 2017\n"
"Language-Team: Dutch (Netherlands) (https://www.transifex.com/oca/"
"teams/23907/nl_NL/)\n"
"Language: nl_NL\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Informatie"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr ""
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr ""
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Waarschuwing"
#~ msgid "Users"
#~ msgstr "Gebruikers"

View File

@@ -0,0 +1,134 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
# Translators:
# Rodrigo de Almeida Sottomaior Macedo <rmsolucoeseminformatic4@gmail.com>, 2017
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 10.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-06-22 08:27+0000\n"
"PO-Revision-Date: 2024-05-21 03:08+0000\n"
"Last-Translator: Rodrigo Macedo <sottomaiormacedotec@users.noreply."
"translation.odoo-community.org>\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/oca/teams/"
"23907/pt_BR/)\n"
"Language: pt_BR\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n > 1;\n"
"X-Generator: Weblate 4.17\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Perigo"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Padrão"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Informação"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "Notificar o nome do canal de perigo"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "Notificar o nome do canal padrão"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "Notificar o nome do canal de informações"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "Notificar o nome do canal de sucesso"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "Notificar o nome do canal de alerta"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr "Aberto"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "É proibido enviar uma notificação para outro usuário."
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Sucesso"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "Notificação de teste de perigo"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "Notificação de Teste padrão"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "Notificação de Teste informativo"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "Notificação de Teste de sucesso"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "Notificação de Teste de alerta"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "Notificação de Web Teste"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr "Usuário"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Alerta"
#~ msgid "Users"
#~ msgstr "Usuários"

View File

@@ -0,0 +1,126 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2025-02-20 10:06+0000\n"
"Last-Translator: İsmail Çağan Yılmaz <ismail.cagan.yilmaz@gmail.com>\n"
"Language-Team: none\n"
"Language: tr\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 5.6.2\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "Tehlike"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "Varsayılan"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "Bilgi"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "Tehlike Kanal Adını Bildir"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "Varsayılan Kanal Adını Bildir"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "Bilgi Kanal Adını Bildir"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "Başarı Kanal Adını Bildir"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "Uyarı Kanal Adını Bildir"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr "Açık"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "Başka bir kullanıcıya bildirim göndermek yasaktır."
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "Başarı"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "Tehlike bildirimini test edin"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "Varsayılan bildirimi test edin"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "Bilgi bildirimini test edin"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "Başarı bildirimini test edin"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "Uyarı bildirimini test edin"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "Web bildirimini test edin"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr "Kullanıcı"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "Uyarı"

View File

@@ -0,0 +1,123 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 16.0\n"
"Report-Msgid-Bugs-To: \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: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr ""
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr ""
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr ""
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr ""
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr ""

View File

@@ -0,0 +1,129 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * web_notify
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 12.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2019-09-01 12:52+0000\n"
"Last-Translator: 黎伟杰 <674416404@qq.com>\n"
"Language-Team: none\n"
"Language: zh_CN\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=1; plural=0;\n"
"X-Generator: Weblate 3.8\n"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Danger"
msgstr "危险"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Default"
msgstr "默认"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Information"
msgstr "信息"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_danger_channel_name
msgid "Notify Danger Channel Name"
msgstr "通知危险频道名称"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_default_channel_name
msgid "Notify Default Channel Name"
msgstr "通知默认频道名称"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_info_channel_name
msgid "Notify Info Channel Name"
msgstr "通知信息频道名称"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_success_channel_name
msgid "Notify Success Channel Name"
msgstr "通知成功频道名称"
#. module: web_notify
#: model:ir.model.fields,field_description:web_notify.field_res_users__notify_warning_channel_name
msgid "Notify Warning Channel Name"
msgstr "通知警告频道名称"
#. module: web_notify
#. odoo-javascript
#: code:addons/web_notify/static/src/js/services/notification_services.esm.js:0
#, python-format
msgid "Open"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Sending a notification to another user is forbidden."
msgstr "禁止向其他用户发送通知。"
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Success"
msgstr "成功"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test danger notification"
msgstr "测试危险通知"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test default notification"
msgstr "测试默认通知"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test info notification"
msgstr "测试信息通知"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test success notification"
msgstr "测试成功通知"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test warning notification"
msgstr "测试警告通知"
#. module: web_notify
#: model_terms:ir.ui.view,arch_db:web_notify.view_users_form_simple_modif_inherit
msgid "Test web notify"
msgstr "测试网站通知"
#. module: web_notify
#: model:ir.model,name:web_notify.model_res_users
msgid "User"
msgstr ""
#. module: web_notify
#. odoo-python
#: code:addons/web_notify/models/res_users.py:0
#, python-format
msgid "Warning"
msgstr "警告"
#~ msgid "Users"
#~ msgstr "用户"

View File

@@ -0,0 +1 @@
from . import res_users

View File

@@ -0,0 +1,152 @@
# Copyright 2016 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
from odoo import _, api, exceptions, fields, models
from odoo.addons.bus.models.bus import channel_with_db, json_dump
from odoo.addons.web.controllers.utils import clean_action
DEFAULT_MESSAGE = "Default message"
SUCCESS = "success"
DANGER = "danger"
WARNING = "warning"
INFO = "info"
DEFAULT = "default"
class ResUsers(models.Model):
_inherit = "res.users"
@api.depends("create_date")
def _compute_channel_names(self):
for record in self:
record.notify_success_channel_name = json_dump(
channel_with_db(self.env.cr.dbname, record.partner_id)
)
record.notify_danger_channel_name = json_dump(
channel_with_db(self.env.cr.dbname, record.partner_id)
)
record.notify_warning_channel_name = json_dump(
channel_with_db(self.env.cr.dbname, record.partner_id)
)
record.notify_info_channel_name = json_dump(
channel_with_db(self.env.cr.dbname, record.partner_id)
)
record.notify_default_channel_name = json_dump(
channel_with_db(self.env.cr.dbname, record.partner_id)
)
notify_success_channel_name = fields.Char(compute="_compute_channel_names")
notify_danger_channel_name = fields.Char(compute="_compute_channel_names")
notify_warning_channel_name = fields.Char(compute="_compute_channel_names")
notify_info_channel_name = fields.Char(compute="_compute_channel_names")
notify_default_channel_name = fields.Char(compute="_compute_channel_names")
def notify_success(
self,
message="Default message",
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
title = title or _("Success")
self._notify_channel(
SUCCESS, message, title, sticky, target, action, params, sound
)
def notify_danger(
self,
message="Default message",
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
title = title or _("Danger")
self._notify_channel(
DANGER, message, title, sticky, target, action, params, sound
)
def notify_warning(
self,
message="Default message",
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
title = title or _("Warning")
self._notify_channel(
WARNING, message, title, sticky, target, action, params, sound
)
def notify_info(
self,
message="Default message",
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
title = title or _("Information")
self._notify_channel(
INFO, message, title, sticky, target, action, params, sound
)
def notify_default(
self,
message="Default message",
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
title = title or _("Default")
self._notify_channel(
DEFAULT, message, title, sticky, target, action, params, sound
)
def _notify_channel(
self,
type_message=DEFAULT,
message=DEFAULT_MESSAGE,
title=None,
sticky=False,
target=None,
action=None,
params=None,
sound=None,
):
if not (self.env.user._is_admin() or self.env.su) and any(
user.id != self.env.uid for user in self
):
raise exceptions.UserError(
_("Sending a notification to another user is forbidden.")
)
if not target:
target = self.partner_id
if action:
action = clean_action(action, self.env)
bus_message = {
"type": type_message,
"message": message,
"title": title,
"sticky": sticky,
"action": action,
"params": dict(params or []),
"sound": sound,
}
notifications = [[partner, "web.notify", [bus_message]] for partner in target]
self.env["bus.bus"]._sendmany(notifications)

View File

@@ -0,0 +1,10 @@
* Laurent Mignon <laurent.mignon@acsone.eu>
* Serpent Consulting Services Pvt. Ltd.<jay.vora@serpentcs.com>
* Aitor Bouzas <aitor.bouzas@adaptivecity.com>
* Shepilov Vladislav <shepilov.v@protonmail.com>
* Kevin Khao <kevin.khao@akretion.com>
* `Tecnativa <https://www.tecnativa.com>`_:
* David Vidal
* Cetmix OÜ <https://cetmix.com/>

View File

@@ -0,0 +1,10 @@
Send instant notification messages to the user in live.
This technical module allows you to send instant notification messages from the server to the user in live.
Two kinds of notification are supported.
* Success: Displayed in a `success` theme color flying popup div
* Danger: Displayed in a `danger` theme color flying popup div
* Warning: Displayed in a `warning` theme color flying popup div
* Information: Displayed in a `info` theme color flying popup div
* Default: Displayed in a `default` theme color flying popup div

View File

@@ -0,0 +1 @@
This module is based on the Instant Messaging Bus. To work properly, the server must be launched in gevent mode.

View File

@@ -0,0 +1,78 @@
To send a notification to the user you just need to call one of the new methods defined on res.users:
.. code-block:: python
self.env.user.notify_success(message='My success message')
or
.. code-block:: python
self.env.user.notify_danger(message='My danger message')
or
.. code-block:: python
self.env.user.notify_warning(message='My warning message')
or
.. code-block:: python
self.env.user.notify_info(message='My information message')
or
.. code-block:: python
self.env.user.notify_default(message='My default message')
You can also add sound to your notifications by using the sound parameter. The sound parameter expects a string containing the URL path to the audio file that should be played when the notification is displayed.
Example:
.. code-block:: python
self.env.user.notify_success(message='My success message', sound='/<YOUR_MODULE>/static/audio/success.mp3' )
or
.. code-block:: python
self.env.user.notify_info( message='My information message', sound='/<YOUR_MODULE>/static/audio/info.mp3' )
The sound parameter can be used with any notification type (success, danger, warning, info, or default). If the sound parameter is not provided, the notification will be displayed without any sound.
The notifications can bring interactivity with some buttons.
* One allowing to refresh the active view
* Another allowing to send a window / client action
The reload button is activated when sending the notification with:
The action can be used using the ``action`` keyword and we can choose which name to
give to our button with the ``button_name`` key in the action context `params` key:
.. code-block:: python
action = self.env["ir.actions.act_window"]._for_xml_id('sale.action_orders')
action.update({
'res_id': self.id,
'views': [(False, 'form')],
})
action["context"].setdefault("params", {})
action["context"]["params"]["button_name"] = "Sales"
action["context"]["params"]["button_icon"] = "fa-eye"
self.env.user.notify_info('My information message', action=action)
.. figure:: ../static/img/notifications_screenshot.gif
:alt: Sample notifications
You can test the behaviour of the notifications by installing this module in a demo database.
Access the users form through Settings -> Users & Companies. You'll see a tab called "Test web notify", here you'll find two buttons that'll allow you test the module.
.. figure:: ../static/img/test_notifications_demo.png
:alt: Sample notifications

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.2 KiB

View File

@@ -0,0 +1,514 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils: https://docutils.sourceforge.io/" />
<title>README.rst</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
Despite the name, some widely supported CSS2 features are used.
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: gray; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic, pre.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document">
<a class="reference external image-reference" href="https://odoo-community.org/get-involved?utm_source=readme">
<img alt="Odoo Community Association" src="https://odoo-community.org/readme-banner-image" />
</a>
<div class="section" id="web-notify">
<h1>Web Notify</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:20617e7f2a08e151432917686de12fb06a9644a8e086498ffb8b3bcf2d0d57b0
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Production/Stable" src="https://img.shields.io/badge/maturity-Production%2FStable-green.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/license-LGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/web/tree/16.0/web_notify"><img alt="OCA/web" src="https://img.shields.io/badge/github-OCA%2Fweb-lightgray.png?logo=github" /></a> <a class="reference external image-reference" href="https://translation.odoo-community.org/projects/web-16-0/web-16-0-web_notify"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/web&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-Try%20me-875A7B.png" /></a></p>
<p>Send instant notification messages to the user in live.</p>
<p>This technical module allows you to send instant notification messages from the server to the user in live.
Two kinds of notification are supported.</p>
<ul class="simple">
<li>Success: Displayed in a <cite>success</cite> theme color flying popup div</li>
<li>Danger: Displayed in a <cite>danger</cite> theme color flying popup div</li>
<li>Warning: Displayed in a <cite>warning</cite> theme color flying popup div</li>
<li>Information: Displayed in a <cite>info</cite> theme color flying popup div</li>
<li>Default: Displayed in a <cite>default</cite> theme color flying popup div</li>
</ul>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#installation" id="toc-entry-1">Installation</a></li>
<li><a class="reference internal" href="#usage" id="toc-entry-2">Usage</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="installation">
<h2><a class="toc-backref" href="#toc-entry-1">Installation</a></h2>
<p>This module is based on the Instant Messaging Bus. To work properly, the server must be launched in gevent mode.</p>
</div>
<div class="section" id="usage">
<h2><a class="toc-backref" href="#toc-entry-2">Usage</a></h2>
<p>To send a notification to the user you just need to call one of the new methods defined on res.users:</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_success</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My success message'</span><span class="p">)</span>
</pre>
<p>or</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_danger</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My danger message'</span><span class="p">)</span>
</pre>
<p>or</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_warning</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My warning message'</span><span class="p">)</span>
</pre>
<p>or</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_info</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My information message'</span><span class="p">)</span>
</pre>
<p>or</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_default</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My default message'</span><span class="p">)</span>
</pre>
<p>You can also add sound to your notifications by using the sound parameter. The sound parameter expects a string containing the URL path to the audio file that should be played when the notification is displayed.</p>
<p>Example:</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_success</span><span class="p">(</span><span class="n">message</span><span class="o">=</span><span class="s1">'My success message'</span><span class="p">,</span> <span class="n">sound</span><span class="o">=</span><span class="s1">'/&lt;YOUR_MODULE&gt;/static/audio/success.mp3'</span> <span class="p">)</span>
</pre>
<p>or</p>
<pre class="code python literal-block">
<span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_info</span><span class="p">(</span> <span class="n">message</span><span class="o">=</span><span class="s1">'My information message'</span><span class="p">,</span> <span class="n">sound</span><span class="o">=</span><span class="s1">'/&lt;YOUR_MODULE&gt;/static/audio/info.mp3'</span> <span class="p">)</span>
</pre>
<p>The sound parameter can be used with any notification type (success, danger, warning, info, or default). If the sound parameter is not provided, the notification will be displayed without any sound.</p>
<p>The notifications can bring interactivity with some buttons.</p>
<ul class="simple">
<li>One allowing to refresh the active view</li>
<li>Another allowing to send a window / client action</li>
</ul>
<p>The reload button is activated when sending the notification with:</p>
<p>The action can be used using the <tt class="docutils literal">action</tt> keyword and we can choose which name to
give to our button with the <tt class="docutils literal">button_name</tt> key in the action context <cite>params</cite> key:</p>
<pre class="code python literal-block">
<span class="n">action</span> <span class="o">=</span> <span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="p">[</span><span class="s2">&quot;ir.actions.act_window&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">_for_xml_id</span><span class="p">(</span><span class="s1">'sale.action_orders'</span><span class="p">)</span><span class="w">
</span><span class="n">action</span><span class="o">.</span><span class="n">update</span><span class="p">({</span><span class="w">
</span> <span class="s1">'res_id'</span><span class="p">:</span> <span class="bp">self</span><span class="o">.</span><span class="n">id</span><span class="p">,</span><span class="w">
</span> <span class="s1">'views'</span><span class="p">:</span> <span class="p">[(</span><span class="kc">False</span><span class="p">,</span> <span class="s1">'form'</span><span class="p">)],</span><span class="w">
</span><span class="p">})</span><span class="w">
</span><span class="n">action</span><span class="p">[</span><span class="s2">&quot;context&quot;</span><span class="p">]</span><span class="o">.</span><span class="n">setdefault</span><span class="p">(</span><span class="s2">&quot;params&quot;</span><span class="p">,</span> <span class="p">{})</span><span class="w">
</span><span class="n">action</span><span class="p">[</span><span class="s2">&quot;context&quot;</span><span class="p">][</span><span class="s2">&quot;params&quot;</span><span class="p">][</span><span class="s2">&quot;button_name&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;Sales&quot;</span><span class="w">
</span><span class="n">action</span><span class="p">[</span><span class="s2">&quot;context&quot;</span><span class="p">][</span><span class="s2">&quot;params&quot;</span><span class="p">][</span><span class="s2">&quot;button_icon&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="s2">&quot;fa-eye&quot;</span><span class="w">
</span><span class="bp">self</span><span class="o">.</span><span class="n">env</span><span class="o">.</span><span class="n">user</span><span class="o">.</span><span class="n">notify_info</span><span class="p">(</span><span class="s1">'My information message'</span><span class="p">,</span> <span class="n">action</span><span class="o">=</span><span class="n">action</span><span class="p">)</span>
</pre>
<div class="figure">
<img alt="Sample notifications" src="https://raw.githubusercontent.com/OCA/web/16.0/web_notify/static/img/notifications_screenshot.gif" />
</div>
<p>You can test the behaviour of the notifications by installing this module in a demo database.
Access the users form through Settings -&gt; Users &amp; Companies. Youll see a tab called “Test web notify”, here youll find two buttons thatll allow you test the module.</p>
<div class="figure">
<img alt="Sample notifications" src="https://raw.githubusercontent.com/OCA/web/16.0/web_notify/static/img/test_notifications_demo.png" />
</div>
</div>
<div class="section" id="bug-tracker">
<h2><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h2>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/web/issues">GitHub Issues</a>.
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
<a class="reference external" href="https://github.com/OCA/web/issues/new?body=module:%20web_notify%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h2><a class="toc-backref" href="#toc-entry-4">Credits</a></h2>
<div class="section" id="authors">
<h3><a class="toc-backref" href="#toc-entry-5">Authors</a></h3>
<ul class="simple">
<li>ACSONE SA/NV</li>
<li>AdaptiveCity</li>
</ul>
</div>
<div class="section" id="contributors">
<h3><a class="toc-backref" href="#toc-entry-6">Contributors</a></h3>
<ul class="simple">
<li>Laurent Mignon &lt;<a class="reference external" href="mailto:laurent.mignon&#64;acsone.eu">laurent.mignon&#64;acsone.eu</a>&gt;</li>
<li>Serpent Consulting Services Pvt. Ltd.&lt;<a class="reference external" href="mailto:jay.vora&#64;serpentcs.com">jay.vora&#64;serpentcs.com</a>&gt;</li>
<li>Aitor Bouzas &lt;<a class="reference external" href="mailto:aitor.bouzas&#64;adaptivecity.com">aitor.bouzas&#64;adaptivecity.com</a>&gt;</li>
<li>Shepilov Vladislav &lt;<a class="reference external" href="mailto:shepilov.v&#64;protonmail.com">shepilov.v&#64;protonmail.com</a>&gt;</li>
<li>Kevin Khao &lt;<a class="reference external" href="mailto:kevin.khao&#64;akretion.com">kevin.khao&#64;akretion.com</a>&gt;</li>
<li><a class="reference external" href="https://www.tecnativa.com">Tecnativa</a>:<ul>
<li>David Vidal</li>
</ul>
</li>
<li>Cetmix OÜ &lt;<a class="reference external" href="https://cetmix.com/">https://cetmix.com/</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h3><a class="toc-backref" href="#toc-entry-7">Maintainers</a></h3>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org">
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
</a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/web/tree/16.0/web_notify">OCA/web</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</div>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

View File

@@ -0,0 +1,70 @@
/** @odoo-module alias=web_notify.AudioPlayer **/
import {Component, useState} from "@odoo/owl";
/**
* @typedef AudioPlayerProps
* @property {string} src URL of the audio file to be played
* @property {number} [volume=1.0] Volume level of the audio (from 0.0 to 1.0)
* @property {boolean} [loop=false] Whether the audio should loop
* @property {Function} [onEnded] Callback function to be called when the audio ends
*/
/**
* The AudioPlayer component is responsible for playing audio files with
* specified settings like volume and looping. It also provides the ability
* to trigger actions when the audio playback ends.
*/
export class AudioPlayer extends Component {
setup() {
this.state = useState({isPlaying: false});
this.audioElement = new Audio(this.props.src);
// Set audio properties
this.audioElement.volume = this.props.volume || 1.0;
this.audioElement.loop = this.props.loop || false;
// Start playing the audio
this.audioElement
.play()
.then(() => {
this.state.isPlaying = true;
})
.catch((error) => {
console.error("Audio playback failed:", error);
});
// Listen for the end of the audio playback
this.audioElement.addEventListener("ended", this.onAudioEnded.bind(this));
}
/**
* Stops the audio playback and triggers the onEnded callback if provided.
*/
stopAudio() {
this.audioElement.pause();
this.audioElement.currentTime = 0;
this.state.isPlaying = false;
if (this.props.onEnded) {
this.props.onEnded();
}
}
/**
* Handler for when the audio playback ends.
*/
onAudioEnded() {
if (!this.props.loop) {
this.stopAudio();
}
}
willUnmount() {
// Clean up the audio element and listeners
this.audioElement.removeEventListener("ended", this.onAudioEnded);
this.audioElement.pause();
}
}
AudioPlayer.template = "web_notify.AudioPlayer";

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!-- Copyright 2024 Cetmix OÜ
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<templates xml:space="preserve">
<t t-name="web_notify.AudioPlayer" owl="1">
<!-- No visual elements needed, audio is controlled programmatically -->
</t>
</templates>

View File

@@ -0,0 +1,12 @@
/** @odoo-module */
import {Notification} from "@web/core/notifications/notification";
import {patch} from "web.utils";
patch(Notification.props, "webNotifyProps", {
type: {
type: String,
optional: true,
validate: (t) =>
["warning", "danger", "success", "info", "default"].includes(t),
},
});

View File

@@ -0,0 +1,70 @@
/** @odoo-module **/
import {Markup} from "web.utils";
import {browser} from "@web/core/browser/browser";
import {registry} from "@web/core/registry";
export const webNotificationService = {
dependencies: ["bus_service", "action", "notification_sound"],
start(env, {bus_service, action, notification_sound}) {
let webNotifTimeouts = {};
/**
* Displays the web notification with sound on user's screen
* @param {*} notifications
*/
function displaywebNotification(notifications) {
Object.values(webNotifTimeouts).forEach((notif) =>
browser.clearTimeout(notif)
);
webNotifTimeouts = {};
notifications.forEach((notif) => {
browser.setTimeout(() => {
var buttons = [];
if (notif.action) {
const params =
(notif.action.context && notif.action.context.params) || {};
buttons = [
{
name: params.button_name || env._t("Open"),
primary: true,
onClick: async () => {
await action.doAction(notif.action);
},
...(params.button_icon && {icon: params.button_icon}),
},
];
}
const notificationRemove = notification_sound.add(
Markup(notif.message),
{
title: notif.title,
type: notif.type,
sticky: notif.sticky,
className: notif.className,
buttons: buttons.map((button) => {
const onClick = button.onClick;
button.onClick = async () => {
await onClick();
notificationRemove();
};
return button;
}),
sound: notif.sound,
}
);
});
});
}
bus_service.addEventListener("notification", ({detail: notifications}) => {
for (const {payload, type} of notifications) {
if (type === "web.notify") {
displaywebNotification(payload);
}
}
});
bus_service.start();
},
};
registry.category("services").add("webNotification", webNotificationService);

View File

@@ -0,0 +1,43 @@
/** @odoo-module **/
import {registry} from "@web/core/registry";
import {AudioPlayer} from "../components/audio_player.esm";
const effectRegistry = registry.category("effects");
// -----------------------------------------------------------------------------
// Audio effect
// -----------------------------------------------------------------------------
/**
* Handles effect of type "audio_effect". It returns the AudioPlayer component
* with the given audio source URL and other properties.
*
* @param {Object} env
* @param {Object} [params={}]
* @param {string} params.src
* The URL of the audio file to play.
* @param {number} [params.volume=1.0] Volume level of the audio (from 0.0 to 1.0)
* @param {boolean} [params.loop=false] Whether the audio should loop
* @param {Function} [params.onEnded] Callback function to be called when the audio ends
*/
function audioEffect(env, params = {}) {
if (!params.src) {
console.warn(
"Audio effect requires a 'src' parameter with the URL of the audio file."
);
return;
}
return {
Component: AudioPlayer,
props: {
src: params.src,
volume: params.volume || 1.0,
loop: params.loop || false,
onEnded: params.onEnded,
},
};
}
effectRegistry.add("audio_effect", audioEffect);

View File

@@ -0,0 +1,58 @@
/** @odoo-module **/
import {registry} from "@web/core/registry";
/**
* The notificationSoundService is responsible for handling the playback of audio
* notifications when a new notification is added. This service integrates with
* the notification system and the effect service to provide audible feedback
* based on the type of notification.
*
* Dependencies:
* - notification: The service responsible for displaying notifications on the UI.
* - effect: The service that handles visual and auditory effects in the application.
*/
export const notificationSoundService = {
dependencies: ["notification", "effect"],
/**
* Starts the notification sound service, enabling sound playback for notifications.
*
* @param {Object} env The environment object, providing access to various services.
* @param {Object} services An object containing the dependencies (notification, effect).
* @returns {Object} The add function, used to add notifications with sound.
*/
start(env, {notification, effect}) {
/**
* Adds a notification with an associated sound effect.
*
* @param {String} message The message to be displayed in the notification.
* @param {Object} [options={}] Additional options for the notification, such as type, sound and etc
* @returns {Function} A function to close the notification.
*/
function add(message, options = {}) {
const sound = options.sound || false;
delete options.sound; // Remove sound option from the options before passing to notification
const closeFn = notification.add(message, options);
if (sound)
// Trigger the audio effect.
effect.add({
type: "audio_effect",
src: sound,
volume: 0.8,
loop: false,
onEnded: () => {
// Placeholder for any action after sound ends
},
});
return closeFn;
}
return {add};
},
};
// Register the notification sound service in the service registry
registry.category("services").add("notification_sound", notificationSoundService);

View File

@@ -0,0 +1,3 @@
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl).
from . import test_res_users

View File

@@ -0,0 +1,124 @@
# Copyright 2016 ACSONE SA/NV
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
import json
from odoo import exceptions
from odoo.tests import common
from ..models.res_users import DANGER, DEFAULT, INFO, SUCCESS, WARNING
class TestResUsers(common.TransactionCase):
def test_notify_success(self):
bus_bus = self.env["bus.bus"]
domain = [("channel", "=", self.env.user.notify_success_channel_name)]
existing = bus_bus.search(domain)
test_msg = {
"message": "message",
"title": "title",
"sticky": True,
"action": None,
"params": {},
"sound": "/mail/static/src/audio/ting.mp3",
}
self.env.user.notify_success(**test_msg)
news = bus_bus.search(domain) - existing
self.assertEqual(1, len(news))
test_msg.update({"type": SUCCESS})
payload = json.loads(news.message)["payload"][0]
self.assertDictEqual(test_msg, payload)
def test_notify_danger(self):
bus_bus = self.env["bus.bus"]
domain = [("channel", "=", self.env.user.notify_danger_channel_name)]
existing = bus_bus.search(domain)
test_msg = {
"message": "message",
"title": "title",
"sticky": True,
"action": None,
"params": {},
"sound": "/mail/static/src/audio/ting.mp3",
}
self.env.user.notify_danger(**test_msg)
news = bus_bus.search(domain) - existing
self.assertEqual(1, len(news))
test_msg.update({"type": DANGER})
payload = json.loads(news.message)["payload"][0]
self.assertDictEqual(test_msg, payload)
def test_notify_warning(self):
bus_bus = self.env["bus.bus"]
domain = [("channel", "=", self.env.user.notify_warning_channel_name)]
existing = bus_bus.search(domain)
test_msg = {
"message": "message",
"title": "title",
"sticky": True,
"action": None,
"params": {},
"sound": "/mail/static/src/audio/ting.mp3",
}
self.env.user.notify_warning(**test_msg)
news = bus_bus.search(domain) - existing
self.assertEqual(1, len(news))
test_msg.update({"type": WARNING})
payload = json.loads(news.message)["payload"][0]
self.assertDictEqual(test_msg, payload)
def test_notify_info(self):
bus_bus = self.env["bus.bus"]
domain = [("channel", "=", self.env.user.notify_info_channel_name)]
existing = bus_bus.search(domain)
test_msg = {
"message": "message",
"title": "title",
"sticky": True,
"action": None,
"params": {},
"sound": "/mail/static/src/audio/ting.mp3",
}
self.env.user.notify_info(**test_msg)
news = bus_bus.search(domain) - existing
self.assertEqual(1, len(news))
test_msg.update({"type": INFO})
payload = json.loads(news.message)["payload"][0]
self.assertDictEqual(test_msg, payload)
def test_notify_default(self):
bus_bus = self.env["bus.bus"]
domain = [("channel", "=", self.env.user.notify_default_channel_name)]
existing = bus_bus.search(domain)
test_msg = {
"message": "message",
"title": "title",
"sticky": True,
"action": None,
"params": {},
"sound": "/mail/static/src/audio/ting.mp3",
}
self.env.user.notify_default(**test_msg)
news = bus_bus.search(domain) - existing
self.assertEqual(1, len(news))
test_msg.update({"type": DEFAULT})
payload = json.loads(news.message)["payload"][0]
self.assertDictEqual(test_msg, payload)
def test_notify_many(self):
# check that the notification of a list of users is done with
# a single call to the bus
users = self.env.user.search([(1, "=", 1)])
self.assertTrue(len(users) > 1)
self.env.user.notify_warning(message="message", target=users.partner_id)
def test_notify_other_user(self):
other_user = self.env.ref("base.user_demo")
other_user_model = self.env["res.users"].with_user(other_user)
with self.assertRaises(exceptions.UserError):
other_user_model.browse(self.env.uid).notify_info(message="hello")
def test_notify_admin_allowed_other_user(self):
other_user = self.env.ref("base.user_demo")
other_user.notify_info(message="hello")

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2018 Aitor Bouzas <aitor.bouzas@adaptivecity.com>
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo>
<record id="view_users_form_simple_modif_inherit" model="ir.ui.view">
<field name="name">res.users.form.simple.view.modif.inherit</field>
<field name="model">res.users</field>
<field name="inherit_id" ref="base.view_users_form" />
<field name="arch" type="xml">
<xpath expr="//notebook/page[1]" position="after">
<page string="Test web notify" name="test_web_notify">
<group>
<group>
<button
name="notify_success"
type="object"
string="Test success notification"
class="oe_highlight"
/>
</group>
<group>
<button
name="notify_danger"
type="object"
string="Test danger notification"
class="oe_highlight"
/>
</group>
</group>
<group>
<group>
<button
name="notify_warning"
type="object"
string="Test warning notification"
class="oe_highlight"
/>
</group>
<group>
<button
name="notify_info"
type="object"
string="Test info notification"
class="oe_highlight"
/>
</group>
</group>
<group>
<group>
<button
name="notify_default"
type="object"
string="Test default notification"
class="oe_highlight"
/>
</group>
</group>
</page>
</xpath>
</field>
</record>
</odoo>