Skip to content

Commit

Permalink
fix(UVE): UrlContentMap Redirection to Correct Page After Save or Pub…
Browse files Browse the repository at this point in the history
…lish #29896 (#30334)

### Proposed Changes
* Refactored the logic to handle URL redirection after saving a
UrlContentMap.
* Added methods to extract and determine the target URL for redirection,
ensuring the correct URL is used when publishing content.

### Checklist
- [x] Tests
- [x] Translations
- [x] Security Implications Contemplated (add notes if applicable)

### Additional Info
This fix addresses the issue where, after saving or publishing a
UrlContentMap, the system incorrectly redirects to the content type’s
detail page instead of the expected URL. The changes ensure that the
correct URL is detected and used, preventing unwanted redirection after
the save or publish action.

### Screenshots
#### Before:


https://github.com/user-attachments/assets/1b4f74fa-f0b7-46f4-b389-a1bc63f52f7b

#### After:


https://github.com/user-attachments/assets/84ea8aa1-a434-4c17-a4b3-b4f391a58d14
  • Loading branch information
valentinogiardino authored Oct 15, 2024
1 parent 33f27b8 commit 03c82c1
Show file tree
Hide file tree
Showing 5 changed files with 163 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,11 @@ import { DotActionUrlService } from '../services/dot-action-url/dot-action-url.s
import { DotPageApiService } from '../services/dot-page-api.service';
import { DEFAULT_PERSONA, WINDOW } from '../shared/consts';
import { FormStatus, NG_CUSTOM_EVENTS } from '../shared/enums';
import { PAGE_RESPONSE_BY_LANGUAGE_ID, PAYLOAD_MOCK } from '../shared/mocks';
import {
PAGE_RESPONSE_BY_LANGUAGE_ID,
PAGE_RESPONSE_URL_CONTENT_MAP,
PAYLOAD_MOCK
} from '../shared/mocks';
import { UVEStore } from '../store/dot-uve.store';

describe('DotEmaShellComponent', () => {
Expand Down Expand Up @@ -961,7 +965,7 @@ describe('DotEmaShellComponent', () => {

expect(navigate).toHaveBeenCalledWith([], {
queryParams: {
url: 'my-awesome-page'
url: '/my-awesome-page'
},
queryParamsHandling: 'merge'
});
Expand Down Expand Up @@ -1000,6 +1004,40 @@ describe('DotEmaShellComponent', () => {

expect(reloadSpy).toHaveBeenCalled();
});

it('should trigger a store reload if the URL from urlContentMap is the same as the current URL', () => {
const reloadSpy = jest.spyOn(store, 'reload');
// Mocking the uveStore to return a urlContentMap with the same URL as the current one
jest.spyOn(store, 'pageAPIResponse').mockReturnValue(PAGE_RESPONSE_URL_CONTENT_MAP);
jest.spyOn(store, 'params').mockReturnValue({
url: '/test-url',
language_id: '1',
'com.dotmarketing.persona.id': '1'
});

spectator.detectChanges();

spectator.triggerEventHandler(DotEmaDialogComponent, 'action', {
event: new CustomEvent('ng-event', {
detail: {
name: NG_CUSTOM_EVENTS.SAVE_PAGE,
payload: {
htmlPageReferer: '/test-url'
}
}
}),
payload: PAYLOAD_MOCK,
form: {
status: FormStatus.SAVED,
isTranslation: false
}
});

spectator.detectChanges();

expect(reloadSpy).toHaveBeenCalled();
expect(router.navigate).not.toHaveBeenCalled();
});
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { WINDOW } from '../shared/consts';
import { FormStatus, NG_CUSTOM_EVENTS } from '../shared/enums';
import { DialogAction, DotPage } from '../shared/models';
import { UVEStore } from '../store/dot-uve.store';
import { compareUrlPaths } from '../utils';

@Component({
selector: 'dot-ema-shell',
Expand Down Expand Up @@ -166,23 +167,68 @@ export class DotEmaShellComponent implements OnInit, OnDestroy {
}

case NG_CUSTOM_EVENTS.SAVE_PAGE: {
// Maybe this can be out of the switch but we should evaluate it if it's needed
// This can be undefined
const url = event.detail.payload?.htmlPageReferer?.split('?')[0].replace('/', '');

if (url && this.uveStore.params().url !== url) {
this.navigate({
url
});
this.handleSavePageEvent(event);
break;
}
}
}

return;
}
/**
* Handles the save page event triggered from the dialog.
*
* @param {CustomEvent} event - The event object containing details about the save action.
* @return {void}
*/
private handleSavePageEvent(event: CustomEvent): void {
const url = this.extractPageRefererUrl(event);
const targetUrl = this.getTargetUrl(url);

this.uveStore.reload();
if (this.shouldNavigate(targetUrl)) {
// Navigate to the new URL if it's different from the current one
this.navigate({ url: targetUrl });

break;
}
return;
}

this.uveStore.reload();
}
/**
* Extracts the htmlPageReferer url from the event payload.
*
* @param {CustomEvent} event - The event object containing the payload with the URL.
* @return {string | undefined} - The extracted URL or undefined if not found.
*/
private extractPageRefererUrl(event: CustomEvent): string | undefined {
return event.detail.payload?.htmlPageReferer;
}

/**
* Determines the target URL for navigation.
*
* If `urlContentMap` is present and contains a `URL_MAP_FOR_CONTENT`, it will be used.
* Otherwise, it falls back to the URL extracted from the event.
*
* @param {string | undefined} url - The URL extracted from the event.
* @returns {string | undefined} - The final target URL for navigation, or undefined if none.
*/
private getTargetUrl(url: string | undefined): string | undefined {
const urlContentMap = this.uveStore.pageAPIResponse().urlContentMap;

// Return URL from content map or fallback to the provided URL
return urlContentMap?.URL_MAP_FOR_CONTENT || url;
}

/**
* Determines whether navigation to a new URL is necessary.
*
* @param {string | undefined} targetUrl - The target URL for navigation.
* @returns {boolean} - True if the current URL differs from the target URL and navigation is required.
*/
private shouldNavigate(targetUrl: string | undefined): boolean {
const currentUrl = this.uveStore.params().url;

// Navigate if the target URL is defined and different from the current URL
return targetUrl !== undefined && !compareUrlPaths(targetUrl, currentUrl);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ import { DEFAULT_PERSONA } from '../../../shared/consts';
import {
HEADLESS_BASE_QUERY_PARAMS,
MOCK_RESPONSE_HEADLESS,
MOCK_RESPONSE_VTL,
PAGE_RESPONSE_BY_LANGUAGE_ID,
PAGE_RESPONSE_URL_CONTENT_MAP,
URL_CONTENT_MAP_MOCK
} from '../../../shared/mocks';
import { UVEStore } from '../../../store/dot-uve.store';
Expand Down Expand Up @@ -179,7 +181,9 @@ describe('EditEmaToolbarComponent', () => {
}),
setDevice: jest.fn(),
setSocialMedia: jest.fn(),
params: signal(params)
params: signal(params),
pageAPIResponse: signal(MOCK_RESPONSE_VTL),
reload: jest.fn()
})
]
});
Expand Down Expand Up @@ -446,6 +450,21 @@ describe('EditEmaToolbarComponent', () => {
queryParamsHandling: 'merge'
});
});

