diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.spec.ts index d08e2a52073..ca7f26895ea 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.spec.ts @@ -1251,6 +1251,14 @@ describe('DotEmaShellComponent', () => { expect(currentUrl).toMatch(/^\//); }); + + it('should use page hostname when clientHost is not present', () => { + const seoParams = spectator.component['$seoParams'](); + + expect(seoParams.requestHostName).toBe( + `${window.location.protocol}//${MOCK_RESPONSE_HEADLESS.site.hostname}` + ); + }); }); describe('$errorDisplay computed property', () => { diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.ts index 0099305c730..9bd258719b9 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/dot-ema-shell/dot-ema-shell.component.ts @@ -164,7 +164,10 @@ export class DotEmaShellComponent implements OnInit, OnDestroy { protected readonly $seoParams = computed(() => { const url = sanitizeURL(this.uveStore.pageAsset()?.page?.pageURI); const currentUrl = url.startsWith('/') ? url : '/' + url; - const requestHostName = getRequestHostName(this.uveStore.pageParams()); + const requestHostName = getRequestHostName( + this.uveStore.pageParams(), + this.uveStore.pageAsset()?.site?.hostname + ); return { siteId: this.uveStore.pageAsset()?.site?.identifier, diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts index 836bb87a0dc..aa7c50f1493 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/index.ts @@ -794,8 +794,21 @@ export const mapContainerStructureToArrayOfContainers = (containers: DotCMSPageA * @param {DotPageApiParams} params * @return {*} {string} */ -export const getRequestHostName = (params: DotPageApiParams) => { - return params?.clientHost || window.location.origin; +export const getRequestHostName = (params: DotPageApiParams, pageHostname?: string) => { + if (params?.clientHost) { + return params.clientHost; + } + + if (pageHostname) { + try { + return new URL(pageHostname).origin; + } catch { + // Hostname can be provided without scheme (e.g. "siteb.example.com") + return `${window.location.protocol}//${pageHostname}`; + } + } + + return window.location.origin; }; /** diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts index 77bf274f8ce..e326061a2b9 100644 --- a/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts +++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/utils/utils.spec.ts @@ -26,6 +26,7 @@ import { createFullURL, getDragItemData, createReorderMenuURL, + getRequestHostName, getOrientation, normalizeQueryParams, convertUTCToLocalTime, @@ -1295,6 +1296,69 @@ describe('utils functions', () => { }); }); + describe('getRequestHostName', () => { + it('should return clientHost when it is provided', () => { + expect( + getRequestHostName({ + url: 'test', + language_id: '1', + [PERSONA_KEY]: DEFAULT_PERSONA.keyTag, + clientHost: 'https://headless.example.com' + }) + ).toBe('https://headless.example.com'); + }); + + it('should build the host from page hostname when clientHost is missing', () => { + expect( + getRequestHostName( + { + url: 'test', + language_id: '1', + [PERSONA_KEY]: DEFAULT_PERSONA.keyTag + }, + 'siteb.example.com' + ) + ).toBe(`${window.location.protocol}//siteb.example.com`); + }); + + it('should extract origin when page hostname is a full URL', () => { + expect( + getRequestHostName( + { + url: 'test', + language_id: '1', + [PERSONA_KEY]: DEFAULT_PERSONA.keyTag + }, + 'https://siteb.example.com/path' + ) + ).toBe('https://siteb.example.com'); + }); + + it('should prioritize clientHost over page hostname', () => { + expect( + getRequestHostName( + { + url: 'test', + language_id: '1', + [PERSONA_KEY]: DEFAULT_PERSONA.keyTag, + clientHost: 'https://headless.example.com' + }, + 'siteb.example.com' + ) + ).toBe('https://headless.example.com'); + }); + + it('should fallback to window origin when neither clientHost nor page hostname is provided', () => { + expect( + getRequestHostName({ + url: 'test', + language_id: '1', + [PERSONA_KEY]: DEFAULT_PERSONA.keyTag + }) + ).toBe(window.location.origin); + }); + }); + describe('getDragItemData', () => { it('should return correct data for content-type', () => { const dataset = {