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 1c4ee37ba0ae..d427ab38fdd7 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
@@ -12,7 +12,7 @@ import { ConfirmDialogModule } from 'primeng/confirmdialog';
import { DialogService } from 'primeng/dynamicdialog';
import { ToastModule } from 'primeng/toast';
-import { CLIENT_ACTIONS } from '@dotcms/client';
+import { CLIENT_ACTIONS, UVE_MODE } from '@dotcms/client';
import {
DotContentletLockerService,
DotExperimentsService,
@@ -115,7 +115,8 @@ const INITIAL_PAGE_PARAMS = {
language_id: 1,
url: 'index',
variantName: 'DEFAULT',
- 'com.dotmarketing.persona.id': 'modes.persona.no.persona'
+ 'com.dotmarketing.persona.id': 'modes.persona.no.persona',
+ editorMode: UVE_MODE.EDIT
};
const BASIC_OPTIONS = {
@@ -320,7 +321,7 @@ describe('DotEmaShellComponent', () => {
expect(spyloadPageAsset).toHaveBeenCalledWith(INITIAL_PAGE_PARAMS);
expect(spyStoreLoadPage).toHaveBeenCalledWith(INITIAL_PAGE_PARAMS);
expect(spyLocation).toHaveBeenCalledWith(
- '/?language_id=1&url=index&variantName=DEFAULT&com.dotmarketing.persona.id=modes.persona.no.persona'
+ '/?language_id=1&url=index&variantName=DEFAULT&com.dotmarketing.persona.id=modes.persona.no.persona&editorMode=edit'
);
});
@@ -376,7 +377,8 @@ describe('DotEmaShellComponent', () => {
language_id: 2,
url: 'my-awesome-page',
variantName: 'DEFAULT',
- 'com.dotmarketing.persona.id': 'SomeCoolDude'
+ 'com.dotmarketing.persona.id': 'SomeCoolDude',
+ editorMode: UVE_MODE.EDIT
};
const url = router.createUrlTree([], { queryParams: newParams });
@@ -530,6 +532,47 @@ describe('DotEmaShellComponent', () => {
});
});
+ describe('Editor Mode', () => {
+ it('should set editorMode to EDIT when wrong editorMode is passed', () => {
+ const spyStoreLoadPage = jest.spyOn(store, 'loadPageAsset');
+ const params = {
+ ...INITIAL_PAGE_PARAMS,
+ editorMode: 'WRONG'
+ };
+ overrideRouteSnashot(
+ activatedRoute,
+ SNAPSHOT_MOCK({ queryParams: params, data: UVE_CONFIG_MOCK(BASIC_OPTIONS) })
+ );
+ spectator.detectChanges();
+ expect(spyStoreLoadPage).toHaveBeenCalledWith({
+ ...INITIAL_PAGE_PARAMS,
+ editorMode: UVE_MODE.EDIT
+ });
+ });
+
+ it('should add the current date if preview param is true and publishDate is not present', () => {
+ const spyStoreLoadPage = jest.spyOn(store, 'loadPageAsset');
+ const params = {
+ ...INITIAL_PAGE_PARAMS,
+ editorMode: UVE_MODE.PREVIEW
+ };
+
+ // override the new Date() to return a fixed date
+ const fixedDate = new Date('2024-01-01');
+ jest.spyOn(global, 'Date').mockImplementation(() => fixedDate);
+
+ const data = UVE_CONFIG_MOCK(BASIC_OPTIONS);
+
+ overrideRouteSnashot(activatedRoute, SNAPSHOT_MOCK({ queryParams: params, data }));
+
+ spectator.detectChanges();
+ expect(spyStoreLoadPage).toHaveBeenCalledWith({
+ ...params,
+ publishDate: fixedDate.toISOString()
+ });
+ });
+ });
+
describe('Site Changes', () => {
it('should trigger a navigate to /pages when site changes', async () => {
const navigate = jest.spyOn(router, 'navigate');
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 fc10066c55c1..1a8e96406d19 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
@@ -11,6 +11,7 @@ import { ToastModule } from 'primeng/toast';
import { skip } from 'rxjs/operators';
+import { UVE_MODE } from '@dotcms/client';
import {
DotESContentService,
DotExperimentsService,
@@ -30,10 +31,10 @@ import { EditEmaNavigationBarComponent } from './components/edit-ema-navigation-
import { DotEmaDialogComponent } from '../components/dot-ema-dialog/dot-ema-dialog.component';
import { DotActionUrlService } from '../services/dot-action-url/dot-action-url.service';
-import { DotPageApiParams, DotPageApiService } from '../services/dot-page-api.service';
+import { DotPageApiService } from '../services/dot-page-api.service';
import { WINDOW } from '../shared/consts';
import { NG_CUSTOM_EVENTS } from '../shared/enums';
-import { DialogAction } from '../shared/models';
+import { DialogAction, DotPageAssetParams } from '../shared/models';
import { UVEStore } from '../store/dot-uve.store';
import {
checkClientHostAccess,
@@ -201,7 +202,7 @@ export class DotEmaShellComponent implements OnInit, OnDestroy {
* @return {*} {DotPageApiParams}
* @memberof DotEmaShellComponent
*/
- #getPageParams(): DotPageApiParams {
+ #getPageParams(): DotPageAssetParams {
const { queryParams, data } = this.#activatedRoute.snapshot;
const uveConfig = data?.uveConfig;
const allowedDevURLs = uveConfig?.options?.allowedDevURLs;
@@ -218,6 +219,14 @@ export class DotEmaShellComponent implements OnInit, OnDestroy {
params.clientHost = uveConfig.url;
}
+ if (params.editorMode !== UVE_MODE.EDIT && params.editorMode !== UVE_MODE.PREVIEW) {
+ params.editorMode = UVE_MODE.EDIT;
+ }
+
+ if (params.editorMode === UVE_MODE.PREVIEW && !params.publishDate) {
+ params.publishDate = new Date().toISOString();
+ }
+
return params;
}
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html
index e5e086766816..cb5d2ae65352 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html
@@ -12,21 +12,18 @@
@if (preview) {
}
@@ -62,7 +59,7 @@
icon="pi pi-eye"
styleClass="p-button-text p-button-sm"
data-testId="uve-toolbar-preview"
- (click)="setPreviewMode()" />
+ (click)="triggerPreviewMode()" />
@@ -89,7 +86,7 @@
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.scss b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.scss
index d8049559596f..d8302f003f62 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.scss
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.scss
@@ -4,19 +4,17 @@
.uve-toolbar {
padding: 0 $spacing-4;
transition: padding 0.2s ease;
- border-color: $color-palette-primary-200;
border-top: 1px solid transparent; // Avoid jump
}
- .uve-toolbar-preview {
- border-top-color: $color-palette-primary-200;
- padding-inline: $spacing-5;
- }
-
- .p-chip.uve-toolbar-chips {
+ .p-button.uve-toolbar-chips,
+ .p-button.uve-toolbar-chips:hover,
+ .p-button.uve-toolbar-chips:focus {
height: $field-height-sm;
background-color: $color-palette-primary-op-10;
border-color: $color-palette-primary-op-10;
+ color: var(--color-palette-primary-500);
+ cursor: pointer;
}
}
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts
index 291efdc4c758..603ddb209a08 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts
@@ -9,6 +9,7 @@ import { By } from '@angular/platform-browser';
import { ConfirmationService, MessageService } from 'primeng/api';
+import { UVE_MODE } from '@dotcms/client';
import {
DotExperimentsService,
DotLanguagesService,
@@ -103,6 +104,9 @@ describe('DotUveToolbarComponent', () => {
let messageService: MessageService;
let confirmationService: ConfirmationService;
+ const fixedDate = new Date('2024-01-01');
+ jest.spyOn(global, 'Date').mockImplementation(() => fixedDate);
+
const createComponent = createComponentFactory({
component: DotUveToolbarComponent,
imports: [
@@ -249,7 +253,10 @@ describe('DotUveToolbarComponent', () => {
spectator.click(byTestId('uve-toolbar-preview'));
- expect(spy).toHaveBeenCalledWith({ preview: 'true' });
+ expect(spy).toHaveBeenCalledWith({
+ editorMode: UVE_MODE.PREVIEW,
+ publishDate: fixedDate.toISOString()
+ });
});
});
@@ -378,7 +385,10 @@ describe('DotUveToolbarComponent', () => {
beforeEach(() => {
spectator = createComponent({
providers: [
- mockProvider(UVEStore, { ...baseUVEState, $isPreviewMode: signal(true) })
+ mockProvider(UVEStore, {
+ ...baseUVEState,
+ $isPreviewMode: signal(true)
+ })
]
});
@@ -390,33 +400,62 @@ describe('DotUveToolbarComponent', () => {
expect(spectator.query(byTestId('close-preview-mode'))).toBeTruthy();
});
- it('should call store.loadPageAsset with preview null', () => {
+ it('should call store.loadPageAsset without editorMode and publishDate', () => {
const spy = jest.spyOn(store, 'loadPageAsset');
spectator.click(byTestId('close-preview-mode'));
spectator.detectChanges();
- expect(spy).toHaveBeenCalledWith({ preview: null });
+ expect(spy).toHaveBeenCalledWith({ editorMode: undefined, publishDate: undefined });
+ });
+
+ it('should call store.loadPageAsset when datePreview model is updated', () => {
+ const spy = jest.spyOn(store, 'loadPageAsset');
+
+ spectator.debugElement.componentInstance.$previewDate.set(new Date('2024-02-01'));
+ spectator.detectChanges();
+
+ expect(spy).toHaveBeenCalledWith({
+ editorMode: UVE_MODE.PREVIEW,
+ publishDate: new Date('2024-02-01').toISOString()
+ });
+ });
+
+ it('should call store.loadPageAsset with currentDate when datePreview model is updated with a past date', () => {
+ const spy = jest.spyOn(store, 'loadPageAsset');
+
+ spectator.debugElement.componentInstance.$previewDate.set(new Date('2023-02-01'));
+ spectator.detectChanges();
+
+ expect(spy).toHaveBeenCalledWith({
+ editorMode: UVE_MODE.PREVIEW,
+ publishDate: fixedDate.toISOString()
+ });
});
});
it('should have desktop button', () => {
+ spectator.detectChanges();
expect(spectator.query(byTestId('desktop-preview'))).toBeTruthy();
});
it('should have mobile button', () => {
+ spectator.detectChanges();
expect(spectator.query(byTestId('mobile-preview'))).toBeTruthy();
});
it('should have tablet button', () => {
+ spectator.detectChanges();
expect(spectator.query(byTestId('tablet-preview'))).toBeTruthy();
});
it('should have more devices button', () => {
+ spectator.detectChanges();
expect(spectator.query(byTestId('more-devices-preview'))).toBeTruthy();
});
it('should not have experiments', () => {
+ spectator.detectChanges();
expect(spectator.query(byTestId('uve-toolbar-running-experiment'))).toBeFalsy();
});
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts
index 0f649288747d..7c8d9d567d4b 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts
@@ -6,8 +6,11 @@ import {
computed,
EventEmitter,
inject,
+ model,
Output,
- viewChild
+ effect,
+ viewChild,
+ untracked
} from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@@ -18,8 +21,10 @@ import { ChipModule } from 'primeng/chip';
import { SplitButtonModule } from 'primeng/splitbutton';
import { ToolbarModule } from 'primeng/toolbar';
+import { UVE_MODE } from '@dotcms/client';
import { DotMessageService, DotPersonalizeService } from '@dotcms/data-access';
import { DotPersona, DotLanguage } from '@dotcms/dotcms-models';
+import { DotMessagePipe } from '@dotcms/ui';
import { DEFAULT_PERSONA } from '../../../shared/consts';
import { DotPage } from '../../../shared/models';
@@ -49,6 +54,8 @@ import { EditEmaPersonaSelectorComponent } from '../edit-ema-persona-selector/ed
ReactiveFormsModule,
EditEmaPersonaSelectorComponent,
EditEmaLanguageSelectorComponent,
+ ClipboardModule,
+ DotMessagePipe,
DotUveWorkflowActionsComponent,
ChipModule
],
@@ -74,6 +81,8 @@ export class DotUveToolbarComponent {
readonly $apiURL = this.#store.$apiURL;
readonly $personaSelectorProps = this.#store.$personaSelector;
+ protected readonly CURRENT_DATE = new Date();
+
readonly $styleToolbarClass = computed(() => {
if (!this.$isPreviewMode()) {
return 'uve-toolbar';
@@ -82,6 +91,35 @@ export class DotUveToolbarComponent {
return 'uve-toolbar uve-toolbar-preview';
});
+ protected readonly publishDateParam = this.#store.pageParams().publishDate;
+ protected readonly $previewDate = model(
+ this.publishDateParam ? new Date(this.publishDateParam) : null
+ );
+
+ readonly $previewDateEffect = effect(
+ () => {
+ const previewDate = this.$previewDate();
+
+ if (!previewDate) {
+ return;
+ }
+
+ // If previewDate is minor that the CURRENT DATE, set previewDate to CURRENT DATE
+ if (previewDate < this.CURRENT_DATE) {
+ this.$previewDate.set(this.CURRENT_DATE);
+
+ return;
+ }
+
+ untracked(() => {
+ this.#store.loadPageAsset({
+ editorMode: UVE_MODE.PREVIEW,
+ publishDate: previewDate?.toISOString()
+ });
+ });
+ },
+ { allowSignalWrites: true }
+ );
readonly $pageInode = computed(() => {
return this.#store.pageAPIResponse()?.page.inode;
});
@@ -89,24 +127,23 @@ export class DotUveToolbarComponent {
readonly $actions = this.#store.workflowLoading;
readonly $workflowLoding = this.#store.workflowLoading;
- protected readonly date = new Date();
-
/**
- * Set the preview mode
+ * Initialize the preview mode
*
+ * @param {Date} publishDate
* @memberof DotUveToolbarComponent
*/
- protected setPreviewMode() {
- this.#store.loadPageAsset({ preview: 'true' });
+ protected triggerPreviewMode(publishDate = new Date()) {
+ this.$previewDate.set(publishDate);
}
/**
- * Set the edit mode
+ * Initialize the edit mode
*
* @memberof DotUveToolbarComponent
*/
- protected setEditMode() {
- this.#store.loadPageAsset({ preview: null });
+ protected triggerEditMode() {
+ this.#store.loadPageAsset({ editorMode: undefined, publishDate: undefined });
}
/**
@@ -135,6 +172,11 @@ export class DotUveToolbarComponent {
this.#store.loadPageAsset({ language_id });
}
+ /**
+ * Trigger the copy toasts
+ *
+ * @memberof DotUveToolbarComponent
+ */
triggerCopyToast() {
this.#messageService.add({
severity: 'success',
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts
index d40d3308e568..9e26efa742fe 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.spec.ts
@@ -2918,21 +2918,6 @@ describe('EditEmaEditorComponent', () => {
describe('CUSTOMER ACTIONS', () => {
describe('CLIENT_READY', () => {
- it('should set client is ready when not extra configuration is send', () => {
- const setIsClientReadySpy = jest.spyOn(store, 'setIsClientReady');
-
- window.dispatchEvent(
- new MessageEvent('message', {
- origin: HOST,
- data: {
- action: CLIENT_ACTIONS.CLIENT_READY
- }
- })
- );
-
- expect(setIsClientReadySpy).toHaveBeenCalledWith(true);
- });
-
it('should set client GraphQL configuration and call the reload', () => {
const setClientConfigurationSpy = jest.spyOn(
store,
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts
index a26c028c30cf..03adcd56f94e 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.ts
@@ -1011,13 +1011,6 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
return;
}
- // If there is no client configuration, we just set the client as ready
- if (!clientConfig) {
- this.uveStore.setIsClientReady(true);
-
- return;
- }
-
this.uveStore.setClientConfiguration({ query, params });
this.uveStore.reloadCurrentPage();
},
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.spec.ts
index 903e24d7e79a..e881a5096d35 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.spec.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.spec.ts
@@ -1,5 +1,7 @@
import { createHttpFactory, HttpMethod, SpectatorHttp } from '@ngneat/spectator';
+import { UVE_MODE } from '@dotcms/client';
+
import { DotPageApiService } from './dot-page-api.service';
describe('DotPageApiService', () => {
@@ -109,13 +111,13 @@ describe('DotPageApiService', () => {
});
describe('preview', () => {
- it("should request page in preview mode if 'preview' is true", () => {
+ it("should request page in preview mode if 'editorMode' is 'preview'", () => {
spectator.service
.get({
url: 'test-url',
language_id: 'en',
'com.dotmarketing.persona.id': 'modes.persona.no.persona',
- preview: 'true'
+ editorMode: UVE_MODE.PREVIEW
})
.subscribe();
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.ts
index 252b60f6609e..9669f7e82fda 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/services/dot-page-api.service.ts
@@ -5,7 +5,7 @@ import { Injectable } from '@angular/core';
import { catchError, map, pluck } from 'rxjs/operators';
-import { graphqlToPageEntity } from '@dotcms/client';
+import { graphqlToPageEntity, UVE_MODE } from '@dotcms/client';
import { Site } from '@dotcms/dotcms-js';
import {
DEFAULT_VARIANT_ID,
@@ -19,7 +19,7 @@ import {
} from '@dotcms/dotcms-models';
import { PAGE_MODE } from '../shared/enums';
-import { DotPage, SavePagePayload } from '../shared/models';
+import { DotPage, DotPageAssetParams, SavePagePayload } from '../shared/models';
import { ClientRequestProps } from '../store/features/client/withClient';
import { createPageApiUrlWithQueryParams } from '../utils';
@@ -43,15 +43,15 @@ export interface DotPageApiParams {
url: string;
language_id: string;
'com.dotmarketing.persona.id': string;
- preview?: string;
variantName?: string;
experimentId?: string;
mode?: string;
clientHost?: string;
depth?: string;
+ publishDate?: string;
}
-export enum DotPageApiKeys {
+export enum DotPageAssetKeys {
URL = 'url',
MODE = 'mode',
DEPTH = 'depth',
@@ -60,7 +60,8 @@ export enum DotPageApiKeys {
LANGUAGE_ID = 'language_id',
EXPERIMENT_ID = 'experimentId',
PERSONA_ID = 'com.dotmarketing.persona.id',
- PREVIEW = 'preview'
+ PUBLISH_DATE = 'publishDate',
+ EDITOR_MODE = 'editorMode'
}
export interface GetPersonasParams {
@@ -92,13 +93,21 @@ export class DotPageApiService {
* @return {*} {Observable}
* @memberof DotPageApiService
*/
- get(params: DotPageApiParams): Observable {
+ get(params: DotPageAssetParams): Observable {
// Remove trailing and leading slashes
- const { clientHost, preview, depth = '0', language_id, variantName, experimentId } = params;
+ const {
+ clientHost,
+ editorMode,
+ depth = '0',
+ language_id,
+ variantName,
+ experimentId,
+ publishDate
+ } = params;
const url = params.url.replace(/^\/+|\/+$/g, '');
- const isPreview = preview === 'true';
const pageType = clientHost ? 'json' : 'render';
+ const isPreview = editorMode === UVE_MODE.PREVIEW;
const mode = isPreview ? PAGE_MODE.LIVE : PAGE_MODE.EDIT;
const pageApiUrl = createPageApiUrlWithQueryParams(url, {
@@ -107,7 +116,8 @@ export class DotPageApiService {
variantName,
experimentId,
depth,
- mode
+ mode,
+ publishDate: publishDate ?? undefined
});
const apiUrl = `/api/v1/page/${pageType}/${pageApiUrl}`;
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/models.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/models.ts
index d37c3ea172d1..9384e66bf6a9 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/models.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/models.ts
@@ -1,4 +1,4 @@
-import { CLIENT_ACTIONS } from '@dotcms/client';
+import { UVE_MODE, CLIENT_ACTIONS } from '@dotcms/client';
import { DotCMSContentlet, DotDevice } from '@dotcms/dotcms-models';
import { InfoPage } from '@dotcms/ui';
@@ -246,3 +246,5 @@ export interface ReorderMenuPayload {
startLevel: number;
depth: number;
}
+
+export type DotPageAssetParams = DotPageApiParams & { editorMode?: UVE_MODE };
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.spec.ts
index 4755c10f8e5e..2e676a06596b 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.spec.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.spec.ts
@@ -12,6 +12,7 @@ import { ActivatedRoute, Router } from '@angular/router';
import { MessageService } from 'primeng/api';
+import { UVE_MODE } from '@dotcms/client';
import {
DotExperimentsService,
DotLanguagesService,
@@ -362,13 +363,13 @@ describe('UVEStore', () => {
describe('$isPreviewMode', () => {
it("should return true when the preview is 'true'", () => {
- store.loadPageAsset({ preview: 'true' });
+ store.loadPageAsset({ editorMode: UVE_MODE.PREVIEW });
expect(store.$isPreviewMode()).toBe(true);
});
it("should return false when the preview is not 'true'", () => {
- store.loadPageAsset({ preview: null });
+ store.loadPageAsset({ editorMode: null });
expect(store.$isPreviewMode()).toBe(false);
});
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.ts
index e6fb45529b9d..c28caa65b7de 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/dot-uve.store.ts
@@ -2,6 +2,8 @@ import { patchState, signalStore, withComputed, withMethods, withState } from '@
import { computed, untracked } from '@angular/core';
+import { UVE_MODE } from '@dotcms/client';
+
import { withEditor } from './features/editor/withEditor';
import { withFlags } from './features/flags/withFlags';
import { withLayout } from './features/layout/withLayout';
@@ -126,7 +128,7 @@ export const UVEStore = signalStore(
return pageAPIResponse()?.viewAs.language?.id || 1;
}),
$isPreviewMode: computed(() => {
- return pageParams()?.preview === 'true';
+ return pageParams()?.editorMode === UVE_MODE.PREVIEW;
})
};
}
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/client/withClient.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/client/withClient.ts
index 6cd615abea96..30690ba2c8f1 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/client/withClient.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/client/withClient.ts
@@ -55,6 +55,8 @@ export function withClient() {
},
setClientConfiguration: ({ query, params }: ClientRequestProps) => {
patchState(store, {
+ // Added this to avoid the client ready event to be triggered
+ isClientReady: true,
clientRequestProps: {
query,
params
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts
index 1bd25f53c8fd..7b6281746207 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/toolbar/withUVEToolbar.ts
@@ -9,6 +9,7 @@ import {
import { computed } from '@angular/core';
+import { UVE_MODE } from '@dotcms/client';
import { DotExperimentStatus } from '@dotcms/dotcms-models';
import { DEFAULT_PERSONA } from '../../../../shared/consts';
@@ -87,7 +88,7 @@ export function withUVEToolbar() {
const siteId = pageAPIResponse?.site?.identifier;
const clientHost = `${params?.clientHost ?? window.location.origin}`;
- const isPreview = params?.preview === 'true';
+ const isPreview = params?.editorMode === UVE_MODE.PREVIEW;
const prevewItem = isPreview
? {
deviceSelector: {
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts
index 14dc9b20de07..b98da56a9667 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.spec.ts
@@ -6,6 +6,7 @@ import { of } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';
+import { UVE_MODE } from '@dotcms/client';
import { CurrentUser } from '@dotcms/dotcms-js';
import { DEFAULT_VARIANT_ID, DEFAULT_VARIANT_NAME, DotCMSContentlet } from '@dotcms/dotcms-models';
import { getRunningExperimentMock, mockDotDevices, seoOGTagsMock } from '@dotcms/utils-testing';
@@ -672,7 +673,9 @@ describe('withEditor', () => {
});
it('should not have opacity or progressBar in preview mode', () => {
- patchState(store, { pageParams: { ...emptyParams, preview: 'true' } });
+ patchState(store, {
+ pageParams: { ...emptyParams, editorMode: UVE_MODE.PREVIEW }
+ });
expect(store.$editorProps().iframe.opacity).toBe('1');
expect(store.$editorProps().progressBar).toBe(false);
@@ -831,6 +834,20 @@ describe('withEditor', () => {
expect(store.$editorProps().contentletTools).toBe(null);
});
+
+ it('should have contentletTools when the page can be edited and is in preview mode', () => {
+ patchState(store, {
+ isEditState: true,
+ canEditPage: true,
+ pageParams: {
+ ...emptyParams,
+ editorMode: UVE_MODE.PREVIEW
+ },
+ state: EDITOR_STATE.IDLE
+ });
+
+ expect(store.$editorProps().contentletTools).toEqual(null);
+ });
});
describe('dropzone', () => {
const bounds = getBoundsMock(ACTION_MOCK);
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts
index fe52357aa962..0e60b8be6d6c 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/editor/withEditor.ts
@@ -9,6 +9,7 @@ import {
import { computed, untracked } from '@angular/core';
+import { UVE_MODE } from '@dotcms/client';
import { DotTreeNode, SeoMetaTags } from '@dotcms/dotcms-models';
import {
@@ -115,7 +116,7 @@ export function withEditor() {
const dragItem = store.dragItem();
const isEditState = store.isEditState();
- const isPreview = params?.preview === 'true';
+ const isPreview = params?.editorMode === UVE_MODE.PREVIEW;
const isPageReady = isTraditionalPage || isClientReady || isPreview;
const isLoading = !isPageReady || store.status() === UVE_STATUS.LOADING;
@@ -129,7 +130,11 @@ export function withEditor() {
const showBlockEditorSidebar = canEditPage && isEditState && isEnterprise;
const canUserHaveContentletTools =
- !!contentletArea && canEditPage && isEditState && !isScrolling;
+ !!contentletArea &&
+ canEditPage &&
+ isEditState &&
+ !isScrolling &&
+ !isPreview;
const showDropzone = canEditPage && state === EDITOR_STATE.DRAGGING;
const showPalette = isEnterprise && canEditPage && isEditState && !isPreview;
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/load/withLoad.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/load/withLoad.ts
index fb532018d950..ca4c9ef6ae96 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/load/withLoad.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/load/withLoad.ts
@@ -12,8 +12,9 @@ import { DotExperimentsService, DotLanguagesService, DotLicenseService } from '@
import { LoginService } from '@dotcms/dotcms-js';
import { DEFAULT_VARIANT_ID } from '@dotcms/dotcms-models';
-import { DotPageApiParams, DotPageApiService } from '../../../services/dot-page-api.service';
+import { DotPageApiService } from '../../../services/dot-page-api.service';
import { UVE_STATUS } from '../../../shared/enums';
+import { DotPageAssetParams } from '../../../shared/models';
import { computeCanEditPage, computePageIsLocked, isForwardOrPage } from '../../../utils';
import { UVEState } from '../../models';
import { withClient } from '../client/withClient';
@@ -48,11 +49,11 @@ export function withLoad() {
* @param {DotPageApiParams} pageParams - The parameters used to fetch the page asset.
* @memberof DotEmaShellComponent
*/
- loadPageAsset: rxMethod>(
+ loadPageAsset: rxMethod>(
pipe(
map((params) => {
if (!store.pageParams()) {
- return params as DotPageApiParams;
+ return params as DotPageAssetParams;
}
return {
@@ -140,9 +141,7 @@ export function withLoad() {
currentUser
);
- const isPreview = pageParams.preview === 'true';
const isTraditionalPage = !pageParams.clientHost;
- const isClientReady = isTraditionalPage || isPreview;
patchState(store, {
pageAPIResponse: pageAsset,
@@ -152,7 +151,7 @@ export function withLoad() {
languages,
canEditPage,
pageIsLocked,
- isClientReady,
+ isClientReady: isTraditionalPage,
isTraditionalPage,
status: UVE_STATUS.LOADED
});
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/models.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/models.ts
index 113a528e5bcc..6e0d7e59fb3d 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/models.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/models.ts
@@ -7,15 +7,15 @@ import {
} from '@dotcms/dotcms-models';
import { InfoPage } from '@dotcms/ui';
-import { DotPageApiParams, DotPageApiResponse } from '../services/dot-page-api.service';
+import { DotPageApiResponse } from '../services/dot-page-api.service';
import { UVE_STATUS } from '../shared/enums';
-import { DotPage, NavigationBarItem } from '../shared/models';
+import { DotPage, DotPageAssetParams, NavigationBarItem } from '../shared/models';
export interface UVEState {
languages: DotLanguage[];
isEnterprise: boolean;
pageAPIResponse?: DotPageApiResponse;
- pageParams?: DotPageApiParams;
+ pageParams?: DotPageAssetParams;
currentUser?: CurrentUser;
experiment?: DotExperiment;
errorCode?: number;
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 fa42f0370dfe..897ce5f41314 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
@@ -1,5 +1,6 @@
import { Params } from '@angular/router';
+import { UVE_MODE } from '@dotcms/client';
import { CurrentUser } from '@dotcms/dotcms-js';
import {
DEFAULT_VARIANT_ID,
@@ -12,15 +13,16 @@ import {
} from '@dotcms/dotcms-models';
import { EmaDragItem } from '../edit-ema-editor/components/ema-page-dropzone/types';
-import { DotPageApiKeys, DotPageApiParams } from '../services/dot-page-api.service';
+import { DotPageAssetKeys, DotPageApiParams } from '../services/dot-page-api.service';
import { COMMON_ERRORS, DEFAULT_PERSONA } from '../shared/consts';
-import { EDITOR_STATE } from '../shared/enums';
+import { EDITOR_STATE, PAGE_MODE } from '../shared/enums';
import {
ActionPayload,
ContainerPayload,
ContentletDragPayload,
ContentTypeDragPayload,
DotPage,
+ DotPageAssetParams,
DragDatasetItem,
PageContainer
} from '../shared/models';
@@ -218,12 +220,12 @@ export const getPersonalization = (persona: Record) => {
*
* @export
* @param {string} url
- * @param {Partial} params
+ * @param {Partial} params
* @return {*} {string}
*/
export function createPageApiUrlWithQueryParams(
url: string,
- params: Partial
+ params: Partial
): string {
// Set default values
const completedParams = {
@@ -231,7 +233,8 @@ export function createPageApiUrlWithQueryParams(
language_id: params?.language_id ?? '1',
'com.dotmarketing.persona.id':
params?.['com.dotmarketing.persona.id'] ?? DEFAULT_PERSONA.identifier,
- variantName: params?.variantName ?? DEFAULT_VARIANT_ID
+ variantName: params?.variantName ?? DEFAULT_VARIANT_ID,
+ mode: params?.editorMode === UVE_MODE.PREVIEW ? PAGE_MODE.LIVE : params.mode
};
// Filter out undefined values and url
@@ -594,16 +597,16 @@ export const checkClientHostAccess = (
* @param {Params} params
* @return {*} {DotPageApiParams}
*/
-export function getAllowedPageParams(params: Params): DotPageApiParams {
- const allowedParams: DotPageApiKeys[] = Object.values(DotPageApiKeys);
+export function getAllowedPageParams(params: Params): DotPageAssetParams {
+ const allowedParams: DotPageAssetKeys[] = Object.values(DotPageAssetKeys);
return Object.keys(params)
- .filter((key) => key && allowedParams.includes(key as DotPageApiKeys))
+ .filter((key) => key && allowedParams.includes(key as DotPageAssetKeys))
.reduce((obj, key) => {
obj[key] = params[key];
return obj;
- }, {}) as DotPageApiParams;
+ }, {}) as DotPageAssetParams;
}
/**
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 9419124736a7..5e909c09d4d0 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
@@ -23,6 +23,7 @@ import {
} from '.';
import { DotPageApiParams } from '../services/dot-page-api.service';
+import { PAGE_MODE } from '../shared/enums';
import { dotPageContainerStructureMock } from '../shared/mocks';
import { ContentletDragPayload, ContentTypeDragPayload, DotPage } from '../shared/models';
@@ -357,11 +358,11 @@ describe('utils functions', () => {
language_id: '20',
'com.dotmarketing.persona.id': 'the-chosen-one',
experimentId: '123',
- mode: 'PREVIEW_MODE'
+ mode: PAGE_MODE.LIVE
};
const result = createPageApiUrlWithQueryParams('test', queryParams);
expect(result).toBe(
- 'test?variantName=test&language_id=20&com.dotmarketing.persona.id=the-chosen-one&experimentId=123&mode=PREVIEW_MODE'
+ 'test?variantName=test&language_id=20&com.dotmarketing.persona.id=the-chosen-one&experimentId=123&mode=LIVE'
);
});
diff --git a/core-web/libs/sdk/client/src/index.ts b/core-web/libs/sdk/client/src/index.ts
index ede6358c0df3..2ed10316e5a3 100644
--- a/core-web/libs/sdk/client/src/index.ts
+++ b/core-web/libs/sdk/client/src/index.ts
@@ -3,7 +3,8 @@ import { CLIENT_ACTIONS, postMessageToEditor } from './lib/editor/models/client.
import {
CustomClientParams,
DotCMSPageEditorConfig,
- EditorConfig
+ EditorConfig,
+ UVE_MODE
} from './lib/editor/models/editor.model';
import {
InlineEditorData,
@@ -42,5 +43,6 @@ export {
initInlineEditing,
InlineEditEventData,
InlineEditorData,
- INLINE_EDITING_EVENT_KEY
+ INLINE_EDITING_EVENT_KEY,
+ UVE_MODE
};
diff --git a/core-web/libs/sdk/client/src/lib/editor/models/editor.model.ts b/core-web/libs/sdk/client/src/lib/editor/models/editor.model.ts
index 07f28b7784be..3848e895d750 100644
--- a/core-web/libs/sdk/client/src/lib/editor/models/editor.model.ts
+++ b/core-web/libs/sdk/client/src/lib/editor/models/editor.model.ts
@@ -66,3 +66,8 @@ export interface ReorderMenuConfig {
*/
depth: number;
}
+
+export enum UVE_MODE {
+ EDIT = 'edit',
+ PREVIEW = 'preview'
+}
diff --git a/core-web/libs/sdk/client/src/lib/editor/sdk-editor.spec.ts b/core-web/libs/sdk/client/src/lib/editor/sdk-editor.spec.ts
index 3e0b9e8ebbdd..d65dbf56d602 100644
--- a/core-web/libs/sdk/client/src/lib/editor/sdk-editor.spec.ts
+++ b/core-web/libs/sdk/client/src/lib/editor/sdk-editor.spec.ts
@@ -108,6 +108,16 @@ describe('DotCMSPageEditor', () => {
lastScrollYPosition: 0
});
});
+
+ it('should isInsideEditor return false when is preview mode', () => {
+ Object.defineProperty(window, 'location', {
+ value: {
+ search: '?editorMode=preview'
+ },
+ writable: true
+ });
+ expect(isInsideEditor()).toBe(false);
+ });
});
describe('Add Class to Empty Contentets', () => {
@@ -164,15 +174,4 @@ describe('DotCMSPageEditor', () => {
});
});
});
-
- it('should isInsideEditor return false when is preview mode', () => {
- Object.defineProperty(window, 'location', {
- value: {
- search: '?preview=true'
- },
- writable: true
- });
-
- expect(isInsideEditor()).toBe(false);
- });
});
diff --git a/core-web/libs/sdk/client/src/lib/editor/sdk-editor.ts b/core-web/libs/sdk/client/src/lib/editor/sdk-editor.ts
index 54df6ccae698..e1132cfc1341 100644
--- a/core-web/libs/sdk/client/src/lib/editor/sdk-editor.ts
+++ b/core-web/libs/sdk/client/src/lib/editor/sdk-editor.ts
@@ -93,15 +93,21 @@ export function reorderMenu(config?: ReorderMenuConfig): void {
}
/**
- * Checks if the code is running inside an editor.
+ * Checks if the code is running inside the DotCMS Universal Visual Editor (UVE).
*
- * @returns {boolean} Returns true if the code is running inside an editor, otherwise false.
+ * The function checks three conditions:
+ * 1. If window is defined (for SSR environments)
+ * 2. If the page is not in preview mode
+ * 3. If the current window is embedded in a parent frame
+ *
+ * @returns {boolean} Returns true if running inside the UVE editor, false if running standalone or in preview mode
* @example
* ```ts
+ * // Check if code is running in editor before initializing editor-specific features
* if (isInsideEditor()) {
- * console.log('Running inside the editor');
+ * initEditor(config);
* } else {
- * console.log('Running outside the editor');
+ * initStandaloneMode();
* }
* ```
*/
@@ -110,9 +116,7 @@ export function isInsideEditor(): boolean {
return false;
}
- const preview = isPreviewMode();
-
- if (preview) {
+ if (isPreviewMode()) {
return false;
}
diff --git a/core-web/libs/sdk/client/src/lib/utils/page/common-utils.spec.ts b/core-web/libs/sdk/client/src/lib/utils/page/common-utils.spec.ts
index 7d1c40c6aa61..79afca346758 100644
--- a/core-web/libs/sdk/client/src/lib/utils/page/common-utils.spec.ts
+++ b/core-web/libs/sdk/client/src/lib/utils/page/common-utils.spec.ts
@@ -36,15 +36,15 @@ describe('Common Utils', () => {
});
describe('Is Preview Mode', () => {
- it('should return true when preview mode is enabled', () => {
+ it('should return true when editorMode is preview', () => {
jest.spyOn(window, 'location', 'get').mockReturnValueOnce({
- search: '?preview=true'
+ search: '?editorMode=preview'
} as Location);
expect(isPreviewMode()).toBe(true);
});
- it('should return false when preview mode is disabled', () => {
+ it('should return false when editorMode is not preview', () => {
jest.spyOn(window, 'location', 'get').mockReturnValueOnce({
search: ''
} as Location);
diff --git a/core-web/libs/sdk/client/src/lib/utils/page/common-utils.ts b/core-web/libs/sdk/client/src/lib/utils/page/common-utils.ts
index b9580fc444be..f7c8c86aa26e 100644
--- a/core-web/libs/sdk/client/src/lib/utils/page/common-utils.ts
+++ b/core-web/libs/sdk/client/src/lib/utils/page/common-utils.ts
@@ -1,4 +1,5 @@
import { PageApiOptions } from '../../client/sdk-js-client';
+import { UVE_MODE } from '../../editor/models/editor.model';
/**
* Interface representing the properties for page request parameters.
@@ -57,6 +58,10 @@ export const getPageRequestParams = ({
finalParams['personaId'] = copiedParams['personaId'] || dotMarketingPersonaId;
}
+ if (copiedParams['publishDate']) {
+ finalParams['publishDate'] = copiedParams['publishDate'];
+ }
+
return {
path,
...finalParams
@@ -70,7 +75,7 @@ export const getPageRequestParams = ({
*/
export const isPreviewMode = (): boolean => {
const queryParams = new URLSearchParams(window.location.search);
- const isPreviewMode = queryParams.get('preview');
+ const editorMode = queryParams.get('editorMode');
- return isPreviewMode === 'true';
+ return editorMode === UVE_MODE.PREVIEW;
};
diff --git a/examples/nextjs/src/hooks/usePageAsset.js b/examples/nextjs/src/hooks/usePageAsset.js
index c67553920274..c46270d8c84f 100644
--- a/examples/nextjs/src/hooks/usePageAsset.js
+++ b/examples/nextjs/src/hooks/usePageAsset.js
@@ -5,7 +5,6 @@ import { CLIENT_ACTIONS, isInsideEditor, postMessageToEditor } from '@dotcms/cli
export const usePageAsset = (currentPageAsset) => {
const [pageAsset, setPageAsset] = useState(null);
-
useEffect(() => {
if (!isInsideEditor()) {
return;