it('should trigger a store reload if the URL from urlContentMap is the same as the current URL', () => {
jest.spyOn(store, 'pageAPIResponse').mockReturnValue(PAGE_RESPONSE_URL_CONTENT_MAP);

spectator.triggerEventHandler(DotEditEmaWorkflowActionsComponent, 'newPage', {
pageURI: '/test-url',
url: '/test-url',
languageId: 1
// eslint-disable-next-line @typescript-eslint/no-explicit-any
} as any);

spectator.detectChanges();
expect(store.reload).toHaveBeenCalled();
expect(router.navigate).not.toHaveBeenCalled();
});
});

describe('dot-ema-info-display', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,10 +253,21 @@ export class EditEmaToolbarComponent {
});
}

/**
* Determines whether navigation to a new page is necessary based on URL and language changes.
*
* @param {Params} params - The incoming parameters, including a new URL and language ID.
* @returns {boolean} - True if navigation to a new page is needed.
*/
private shouldNavigateToNewPage(params: Params): boolean {
const { url: newUrl, language_id: newLanguageId } = params;
const { url, language_id } = this.uveStore.params();
const { url: currentUrl, language_id: currentLanguageId } = this.uveStore.params();

// Determine the target URL, prioritizing the content map URL if available
const urlContentMap = this.uveStore.pageAPIResponse().urlContentMap;
const targetUrl = urlContentMap?.URL_MAP_FOR_CONTENT || newUrl;

return !compareUrlPaths(newUrl, url) || newLanguageId != language_id;
// Return true if the URL paths are different or the language has changed
return !compareUrlPaths(currentUrl, targetUrl) || newLanguageId != currentLanguageId;
}
}
38 changes: 31 additions & 7 deletions core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,32 @@ export const MOCK_RESPONSE_HEADLESS: DotPageApiResponse = {
containers: mockDotContainers()
};

export const URL_CONTENT_MAP_MOCK = {
contentType: 'Blog',
identifier: '123',
inode: '1234',
title: 'hello world',
baseType: 'CONTENT',
folder: 'SYSTEM_FOLDER',
host: '123',
languageId: 1,
live: true,
modDate: '1722992210315',
modUser: 'dotcms.org.1',
owner: 'dotcms.org.1',
url: '/content.ec123',
working: true,
archived: false,
hasTitleImage: true,
hostName: 'demo.dotcms.com',
locked: false,
modUserName: 'Admin User',
sortOrder: 0,
stInode: '799f176a-d32e-4844-a07c-1b5fcd107578',
titleImage: 'image',
URL_MAP_FOR_CONTENT: '/test-url'
};

export const MOCK_RESPONSE_VTL: DotPageApiResponse = {
page: {
pageURI: 'test-url',
Expand Down Expand Up @@ -214,6 +240,11 @@ export const MOCK_RESPONSE_VTL: DotPageApiResponse = {
containers: mockDotContainers()
};

export const PAGE_RESPONSE_URL_CONTENT_MAP = {
...MOCK_RESPONSE_VTL,
urlContentMap: URL_CONTENT_MAP_MOCK
};

export const dotPageContainerStructureMock: DotPageContainerStructure = {
'123': {
container: {
Expand Down Expand Up @@ -387,13 +418,6 @@ export const EDIT_ACTION_PAYLOAD_MOCK: ActionPayload = {
position: 'before'
};

export const URL_CONTENT_MAP_MOCK = {
contentType: 'Blog',
identifier: '123',
inode: '1234',
title: 'hello world'
};

export const PAGE_RESPONSE_BY_LANGUAGE_ID = {
1: of({
page: {
Expand Down

0 comments on commit 03c82c1

Please sign in to comment.