From 7f171211f619d442220ffd347c43476e90c21d44 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 16 Apr 2026 13:01:24 +0000 Subject: [PATCH 1/7] feat: integrate Translation Platform as Odoo 18 OWL client action - Add OWL client action component (TranslationPlatform) registered as 'translation_platform' in the actions registry - Add DAO modules (letter_dao, translator_dao, settings_dao) using useService('orm') instead of XML-RPC - Add reusable OWL components: loader, modal, signal-problem, child-modal, content-editor, letter-viewer, languages-pick-modal, translator-button - Add page components: home, letters, letter-edit, translators - Add ir.actions.client record + menu item for translator users - Remove website dependency; fix translation_url computation - Remove share=True from group_user to enable backend access - Set Translation Platform as home action for translator users on create - Add migration script to set home action for existing translators - Redirect legacy /translation-platform route to /odoo/translation-platform" Agent-Logs-Url: https://github.com/CompassionCH/compassion-modules/sessions/e2c2193b-7ccb-4cc2-924e-721f15cf8a53 Co-authored-by: ecino <8435180+ecino@users.noreply.github.com> --- sbc_translation/__manifest__.py | 25 +- sbc_translation/controllers/main.py | 18 +- .../migrations/18.0.1.1.0/post_migrate.py | 50 ++++ sbc_translation/models/correspondence.py | 14 +- sbc_translation/models/translation_user.py | 20 +- sbc_translation/security/ir_groups.xml | 1 - .../static/src/components/tp_child_modal.js | 60 ++++ .../src/components/tp_content_editor.js | 116 ++++++++ .../src/components/tp_languages_pick_modal.js | 206 +++++++++++++ .../static/src/components/tp_letter_viewer.js | 230 +++++++++++++++ .../static/src/components/tp_loader.js | 38 +++ .../static/src/components/tp_modal.js | 110 +++++++ .../src/components/tp_signal_problem.js | 93 ++++++ .../src/components/tp_translator_button.js | 82 +++++ .../static/src/models/letter_dao.js | 224 ++++++++++++++ .../static/src/models/settings_dao.js | 49 +++ .../static/src/models/translator_dao.js | 150 ++++++++++ sbc_translation/static/src/pages/tp_home.js | 272 +++++++++++++++++ .../static/src/pages/tp_letter_edit.js | 227 ++++++++++++++ .../static/src/pages/tp_letters.js | 279 ++++++++++++++++++ .../static/src/pages/tp_translators.js | 234 +++++++++++++++ .../static/src/translation_platform.css | 87 ++++++ .../static/src/translation_platform.js | 188 ++++++++++++ .../views/translation_platform_action.xml | 26 ++ 24 files changed, 2769 insertions(+), 30 deletions(-) create mode 100644 sbc_translation/migrations/18.0.1.1.0/post_migrate.py create mode 100644 sbc_translation/static/src/components/tp_child_modal.js create mode 100644 sbc_translation/static/src/components/tp_content_editor.js create mode 100644 sbc_translation/static/src/components/tp_languages_pick_modal.js create mode 100644 sbc_translation/static/src/components/tp_letter_viewer.js create mode 100644 sbc_translation/static/src/components/tp_loader.js create mode 100644 sbc_translation/static/src/components/tp_modal.js create mode 100644 sbc_translation/static/src/components/tp_signal_problem.js create mode 100644 sbc_translation/static/src/components/tp_translator_button.js create mode 100644 sbc_translation/static/src/models/letter_dao.js create mode 100644 sbc_translation/static/src/models/settings_dao.js create mode 100644 sbc_translation/static/src/models/translator_dao.js create mode 100644 sbc_translation/static/src/pages/tp_home.js create mode 100644 sbc_translation/static/src/pages/tp_letter_edit.js create mode 100644 sbc_translation/static/src/pages/tp_letters.js create mode 100644 sbc_translation/static/src/pages/tp_translators.js create mode 100644 sbc_translation/static/src/translation_platform.css create mode 100644 sbc_translation/static/src/translation_platform.js create mode 100644 sbc_translation/views/translation_platform_action.xml diff --git a/sbc_translation/__manifest__.py b/sbc_translation/__manifest__.py index a359e3cd3..87b94c4cd 100644 --- a/sbc_translation/__manifest__.py +++ b/sbc_translation/__manifest__.py @@ -35,9 +35,28 @@ "author": "Compassion CH", "license": "AGPL-3", "website": "https://github.com/CompassionCH/compassion-modules", - "depends": ["sbc_compassion", "partner_contact_birthdate", "website"], + "depends": ["sbc_compassion", "partner_contact_birthdate"], "assets": { - "web.assets_backend": ["sbc_translation/js/translation_letter_counting_js.js"] + "web.assets_backend": [ + "sbc_translation/static/src/translation_platform.css", + "sbc_translation/static/src/models/letter_dao.js", + "sbc_translation/static/src/models/translator_dao.js", + "sbc_translation/static/src/models/settings_dao.js", + "sbc_translation/static/src/components/tp_loader.js", + "sbc_translation/static/src/components/tp_modal.js", + "sbc_translation/static/src/components/tp_signal_problem.js", + "sbc_translation/static/src/components/tp_child_modal.js", + "sbc_translation/static/src/components/tp_translator_button.js", + "sbc_translation/static/src/components/tp_content_editor.js", + "sbc_translation/static/src/components/tp_letter_viewer.js", + "sbc_translation/static/src/components/tp_languages_pick_modal.js", + "sbc_translation/static/src/pages/tp_letters.js", + "sbc_translation/static/src/pages/tp_letter_edit.js", + "sbc_translation/static/src/pages/tp_home.js", + "sbc_translation/static/src/pages/tp_translators.js", + "sbc_translation/static/src/translation_platform.js", + "sbc_translation/js/translation_letter_counting_js.js", + ] }, "data": [ "security/ir_groups.xml", @@ -48,8 +67,8 @@ "wizards/translation_letter_counting_view.xml", "data/mail_template.xml", "data/update_translation_priority_cron.xml", - "data/website.xml", "data/queue_job.xml", + "views/translation_platform_action.xml", "views/translation_user_view.xml", "views/correspondence_view.xml", "views/translation_pool_view.xml", diff --git a/sbc_translation/controllers/main.py b/sbc_translation/controllers/main.py index 3c75ab861..7ffcef837 100644 --- a/sbc_translation/controllers/main.py +++ b/sbc_translation/controllers/main.py @@ -11,7 +11,6 @@ from werkzeug.utils import redirect from odoo import http -from odoo.tools import file_open _logger = logging.getLogger(__name__) @@ -20,18 +19,11 @@ class RestController(http.Controller): @http.route( ["/translation-platform", "/translation-platform/"], type="http", - auth="public", + auth="user", ) - def translation_platform(self, page=""): + def translation_platform(self, page="", **kwargs): """ - Simple server for the translation platform which should be compiled into - /static/tp folder. - :param page: This the route requested - :return: index.html, or assets. + Legacy route: redirect old standalone-app URLs to the new Odoo 18 backend + client action at /odoo/translation-platform. """ - if ( - "assets" in page or page.endswith(".png") or page.endswith(".jpg") - ): # Serving assets - return redirect(f"/sbc_translation/static/tp/{page}") - with file_open("sbc_translation/static/tp/index.html") as app: - return app.read() + return redirect("/odoo/translation-platform", 301) diff --git a/sbc_translation/migrations/18.0.1.1.0/post_migrate.py b/sbc_translation/migrations/18.0.1.1.0/post_migrate.py new file mode 100644 index 000000000..1ea0d6fd2 --- /dev/null +++ b/sbc_translation/migrations/18.0.1.1.0/post_migrate.py @@ -0,0 +1,50 @@ +############################################################################## +# +# Copyright (C) 2024 Compassion CH (http://www.compassion.ch) +# Releasing children from poverty in Jesus' name +# +# The licence is in the file __manifest__.py +# +############################################################################## +import logging + +from odoo import SUPERUSER_ID, api + +_logger = logging.getLogger(__name__) + + +def migrate(cr, version): + """ + Set the Translation Platform as the home action for all existing active + translator (group_user) accounts, and promote them from share/portal users + to internal users if needed (since group_user.share is now False). + """ + if not version: + return + + _logger.info( + "sbc_translation migration 18.0.1.1.0: " + "Setting home action for existing translator users" + ) + + with api.Environment.manage(): + env = api.Environment(cr, SUPERUSER_ID, {}) + + tp_action = env.ref("sbc_translation.action_translation_platform", raise_if_not_found=False) + group_user = env.ref("sbc_translation.group_user", raise_if_not_found=False) + + if not tp_action or not group_user: + _logger.warning( + "Cannot find translation_platform action or group_user – skipping migration" + ) + return + + # Find all active translators and set home action + translators = env["translation.user"].search([("active", "=", True)]) + users = translators.mapped("user_id") + + if users: + users.write({"action_id": tp_action.id}) + _logger.info( + "Updated home action for %d translator users", len(users) + ) diff --git a/sbc_translation/models/correspondence.py b/sbc_translation/models/correspondence.py index ab7597abd..91d45017b 100644 --- a/sbc_translation/models/correspondence.py +++ b/sbc_translation/models/correspondence.py @@ -122,10 +122,11 @@ def _compute_translation_priority_name(self): ) def _compute_translation_url(self): - host = self.env.ref("sbc_translation.translation_website").sudo().domain + base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url", "") + base_url = base_url.rstrip("/") for letter in self: letter.translation_url = ( - f"{host}/translation-platform/letters/letter-edit/{letter.id}" + f"{base_url}/odoo/translation-platform?letterId={letter.id}" ) def _compute_paragraph_ids(self): @@ -637,13 +638,8 @@ def list_letters(self): def get_letter_info(self): """Translation Platform API for fetching letter data.""" self.ensure_one() - base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url") - if base_url: - base_url = base_url.rstrip("/") + "/" - else: - base_url = ( - f"https://{self.env.ref('sbc_translation.translation_website').domain}/" - ) + base_url = self.env["ir.config_parameter"].sudo().get_param("web.base.url", "") + base_url = base_url.rstrip("/") + "/" # Gives access to related objects child = self.child_id.sudo() partner = self.partner_id.sudo() diff --git a/sbc_translation/models/translation_user.py b/sbc_translation/models/translation_user.py index ac8c4a36f..1362db06d 100644 --- a/sbc_translation/models/translation_user.py +++ b/sbc_translation/models/translation_user.py @@ -81,33 +81,45 @@ def _compute_nb_translated_letters_last_year(self): @api.model_create_multi def create(self, vals_list): """ - When creating a translator, put him the rights for using the platform. + When creating a translator, put him the rights for using the platform + and set the Translation Platform as their home action. """ records = super().create(vals_list) user_group = self.env.ref("sbc_translation.group_user") + tp_action = self.env.ref("sbc_translation.action_translation_platform") for translator in records: translator.user_id.write( - {"groups_id": [(4, user_group.id)], "translator_id": translator.id} + { + "groups_id": [(4, user_group.id)], + "translator_id": translator.id, + "action_id": tp_action.id, + } ) return records def write(self, vals): """ When activating/deactivating a translator, update rights accordingly. + When deactivating, remove the home action override. """ super().write(vals) if "active" in vals: user_group = self.env.ref("sbc_translation.group_user") action = 4 if vals["active"] else 3 # Add or remove group self.mapped("user_id").write({"groups_id": [(action, user_group.id)]}) + if not vals["active"]: + # Remove the translation platform home action + self.mapped("user_id").write({"action_id": False}) return True def unlink(self): """ - Remove Translation Platform rights when removing translator. + Remove Translation Platform rights and home action when removing translator. """ user_group = self.env.ref("sbc_translation.group_user") - self.mapped("user_id").write({"groups_id": [(3, user_group.id)]}) + self.mapped("user_id").write( + {"groups_id": [(3, user_group.id)], "action_id": False} + ) return super().unlink() def open_translated_letters(self): diff --git a/sbc_translation/security/ir_groups.xml b/sbc_translation/security/ir_groups.xml index 9cfd3b554..4fcce44fe 100644 --- a/sbc_translation/security/ir_groups.xml +++ b/sbc_translation/security/ir_groups.xml @@ -9,7 +9,6 @@ User the user will have access to the translation platform. - Manager diff --git a/sbc_translation/static/src/components/tp_child_modal.js b/sbc_translation/static/src/components/tp_child_modal.js new file mode 100644 index 000000000..68090ce1c --- /dev/null +++ b/sbc_translation/static/src/components/tp_child_modal.js @@ -0,0 +1,60 @@ +/** @odoo-module */ + +import { Component, xml } from "@odoo/owl"; +import { TpModal } from "./tp_modal"; + +/** + * Child protection policy modal. + * Props: + * active {Boolean} + * onClose {Function} + */ +export class TpChildModal extends Component { + static template = xml` + +
+
Expected/acceptable behaviors:
+
    +
  • I will demonstrate the proper respect and dignity of all children and will demonstrate Jesus's love and care for them, regardless of their gender, age, race, religion, social background, culture, special need or disability.
  • +
  • I will maintain appropriate and reasonable expectations for children based on their age and ability level.
  • +
  • I will engage in age-appropriate communication with beneficiaries.
  • +
  • I will submit to the appropriate background or police checks as permissible by law prior to face-to-face contact with beneficiaries.
  • +
  • I will engage in activities with beneficiaries only in open or visible places, and in the event that an activity needs to take place in an enclosed space, I will ensure that at least one other approved adult is present.
  • +
  • If I witness child abuse, know a child is in danger, observe any concerning behaviors from colleagues, partners or other representatives, or a child comes to me with a report of abuse, I will take it seriously and report it to the proper staff or relevant authorities.
  • +
  • I will keep all information about child protection investigations confidential, keeping in mind privacy and dignity concerns of all involved.
  • +
  • I will contribute to building an environment where children are respected and encouraged to discuss their concerns and rights.
  • +
  • I will follow Compassion's rules about communication with beneficiaries, including social media interaction.
  • +
