diff --git a/.docker_files/main/__manifest__.py b/.docker_files/main/__manifest__.py
index 082f168e..97e729ba 100644
--- a/.docker_files/main/__manifest__.py
+++ b/.docker_files/main/__manifest__.py
@@ -16,6 +16,7 @@
'auditlog',
'base_extended_security',
+ 'base_xml_rename',
'disable_install_from_website',
'ir_attachment_access_token_portal',
'ir_attachment_name_autocomplete',
@@ -24,12 +25,10 @@
'mail_message_from_author',
'mail_notification_no_footer',
'mail_recipient_unchecked',
- 'menu_item_rename',
'note_no_default_stage',
'private_data_group',
'queue_job_auto_requeue',
'super_calendar',
- 'user_group_rename',
'web_email_field_new_tab',
],
'installable': True,
diff --git a/Dockerfile b/Dockerfile
index 939d56cf..471ba9c7 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -17,6 +17,7 @@ USER odoo
COPY auditlog /mnt/extra-addons/auditlog
COPY base_extended_security /mnt/extra-addons/base_extended_security
+COPY base_xml_rename /mnt/extra-addons/base_xml_rename
COPY disable_install_from_website /mnt/extra-addons/disable_install_from_website
COPY ir_attachment_access_token_portal /mnt/extra-addons/ir_attachment_access_token_portal
COPY ir_attachment_name_autocomplete /mnt/extra-addons/ir_attachment_name_autocomplete
@@ -25,13 +26,11 @@ COPY mail_follower_picker /mnt/extra-addons/mail_follower_picker
COPY mail_message_from_author /mnt/extra-addons/mail_message_from_author
COPY mail_notification_no_footer /mnt/extra-addons/mail_notification_no_footer
COPY mail_recipient_unchecked /mnt/extra-addons/mail_recipient_unchecked
-COPY menu_item_rename /mnt/extra-addons/menu_item_rename
COPY note_no_default_stage /mnt/extra-addons/note_no_default_stage
COPY private_data_group /mnt/extra-addons/private_data_group
COPY queue_job_auto_requeue /mnt/extra-addons/queue_job_auto_requeue
COPY super_calendar /mnt/extra-addons/super_calendar
COPY test_http_request /mnt/extra-addons/test_http_request
-COPY user_group_rename /mnt/extra-addons/user_group_rename
COPY web_email_field_new_tab /mnt/extra-addons/web_email_field_new_tab
COPY .docker_files/main /mnt/extra-addons/main
diff --git a/base_xml_rename/README.rst b/base_xml_rename/README.rst
new file mode 100644
index 00000000..81e5f7d1
--- /dev/null
+++ b/base_xml_rename/README.rst
@@ -0,0 +1,91 @@
+Base XML Rename
+===============
+This module allows to rename UI elements using XML data files in modules.
+
+.. contents:: Table of Contents
+
+Context
+-------
+Renaming a menu in Odoo directly through the web interface is not a good idea.
+
+* We rapidly loose track of what was modified.
+* The menu names are not changed in test environments (except with replication of the prod).
+* The changes are lost if someone forces the override of translations.
+
+The same applies for renaming other objects such as user groups.
+
+Module Design
+-------------
+A mixin ``xml.rename.mixin`` is added.
+
+This mixin has a method ``rename`` which takes 3 mandatory parameters:
+
+* ``ref``: the xml reference of the record.
+* ``lang``: the language of the term.
+* ``value``: the new term.
+
+Optionaly, a ``field`` parameter can be supplied, in case the field is not ``name``.
+
+The mixin can be added to any model.
+
+For now, the module adds the mixin to ir.ui.menu and res.groups.
+
+Usage
+-----
+Here are 2 examples for renaming menu items and user groups.
+
+Renaming a Menu
+~~~~~~~~~~~~~~~
+Inside an xml file:
+
+* Add a `function` node with model="ir.ui.menu".
+* Inside the `function` node, you must set 3 `value` nodes.
+
+ These nodes must have type="char" and respectively contain the following data:
+
+ 1. The XML ID of the menu item
+ 2. The languge code
+ 3. The new label to set
+
+Here is an example to rename the `Settings` menu to `Administration`.
+
+.. code-block:: XML
+
+
+ base.menu_administration
+ en_US
+ Administration
+
+
+Here is the result after loading the module containg the XML file.
+
+.. image:: static/description/admin_menu.png
+
+Renaming a User Group
+~~~~~~~~~~~~~~~~~~~~~
+Here is an example of renaming a user group. This works the same way as renaming a menu item.
+
+.. code-block:: XML
+
+
+ account.group_account_user
+ fr_FR
+ Comptable
+
+
+Before loading the XML, the group is named ``Montrer les fonctions de comptabilité complète`` in french.
+
+.. image:: static/description/group_before.png
+
+After loading the XML, the group is named ``Comptable``.
+
+.. image:: static/description/group_after.png
+
+Known Issues
+~~~~~~~~~~~~
+If you reload translations with ``Overwrite Existing Terms`` checked, the terms loaded with XML
+will not be reloaded automatically. You will need to update the modules containing these XML files).
+
+Contributors
+------------
+* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
diff --git a/menu_item_rename/__init__.py b/base_xml_rename/__init__.py
similarity index 100%
rename from menu_item_rename/__init__.py
rename to base_xml_rename/__init__.py
diff --git a/menu_item_rename/__manifest__.py b/base_xml_rename/__manifest__.py
similarity index 100%
rename from menu_item_rename/__manifest__.py
rename to base_xml_rename/__manifest__.py
diff --git a/user_group_rename/__init__.py b/base_xml_rename/models/__init__.py
similarity index 65%
rename from user_group_rename/__init__.py
rename to base_xml_rename/models/__init__.py
index 6da4fa4e..edcf12ef 100644
--- a/user_group_rename/__init__.py
+++ b/base_xml_rename/models/__init__.py
@@ -1,4 +1,8 @@
# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
-from . import models
+from . import (
+ ir_ui_menu,
+ res_groups,
+ xml_rename_mixin,
+)
diff --git a/base_xml_rename/models/ir_ui_menu.py b/base_xml_rename/models/ir_ui_menu.py
new file mode 100644
index 00000000..28d02a7e
--- /dev/null
+++ b/base_xml_rename/models/ir_ui_menu.py
@@ -0,0 +1,23 @@
+# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from odoo import api, models
+from .xml_rename_mixin import is_lang_installed
+
+
+class IrUiMenu(models.Model):
+
+ _inherit = ('ir.ui.menu', 'xml.rename.mixin')
+ _name = 'ir.ui.menu'
+
+ @api.model
+ def rename(self, ref, lang, value, field='name'):
+ super().rename(ref, lang, value, field)
+
+ menu = self.env.ref(ref)
+ should_update_action_name = (
+ is_lang_installed(self.env, lang) and
+ menu.action and field == 'name'
+ )
+ if should_update_action_name:
+ menu.action.with_context(lang=lang).name = value
diff --git a/user_group_rename/tests/__init__.py b/base_xml_rename/models/res_groups.py
similarity index 50%
rename from user_group_rename/tests/__init__.py
rename to base_xml_rename/models/res_groups.py
index 6ef2df91..54f65bd8 100644
--- a/user_group_rename/tests/__init__.py
+++ b/base_xml_rename/models/res_groups.py
@@ -1,2 +1,10 @@
# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+from odoo import models
+
+
+class ResGroups(models.Model):
+
+ _inherit = ('res.groups', 'xml.rename.mixin')
+ _name = 'res.groups'
diff --git a/base_xml_rename/models/xml_rename_mixin.py b/base_xml_rename/models/xml_rename_mixin.py
new file mode 100644
index 00000000..706b5565
--- /dev/null
+++ b/base_xml_rename/models/xml_rename_mixin.py
@@ -0,0 +1,50 @@
+# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
+# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
+
+import logging
+from odoo import api, models
+from odoo.exceptions import ValidationError
+
+
+_logger = logging.getLogger(__name__)
+
+
+def is_lang_installed(env: 'Environment', lang: str):
+ return lang in dict(env['res.lang'].get_installed())
+
+
+class XMLRenameMixin(models.AbstractModel):
+
+ _name = 'xml.rename.mixin'
+ _description = 'Mixin For Renaming Records Through XML'
+
+ @api.model
+ def rename(self, ref, lang, value, field='name'):
+ """Rename the record to rename.
+
+ :param ref: the XML ID of the record to rename
+ :param lang: the language of the term
+ :param value: the new name for the record
+ """
+ if not is_lang_installed(self.env, lang):
+ _logger.debug(
+ 'Skip renaming the record {ref} for the language {lang}. '
+ 'The language is not installed.'
+ .format(ref=ref, lang=lang)
+ )
+ return
+
+ _logger.info(
+ 'Renaming the record {ref} with the label `{value}` '
+ 'for the language {lang}.'
+ .format(ref=ref, lang=lang, value=value)
+ )
+ record = self.env.ref(ref)
+ if record._name != self._name:
+ raise ValidationError(
+ 'The XML ID {ref} does not reference a record of model {model}. '
+ 'It references a record of model {record_model}'
+ .format(ref=ref, model=self._name, record_model=record._name)
+ )
+
+ record.with_context(lang=lang).write({field: value})
diff --git a/menu_item_rename/static/description/admin_menu.png b/base_xml_rename/static/description/admin_menu.png
similarity index 100%
rename from menu_item_rename/static/description/admin_menu.png
rename to base_xml_rename/static/description/admin_menu.png
diff --git a/user_group_rename/static/description/group_after.png b/base_xml_rename/static/description/group_after.png
similarity index 100%
rename from user_group_rename/static/description/group_after.png
rename to base_xml_rename/static/description/group_after.png
diff --git a/user_group_rename/static/description/group_before.png b/base_xml_rename/static/description/group_before.png
similarity index 100%
rename from user_group_rename/static/description/group_before.png
rename to base_xml_rename/static/description/group_before.png
diff --git a/menu_item_rename/static/description/icon.png b/base_xml_rename/static/description/icon.png
similarity index 100%
rename from menu_item_rename/static/description/icon.png
rename to base_xml_rename/static/description/icon.png
diff --git a/menu_item_rename/tests/__init__.py b/base_xml_rename/tests/__init__.py
similarity index 100%
rename from menu_item_rename/tests/__init__.py
rename to base_xml_rename/tests/__init__.py
diff --git a/menu_item_rename/tests/test_menu_item_rename.py b/base_xml_rename/tests/test_ir_ui_menu.py
similarity index 93%
rename from menu_item_rename/tests/test_menu_item_rename.py
rename to base_xml_rename/tests/test_ir_ui_menu.py
index 2cc8a8b8..3882e20a 100644
--- a/menu_item_rename/tests/test_menu_item_rename.py
+++ b/base_xml_rename/tests/test_ir_ui_menu.py
@@ -63,3 +63,8 @@ def test_after_rename_menu_with_en__action_fr_translation_unchanged(self):
new_label = 'New label'
self._rename_menu('en_US', new_label)
assert self.action.with_context(lang='fr_FR').name == self.menu_name_fr
+
+ def test_if_lang_not_active__term_ignored(self):
+ self.fr_lang.active = False
+ self._rename_menu('fr_FR', 'Nouveau libellé')
+ assert self.menu.name == self.menu_name_en
diff --git a/user_group_rename/tests/test_group_rename.py b/base_xml_rename/tests/test_res_groups.py
similarity index 89%
rename from user_group_rename/tests/test_group_rename.py
rename to base_xml_rename/tests/test_res_groups.py
index 2857db83..cf0dee66 100644
--- a/user_group_rename/tests/test_group_rename.py
+++ b/base_xml_rename/tests/test_res_groups.py
@@ -41,3 +41,8 @@ def test_after_rename_group_with_en__fr_translation_unchanged(self):
new_label = 'New label'
self._rename_group('en_US', new_label)
assert self.group.with_context(lang='fr_FR').name == self.group_name_fr
+
+ def test_if_lang_not_active__term_ignored(self):
+ self.fr_lang.active = False
+ self._rename_group('fr_FR', 'Nouveau libellé')
+ assert self.group.name == self.group_name_en
diff --git a/menu_item_rename/README.rst b/menu_item_rename/README.rst
deleted file mode 100644
index 6c2c21bd..00000000
--- a/menu_item_rename/README.rst
+++ /dev/null
@@ -1,42 +0,0 @@
-Menu Item Rename
-================
-This module allows to rename a menu item using XML data files in modules.
-
-Context
--------
-Renaming a menu in Odoo directly through the web interface is not a good idea.
-
-* We rapidly loose track of what was modified.
-* The menu names are not changed in test environments (except with replication of the prod).
-* The changes are lost if someone forces the override of translations.
-
-Usage
------
-Inside an xml file:
-
-* Add a `function` node with model="ir.ui.menu".
-* Inside the `function` node, you must set 3 `value` nodes.
-
- These nodes must have type="char" and respectively contain the following data:
-
- 1. The XML ID of the menu item
- 2. The languge code
- 3. The new label to set
-
-Here is an example to rename the `Settings` menu to `Administration`.
-
-.. code-block:: XML
-
-
- base.menu_administration
- en_US
- Administration
-
-
-Here is the result after loading the module containg the XML file.
-
-.. image:: static/description/admin_menu.png
-
-Contributors
-------------
-* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
diff --git a/menu_item_rename/models.py b/menu_item_rename/models.py
deleted file mode 100644
index 25623202..00000000
--- a/menu_item_rename/models.py
+++ /dev/null
@@ -1,39 +0,0 @@
-# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
-# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
-
-import logging
-from odoo import api, models
-from odoo.exceptions import ValidationError
-
-
-_logger = logging.getLogger(__name__)
-
-
-class MenuItem(models.Model):
-
- _inherit = 'ir.ui.menu'
-
- @api.model
- def rename(self, menu_ref, lang, value):
- """Rename the menu item.
-
- :param menu_ref: the XML ID of the menu item
- :param lang: the language of the menu entry
- :param value: the new name for the menu entry
- """
- _logger.info(
- 'Renaming menu item {menu_ref} with the label `{value}` '
- 'for the language {lang}.'
- .format(menu_ref=menu_ref, lang=lang, value=value)
- )
- menu = self.env.ref(menu_ref)
- if menu._name != 'ir.ui.menu':
- raise ValidationError(
- 'The XML ID {} does not reference a menu item.'
- .format(menu_ref)
- )
-
- menu.with_context(lang=lang).name = value
-
- if menu.action:
- menu.action.with_context(lang=lang).name = value
diff --git a/user_group_rename/README.rst b/user_group_rename/README.rst
deleted file mode 100644
index 6d878106..00000000
--- a/user_group_rename/README.rst
+++ /dev/null
@@ -1,29 +0,0 @@
-User Group Rename
-=================
-This module allows to rename a user group using XML data files in modules.
-
-This module is the same as ``menu_item_rename`` but for user groups instead of menu items.
-
-Usage
------
-Here is an example of usage in an XML file.
-
-.. code-block:: XML
-
-
- account.group_account_user
- fr_FR
- Comptable
-
-
-Before loading the XML, the group is named ``Montrer les fonctions de comptabilité complète`` in french.
-
-.. image:: static/description/group_before.png
-
-After loading the XML, the group is named ``Comptable``.
-
-.. image:: static/description/group_after.png
-
-Contributors
-------------
-* Numigi (tm) and all its contributors (https://bit.ly/numigiens)
diff --git a/user_group_rename/__manifest__.py b/user_group_rename/__manifest__.py
deleted file mode 100644
index fd21de24..00000000
--- a/user_group_rename/__manifest__.py
+++ /dev/null
@@ -1,14 +0,0 @@
-# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
-# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
-
-{
- 'name': 'User Group Rename',
- 'version': '1.0.0',
- 'author': 'Numigi',
- 'maintainer': 'Numigi',
- 'license': 'LGPL-3',
- 'category': 'Other',
- 'summary': 'Rename user groups using module xml.',
- 'depends': ['base'],
- 'installable': True,
-}
diff --git a/user_group_rename/models.py b/user_group_rename/models.py
deleted file mode 100644
index 01e036b1..00000000
--- a/user_group_rename/models.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# © 2019 Numigi (tm) and all its contributors (https://bit.ly/numigiens)
-# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
-
-import logging
-from odoo import api, models
-from odoo.exceptions import ValidationError
-
-
-_logger = logging.getLogger(__name__)
-
-
-class ResGroups(models.Model):
-
- _inherit = 'res.groups'
-
- @api.model
- def rename(self, group_ref, lang, value):
- """Rename the user group.
-
- :param group_ref: the XML ID of the group
- :param lang: the language of the group
- :param value: the new name for the group
- """
- _logger.info(
- 'Renaming the user group {group_ref} with the label `{value}` '
- 'for the language {lang}.'
- .format(group_ref=group_ref, lang=lang, value=value)
- )
- group = self.env.ref(group_ref)
- if group._name != 'res.groups':
- raise ValidationError(
- 'The XML ID {} does not reference a group.'
- .format(group_ref)
- )
-
- group.with_context(lang=lang).name = value
diff --git a/user_group_rename/static/description/admin_menu.png b/user_group_rename/static/description/admin_menu.png
deleted file mode 100644
index c25c4831..00000000
Binary files a/user_group_rename/static/description/admin_menu.png and /dev/null differ
diff --git a/user_group_rename/static/description/icon.png b/user_group_rename/static/description/icon.png
deleted file mode 100644
index 92a86b10..00000000
Binary files a/user_group_rename/static/description/icon.png and /dev/null differ