diff --git a/core-web/libs/dotcms-models/src/lib/shared-models.ts b/core-web/libs/dotcms-models/src/lib/shared-models.ts
index 665e02e46561..d085547b5bfd 100644
--- a/core-web/libs/dotcms-models/src/lib/shared-models.ts
+++ b/core-web/libs/dotcms-models/src/lib/shared-models.ts
@@ -27,7 +27,8 @@ export const enum FeaturedFlags {
FEATURE_FLAG_CONTENT_EDITOR2_ENABLED = 'CONTENT_EDITOR2_ENABLED',
FEATURE_FLAG_CONTENT_EDITOR2_CONTENT_TYPE = 'CONTENT_EDITOR2_CONTENT_TYPE',
FEATURE_FLAG_ANNOUNCEMENTS = 'FEATURE_FLAG_ANNOUNCEMENTS',
- FEATURE_FLAG_NEW_EDIT_PAGE = 'FEATURE_FLAG_NEW_EDIT_PAGE'
+ FEATURE_FLAG_NEW_EDIT_PAGE = 'FEATURE_FLAG_NEW_EDIT_PAGE',
+ FEATURE_FLAG_UVE_PREVIEW_MODE = 'FEATURE_FLAG_UVE_PREVIEW_MODE'
}
export const FEATURE_FLAG_NOT_FOUND = 'NOT_FOUND';
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 c07b3966cd5d..97c92b87865e 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
@@ -27,6 +27,7 @@ import {
DotLanguagesService,
DotLicenseService,
DotMessageService,
+ DotPropertiesService,
DotWorkflowActionsFireService,
PushPublishService
} from '@dotcms/data-access';
@@ -56,6 +57,7 @@ import { DotPageApiService } from '../services/dot-page-api.service';
import { DEFAULT_PERSONA, WINDOW } from '../shared/consts';
import { FormStatus, NG_CUSTOM_EVENTS } from '../shared/enums';
import {
+ dotPropertiesServiceMock,
PAGE_RESPONSE_BY_LANGUAGE_ID,
PAGE_RESPONSE_URL_CONTENT_MAP,
PAYLOAD_MOCK
@@ -103,6 +105,10 @@ describe('DotEmaShellComponent', () => {
DotMessageService,
DialogService,
DotWorkflowActionsFireService,
+ {
+ provide: DotPropertiesService,
+ useValue: dotPropertiesServiceMock
+ },
{
provide: DotcmsConfigService,
useValue: new DotcmsConfigServiceMock()
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
new file mode 100644
index 000000000000..8412fff6e6ce
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.html
@@ -0,0 +1 @@
+
dot-uve-toolbar works!
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
new file mode 100644
index 000000000000..e69de29bb2d1
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
new file mode 100644
index 000000000000..333421176be1
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { DotUveToolbarComponent } from './dot-uve-toolbar.component';
+
+describe('DotUveToolbarComponent', () => {
+ let component: DotUveToolbarComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [DotUveToolbarComponent]
+ }).compileComponents();
+
+ fixture = TestBed.createComponent(DotUveToolbarComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
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
new file mode 100644
index 000000000000..e234bbbea17a
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/components/dot-uve-toolbar/dot-uve-toolbar.component.ts
@@ -0,0 +1,11 @@
+import { ChangeDetectionStrategy, Component } from '@angular/core';
+
+@Component({
+ selector: 'dot-uve-toolbar',
+ standalone: true,
+ imports: [],
+ templateUrl: './dot-uve-toolbar.component.html',
+ styleUrl: './dot-uve-toolbar.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush
+})
+export class DotUveToolbarComponent {}
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html
index 791f9feed1cf..794dd9f784d4 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/edit-ema-editor/edit-ema-editor.component.html
@@ -1,4 +1,9 @@
-
+@if ($previewMode()) {
+
+} @else {
+
+}
+
@if ($editorProps().seoResults && ogTagsResults$) {
UVEStore,
DotFavoritePageService,
DotESContentService,
+ {
+ provide: DotPropertiesService,
+ useValue: {
+ ...dotPropertiesServiceMock,
+ getKeyAsList: () => of([])
+ }
+ },
{
provide: DotAlertConfirmService,
useValue: {
@@ -378,7 +388,6 @@ describe('EditEmaEditorComponent', () => {
dotWorkflowActionsFireService = spectator.inject(DotWorkflowActionsFireService, true);
router = spectator.inject(Router, true);
dotPageApiService = spectator.inject(DotPageApiService, true);
-
addMessageSpy = jest.spyOn(messageService, 'add');
jest.spyOn(dotLicenseService, 'isEnterprise').mockReturnValue(of(true));
@@ -421,6 +430,23 @@ describe('EditEmaEditorComponent', () => {
});
});
+ it('should show the old toolbar when FEATURE_FLAG_UVE_PREVIEW_MODE is false', () => {
+ const toolbar = spectator.query(EditEmaToolbarComponent);
+
+ expect(toolbar).not.toBeNull();
+ });
+
+ it('should show the new toolbar when FEATURE_FLAG_UVE_PREVIEW_MODE is true', () => {
+ store.setFlags({
+ FEATURE_FLAG_UVE_PREVIEW_MODE: true
+ });
+ spectator.detectChanges();
+
+ const toolbar = spectator.query(DotUveToolbarComponent);
+
+ expect(toolbar).not.toBeNull();
+ });
+
it('should hide components when the store changes for a variant', () => {
const componentsToHide = [
'palette',
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 5f3ffa77889c..7625cf1cb97e 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
@@ -45,15 +45,10 @@ import {
SeoMetaTagsResult
} from '@dotcms/dotcms-models';
import { DotResultsSeoToolComponent } from '@dotcms/portlets/dot-ema/ui';
-import {
- SafeUrlPipe,
- DotSpinnerModule,
- DotMessagePipe,
- DotCopyContentModalService
-} from '@dotcms/ui';
+import { SafeUrlPipe, DotSpinnerModule, DotCopyContentModalService } from '@dotcms/ui';
import { isEqual } from '@dotcms/utils';
-import { DotEmaBookmarksComponent } from './components/dot-ema-bookmarks/dot-ema-bookmarks.component';
+import { DotUveToolbarComponent } from './components/dot-uve-toolbar/dot-uve-toolbar.component';
import { EditEmaPaletteComponent } from './components/edit-ema-palette/edit-ema-palette.component';
import { EditEmaToolbarComponent } from './components/edit-ema-toolbar/edit-ema-toolbar.component';
import { EmaContentletToolsComponent } from './components/ema-contentlet-tools/ema-contentlet-tools.component';
@@ -110,13 +105,12 @@ import {
DotEmaDialogComponent,
ConfirmDialogModule,
EditEmaToolbarComponent,
- DotMessagePipe,
EmaPageDropzoneComponent,
EditEmaPaletteComponent,
EmaContentletToolsComponent,
- DotEmaBookmarksComponent,
ProgressBarModule,
DotResultsSeoToolComponent,
+ DotUveToolbarComponent,
DotBlockEditorSidebarComponent
],
providers: [
@@ -158,6 +152,7 @@ export class EditEmaEditorComponent implements OnInit, OnDestroy {
readonly host = '*';
readonly $ogTags: WritableSignal = signal(undefined);
readonly $editorProps = this.uveStore.$editorProps;
+ readonly $previewMode = this.uveStore.$previewMode;
get contentWindow(): Window {
return this.iframe.nativeElement.contentWindow;
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts
index 764a21436b0c..5a491fe8f2f0 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/consts.ts
@@ -1,6 +1,6 @@
import { InjectionToken } from '@angular/core';
-import { DotPersona } from '@dotcms/dotcms-models';
+import { DotPersona, FeaturedFlags } from '@dotcms/dotcms-models';
import { CommonErrors } from './enums';
import { CommonErrorsInfo } from './models';
@@ -67,3 +67,5 @@ export const DEFAULT_PERSONA: DotPersona = {
hasLiveVersion: false,
modUser: 'system'
};
+
+export const UVE_FEATURE_FLAGS = [FeaturedFlags.FEATURE_FLAG_UVE_PREVIEW_MODE];
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
index 0d85461015e7..853e3801d16a 100644
--- a/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/shared/mocks.ts
@@ -3,7 +3,8 @@ import { of } from 'rxjs';
import {
DEFAULT_VARIANT_ID,
DotPageContainerStructure,
- CONTAINER_SOURCE
+ CONTAINER_SOURCE,
+ FeaturedFlags
} from '@dotcms/dotcms-models';
import {
mockSites,
@@ -956,3 +957,10 @@ export const UVE_PAGE_RESPONSE_MAP = {
containers: dotPageContainerStructureMock
})
};
+
+export const dotPropertiesServiceMock = {
+ getFeatureFlags: () =>
+ of({
+ [FeaturedFlags.FEATURE_FLAG_UVE_PREVIEW_MODE]: false
+ })
+};
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 c7297b633cd6..89ca17aa9b41 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
@@ -16,7 +16,8 @@ import {
DotExperimentsService,
DotLanguagesService,
DotLicenseService,
- DotMessageService
+ DotMessageService,
+ DotPropertiesService
} from '@dotcms/data-access';
import { LoginService } from '@dotcms/dotcms-js';
import {
@@ -37,6 +38,7 @@ import { UVE_STATUS } from '../shared/enums';
import {
BASE_SHELL_ITEMS,
BASE_SHELL_PROPS_RESPONSE,
+ dotPropertiesServiceMock,
HEADLESS_BASE_QUERY_PARAMS,
MOCK_RESPONSE_HEADLESS,
MOCK_RESPONSE_VTL,
@@ -65,6 +67,10 @@ describe('UVEStore', () => {
MessageService,
mockProvider(Router),
mockProvider(ActivatedRoute),
+ {
+ provide: DotPropertiesService,
+ useValue: dotPropertiesServiceMock
+ },
{
provide: DotPageApiService,
useValue: {
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 3001d63aaa00..be75b88eef5d 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
@@ -3,11 +3,13 @@ import { patchState, signalStore, withComputed, withMethods, withState } from '@
import { computed } from '@angular/core';
import { withEditor } from './features/editor/withEditor';
+import { withFlags } from './features/flags/withFlags';
import { withLayout } from './features/layout/withLayout';
import { withLoad } from './features/load/withLoad';
import { ShellProps, TranslateProps, UVEState } from './models';
import { DotPageApiResponse } from '../services/dot-page-api.service';
+import { UVE_FEATURE_FLAGS } from '../shared/consts';
import { UVE_STATUS } from '../shared/enums';
import { getErrorPayload, getRequestHostName, sanitizeURL } from '../utils';
@@ -145,5 +147,6 @@ export const UVEStore = signalStore(
}),
withLoad(),
withLayout(),
- withEditor()
+ withEditor(),
+ withFlags(UVE_FEATURE_FLAGS)
);
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/models.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/models.ts
new file mode 100644
index 000000000000..42383fa35a25
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/models.ts
@@ -0,0 +1,7 @@
+import { FeaturedFlags } from '@dotcms/dotcms-models';
+
+export type UVEFlags = { [key in FeaturedFlags]?: boolean };
+
+export interface WithFlagsState {
+ flags: UVEFlags;
+}
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.spec.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.spec.ts
new file mode 100644
index 000000000000..6bd7d6947d4f
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.spec.ts
@@ -0,0 +1,89 @@
+import { describe } from '@jest/globals';
+import { createServiceFactory, SpectatorService } from '@ngneat/spectator/jest';
+import { signalStore, withState } from '@ngrx/signals';
+import { of } from 'rxjs';
+
+import { DotPropertiesService } from '@dotcms/data-access';
+import { FeaturedFlags } from '@dotcms/dotcms-models';
+
+import { withFlags } from './withFlags';
+
+import { DotPageApiParams } from '../../../services/dot-page-api.service';
+import { UVE_FEATURE_FLAGS } from '../../../shared/consts';
+import { UVE_STATUS } from '../../../shared/enums';
+import { UVEState } from '../../models';
+
+const initialState: UVEState = {
+ isEnterprise: false,
+ languages: [],
+ pageAPIResponse: null,
+ currentUser: null,
+ experiment: null,
+ errorCode: null,
+ params: {} as DotPageApiParams,
+ status: UVE_STATUS.LOADING,
+ isTraditionalPage: true,
+ canEditPage: false,
+ pageIsLocked: true
+};
+
+export const uveStoreMock = signalStore(
+ withState(initialState),
+ withFlags(UVE_FEATURE_FLAGS)
+);
+
+const MOCK_RESPONSE = UVE_FEATURE_FLAGS.reduce((acc, flag) => {
+ acc[flag] = true;
+
+ return acc;
+}, {});
+
+describe('withFlags', () => {
+ let spectator: SpectatorService>;
+ let store: InstanceType;
+
+ const createService = createServiceFactory({
+ service: uveStoreMock,
+ providers: [
+ {
+ provide: DotPropertiesService,
+ useValue: {
+ getFeatureFlags: jest.fn().mockReturnValue(of(MOCK_RESPONSE))
+ }
+ }
+ ]
+ });
+
+ beforeEach(() => {
+ spectator = createService();
+ store = spectator.service;
+ });
+
+ describe('onInit', () => {
+ it('should call propertiesService.getFeatureFlags with flags', () => {
+ const propertiesService = spectator.inject(DotPropertiesService);
+
+ expect(propertiesService.getFeatureFlags).toHaveBeenCalledWith(UVE_FEATURE_FLAGS);
+ });
+
+ it('should patch state with flags', () => {
+ expect(store.flags()).toEqual(MOCK_RESPONSE);
+ });
+ });
+ describe('methods', () => {
+ describe('setFlags', () => {
+ it('should patch state with flags', () => {
+ store.setFlags(MOCK_RESPONSE);
+
+ expect(store.flags()).toEqual(MOCK_RESPONSE);
+ });
+ });
+ });
+ describe('computed', () => {
+ it('should return $previewMode', () => {
+ expect(store.$previewMode()).toEqual(
+ MOCK_RESPONSE[FeaturedFlags.FEATURE_FLAG_UVE_PREVIEW_MODE]
+ );
+ });
+ });
+});
diff --git a/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.ts b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.ts
new file mode 100644
index 000000000000..91eee9336f04
--- /dev/null
+++ b/core-web/libs/portlets/edit-ema/portlet/src/lib/store/features/flags/withFlags.ts
@@ -0,0 +1,62 @@
+import {
+ patchState,
+ signalStoreFeature,
+ type,
+ withComputed,
+ withHooks,
+ withMethods,
+ withState
+} from '@ngrx/signals';
+
+import { computed, inject } from '@angular/core';
+
+import { take } from 'rxjs/operators';
+
+import { DotPropertiesService } from '@dotcms/data-access';
+import { FeaturedFlags } from '@dotcms/dotcms-models';
+
+import { UVEFlags, WithFlagsState } from './models';
+
+import { UVEState } from '../../models';
+
+/**
+ *
+ * @description This feature is used to handle the fetch of flags
+ * @export
+ * @return {*}
+ */
+export function withFlags(flags: FeaturedFlags[]) {
+ return signalStoreFeature(
+ {
+ state: type()
+ },
+ withState({ flags: {} }),
+ withComputed(({ flags }) => {
+ return {
+ // You can add here more computed properties if needed
+ $previewMode: computed(() => {
+ const currentFlags = flags();
+
+ return Boolean(currentFlags[FeaturedFlags.FEATURE_FLAG_UVE_PREVIEW_MODE]);
+ })
+ };
+ }),
+ withMethods((store) => ({
+ setFlags: (flags: UVEFlags) => {
+ patchState(store, { flags: { ...flags } });
+ }
+ })),
+ withHooks({
+ onInit: (store) => {
+ const propertiesService = inject(DotPropertiesService);
+
+ propertiesService
+ .getFeatureFlags(flags)
+ .pipe(take(1))
+ .subscribe((flags) => {
+ store.setFlags(flags);
+ });
+ }
+ })
+ );
+}
diff --git a/dotCMS/src/main/java/com/dotcms/rest/api/v1/system/ConfigurationResource.java b/dotCMS/src/main/java/com/dotcms/rest/api/v1/system/ConfigurationResource.java
index a02b51632567..05cb7697c0f0 100644
--- a/dotCMS/src/main/java/com/dotcms/rest/api/v1/system/ConfigurationResource.java
+++ b/dotCMS/src/main/java/com/dotcms/rest/api/v1/system/ConfigurationResource.java
@@ -65,7 +65,7 @@ public class ConfigurationResource implements Serializable {
new String[] {"EMAIL_SYSTEM_ADDRESS", "WYSIWYG_IMAGE_URL_PATTERN", "CHARSET","CONTENT_PALETTE_HIDDEN_CONTENT_TYPES",
"FEATURE_FLAG_EXPERIMENTS", "DOTFAVORITEPAGE_FEATURE_ENABLE", "FEATURE_FLAG_TEMPLATE_BUILDER_2",
"SHOW_VIDEO_THUMBNAIL", "EXPERIMENTS_MIN_DURATION", "EXPERIMENTS_MAX_DURATION", "EXPERIMENTS_DEFAULT_DURATION", "FEATURE_FLAG_SEO_IMPROVEMENTS",
- "FEATURE_FLAG_SEO_PAGE_TOOLS", "FEATURE_FLAG_EDIT_URL_CONTENT_MAP", "CONTENT_EDITOR2_ENABLED", "CONTENT_EDITOR2_CONTENT_TYPE", "FEATURE_FLAG_NEW_BINARY_FIELD", "FEATURE_FLAG_ANNOUNCEMENTS", "FEATURE_FLAG_NEW_EDIT_PAGE" }));
+ "FEATURE_FLAG_SEO_PAGE_TOOLS", "FEATURE_FLAG_EDIT_URL_CONTENT_MAP", "CONTENT_EDITOR2_ENABLED", "CONTENT_EDITOR2_CONTENT_TYPE", "FEATURE_FLAG_NEW_BINARY_FIELD", "FEATURE_FLAG_ANNOUNCEMENTS", "FEATURE_FLAG_NEW_EDIT_PAGE", "FEATURE_FLAG_UVE_PREVIEW_MODE" }));
private boolean isOnBlackList(final String key) {