+
Unacceptable behaviors:
+
    +
  • I will not solicit a romantic/dating relationship and will never engage in sexual/sexually suggestive behavior with any beneficiary, regardless of age.
  • +
  • I will never engage in sexual/sexually suggestive behavior with any child under age 18, regardless of the legal age of consent in-country.
  • +
  • I will never use language that is verbally/emotionally abusive, sexually suggestive, degrading, humiliating, shaming or is otherwise culturally inappropriate with a beneficiary.
  • +
  • I will not touch beneficiaries in an inappropriate or culturally insensitive way.
  • +
  • I will never use any kind of physical discipline or physical punishment as a method of correction for beneficiaries.
  • +
  • I will never travel alone with a beneficiary, without an approved representative or prior approval, except in a life-threatening emergency.
  • +
  • I will not hire any child in any harmful form of child labor and follow local laws regarding child employment.
  • +
  • I will not gather, disclose or support the disclosure of information about beneficiaries or their families without prior, express permission.
  • +
+
+

Child Protection Videos

+ +
+
+
+ `; + + static components = { TpModal }; + + static props = { + active: { type: Boolean }, + onClose: { type: Function }, + }; +} + +export default TpChildModal; diff --git a/sbc_translation/static/src/components/tp_content_editor.js b/sbc_translation/static/src/components/tp_content_editor.js new file mode 100644 index 000000000..9b4168858 --- /dev/null +++ b/sbc_translation/static/src/components/tp_content_editor.js @@ -0,0 +1,116 @@ +/** @odoo-module */ + +import { Component, xml, useState } from "@odoo/owl"; +import { _t } from "@web/core/l10n/translation"; +import { TpModal } from "./tp_modal"; + +/** + * Tips modal showing translation best practices. + * Props: + * active {Boolean} + * onClose {Function} + */ +class TpTipsModal extends Component { + static template = xml` + +
+
    +
  • Keep the same tone and emotional register as the source text.
  • +
  • Do not paraphrase – translate as closely as possible.
  • +
  • Preserve paragraph breaks from the source.
  • +
  • If the source text is unclear, add a comment explaining the issue.
  • +
  • Names of people and places should be kept as they appear.
  • +
  • If you're unsure about a phrase, use the comment field to flag it.
  • +
+
+
+ `; + static components = { TpModal }; + static props = { + active: { type: Boolean }, + onClose: { type: Function }, + }; +} + +/** + * Content editor for translating individual paragraphs. + * Props: + * letter {Object} - letter data with translatedElements array + */ +export class TpContentEditor extends Component { + static template = xml` +
+
+ +
+ — Page Break — +
+ +
+
+
Translated Content
+