Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit a997e3d

Browse files
stevejalimCopilot
andauthored
fix: repair Smartling CAT visual context (#17179)
* fix: repair Smartling CAT visual context Porting over fixups from Smartling to make Smartling's CAT tool work again. * Ensure we don't have a DisallowedHost error during the generation of the HTML artifact, else we get no auto-matched strings in Smartling, which looks like no context was provided at all * Ensure that the context references assets on the prod CDN, so they are not blocked by GCP IAP Jira: https://mozilla-hub.atlassian.net/browse/WT-1184 * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <[email protected]> --------- Co-authored-by: Copilot Autofix powered by AI <[email protected]>
1 parent 5c3feab commit a997e3d

2 files changed

Lines changed: 86 additions & 7 deletions

File tree

bedrock/cms/tests/test_callbacks.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
from unittest import mock
66

7+
from django.test import override_settings
8+
79
import pytest
810
import wagtail_factories
911
from wagtail.models import Page
@@ -15,6 +17,7 @@
1517

1618

1719
@pytest.mark.django_db
20+
@override_settings(WAGTAILADMIN_BASE_URL="https://cms.example.com")
1821
def test_visual_context__for_page(client):
1922
top_level_page = SimpleRichTextPageFactory()
2023

@@ -38,9 +41,41 @@ def test_visual_context__for_page(client):
3841
# light checks because we're not testing wagtaildraftsharing itself
3942
assert "<body" in html
4043
assert "Test SimpleRichTextPage" in html
44+
assert '<base href="https://www.mozilla.org/">' in html
4145

4246
sharing_link_key = WagtaildraftsharingLink.objects.get().key
43-
assert url == f"http://testserver:81/_internal_draft_preview/{sharing_link_key}/"
47+
assert url == f"https://cms.example.com/_internal_draft_preview/{sharing_link_key}/"
48+
49+
50+
@pytest.mark.django_db
51+
@override_settings(WAGTAILADMIN_BASE_URL="https://cms.example.com")
52+
@mock.patch("bedrock.cms.wagtail_localize_smartling.callbacks._get_html_for_sharing_link")
53+
def test_visual_context__cms_hostname_stripped_and_base_tag_injected(mock_get_html, client):
54+
top_level_page = SimpleRichTextPageFactory()
55+
page = SimpleRichTextPageFactory(parent=top_level_page, slug="visual-context-text-page")
56+
page.save_revision()
57+
58+
site = wagtail_factories.SiteFactory(
59+
root_page=top_level_page,
60+
is_default_site=True,
61+
hostname="cms-internal.example.com",
62+
port=8080,
63+
)
64+
cms_root_url = site.root_url # e.g. "http://cms-internal.example.com:8080"
65+
66+
mock_get_html.return_value = f'<html><head></head><body><img src="{cms_root_url}/media/image.jpg"></body></html>'
67+
68+
user = WagtailUserFactory()
69+
mock_job = mock.Mock()
70+
mock_job.translation_source.get_source_instance.return_value = page
71+
mock_job.user = user
72+
73+
_, html = visual_context(smartling_job=mock_job)
74+
75+
assert cms_root_url not in html
76+
assert "https://cms.example.com" not in html
77+
assert '<base href="https://www.mozilla.org/">' in html
78+
assert '<img src="/media/image.jpg">' in html
4479

4580

4681
def test_visual_context__for_inviable_object(client):
@@ -88,6 +123,27 @@ def test_visual_context__for_page__with_no_revision(mock_capture_message, client
88123
mock_capture_message.assert_called_once_with(f"Unable to get a latest_revision for {page} so unable to send visual context.")
89124

90125

126+
@override_settings(WAGTAILADMIN_BASE_URL="https://cms.example.com")
127+
@mock.patch("bedrock.cms.wagtail_localize_smartling.callbacks.SharingLinkView.as_view")
128+
@mock.patch("bedrock.cms.wagtail_localize_smartling.callbacks.RequestFactory")
129+
def test__get_html_for_sharing_link__uses_cms_hostname_and_path_only(mock_factory_cls, mock_as_view):
130+
# sharing_link.url is a full URL — we should extract just the path and use
131+
# the CMS hostname as SERVER_NAME so make_preview_request doesn't fall back
132+
# to 'localhost' / 'testserver' and return a 400 error page.
133+
mock_response = mock.Mock()
134+
mock_response.text = "<html><body>page content</body></html>"
135+
mock_as_view.return_value = mock.Mock(return_value=mock_response)
136+
137+
sharing_link = mock.Mock()
138+
sharing_link.url = "http://localhost/_internal_draft_preview/abc123/"
139+
140+
result = _get_html_for_sharing_link(sharing_link)
141+
142+
mock_factory_cls.assert_called_once_with(SERVER_NAME="cms.example.com")
143+
mock_factory_cls.return_value.get.assert_called_once_with("/_internal_draft_preview/abc123/")
144+
assert result == "<html><body>page content</body></html>"
145+
146+
91147
# The happy path is implicitly tested in test_visual_context__*, above
92148
@mock.patch("bedrock.cms.wagtail_localize_smartling.callbacks.capture_exception")
93149
@mock.patch("bedrock.cms.wagtail_localize_smartling.callbacks.SharingLinkView.as_view")

bedrock/cms/wagtail_localize_smartling/callbacks.py

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,14 @@
88
#
99
# The README.md of that repo covers what inputs are
1010
# provided and what outputs are needed
11-
11+
import logging
1212
from typing import TYPE_CHECKING
13+
from urllib.parse import urlparse
1314

15+
from django.conf import settings
1416
from django.test import RequestFactory
1517

16-
from wagtail.models import Page
18+
from wagtail.models import Page, Site
1719
from wagtail_localize_smartling.exceptions import IncapableVisualContextCallback
1820

1921
if TYPE_CHECKING:
@@ -22,9 +24,18 @@
2224
from wagtaildraftsharing.models import WagtaildraftsharingLink
2325
from wagtaildraftsharing.views import SharingLinkView
2426

27+
logger = logging.getLogger(__name__)
28+
2529

2630
def _get_html_for_sharing_link(sharing_link: WagtaildraftsharingLink) -> str:
27-
request = RequestFactory().get(sharing_link.url)
31+
# Use the CMS hostname (guaranteed to be in ALLOWED_HOSTS) so that
32+
# Wagtail's make_preview_request doesn't fall back to 'testserver' /
33+
# 'localhost', which would cause DisallowedHost inside the middleware
34+
# chain and return a 400 error page instead of the real page HTML.
35+
# Also extract just the path from sharing_link.url in case it's absolute.
36+
cms_hostname = urlparse(settings.WAGTAILADMIN_BASE_URL).hostname
37+
path = urlparse(sharing_link.url).path
38+
request = RequestFactory(SERVER_NAME=cms_hostname).get(path)
2839
view_func = SharingLinkView.as_view()
2940
try:
3041
resp = view_func(
@@ -38,8 +49,10 @@ def _get_html_for_sharing_link(sharing_link: WagtaildraftsharingLink) -> str:
3849
raise IncapableVisualContextCallback("Was not able to get a HTML export from the sharing link")
3950

4051

41-
def _get_full_url_for_sharing_link(sharing_link: WagtaildraftsharingLink, page: "Page") -> str:
42-
return f"{page.get_site().root_url}{sharing_link.url}"
52+
def _get_full_url_for_sharing_link(sharing_link: WagtaildraftsharingLink) -> str:
53+
url = f"{settings.WAGTAILADMIN_BASE_URL}{sharing_link.url}"
54+
logger.debug("Page URL being sent to Smartling: %s", url)
55+
return url
4356

4457

4558
def visual_context(smartling_job: "Job") -> tuple[str, str]:
@@ -73,6 +86,16 @@ def visual_context(smartling_job: "Job") -> tuple[str, str]:
7386
max_ttl=-1, # -1 signifies "No expiry". If we pass None we get the default TTL
7487
)
7588

76-
url = _get_full_url_for_sharing_link(sharing_link=sharing_link, page=content_obj)
89+
url = _get_full_url_for_sharing_link(sharing_link=sharing_link)
7790
html = _get_html_for_sharing_link(sharing_link=sharing_link)
91+
92+
# Strip the CMS hostname from URLs in the HTML so they become relative,
93+
# then inject a <base> tag so Smartling resolves them against the public
94+
# domain rather than the IAP-protected CMS host.
95+
default_site = Site.objects.filter(is_default_site=True).first()
96+
if default_site:
97+
html = html.replace(default_site.root_url, "")
98+
base_tag = f'<base href="{settings.CANONICAL_URL}/">'
99+
html = html.replace("<head>", f"<head>{base_tag}", 1)
100+
78101
return (url, html)

0 commit comments

Comments
 (0)