diff --git a/core-web/apps/dotcms-ui/project.json b/core-web/apps/dotcms-ui/project.json
index 419b3e4c7570..a9b08db9a6ad 100644
--- a/core-web/apps/dotcms-ui/project.json
+++ b/core-web/apps/dotcms-ui/project.json
@@ -18,6 +18,11 @@
"assets": [
"apps/dotcms-ui/src/favicon.ico",
"apps/dotcms-ui/src/assets",
+ {
+ "glob": "**/*",
+ "input": "node_modules/tinymce",
+ "output": "/tinymce/"
+ },
{
"glob": "**/*",
"input": "node_modules/monaco-editor",
diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
index aaf23a98b415..f0295e60e405 100644
--- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
+++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.html
@@ -77,6 +77,11 @@
*ngSwitchCase="fieldTypes.KEY_VALUE"
[formControlName]="field.variable"
[attr.data-testId]="'field-' + field.variable" />
+
+
{{ field.hint }}
diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.spec.ts b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.spec.ts
index ded8eb8f6c7f..333f08b83358 100644
--- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.spec.ts
+++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.spec.ts
@@ -1,5 +1,6 @@
import { describe } from '@jest/globals';
import { byTestId, createComponentFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
+import { EditorComponent } from '@tinymce/tinymce-angular';
import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
@@ -30,6 +31,7 @@ import { DotEditContentSelectFieldComponent } from '../../fields/dot-edit-conten
import { DotEditContentTagFieldComponent } from '../../fields/dot-edit-content-tag-field/dot-edit-content-tag-field.component';
import { DotEditContentTextAreaComponent } from '../../fields/dot-edit-content-text-area/dot-edit-content-text-area.component';
import { DotEditContentTextFieldComponent } from '../../fields/dot-edit-content-text-field/dot-edit-content-text-field.component';
+import { DotWYSIWYGFieldComponent } from '../../fields/dot-wysiwyg-field/dot-wysiwyg-field.component';
import { FIELD_TYPES } from '../../models/dot-edit-content-field.enum';
import { DotEditContentService } from '../../services/dot-edit-content.service';
import {
@@ -122,6 +124,10 @@ const FIELD_TYPES_COMPONENTS: Record | DotEditFieldTe
component: DotEditContentKeyValueComponent,
declarations: [MockComponent(DotKeyValueComponent)],
providers: [mockProvider(DotMessageDisplayService)]
+ },
+ [FIELD_TYPES.WYSIWYG]: {
+ component: DotWYSIWYGFieldComponent,
+ declarations: [MockComponent(EditorComponent)]
}
};
diff --git a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.ts b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.ts
index eb97cc58e43b..34ebe3afe029 100644
--- a/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.ts
+++ b/core-web/libs/edit-content/src/lib/components/dot-edit-content-field/dot-edit-content-field.component.ts
@@ -9,6 +9,7 @@ import { DotFieldRequiredDirective } from '@dotcms/ui';
import { DotEditContentBinaryFieldComponent } from '../../fields/dot-edit-content-binary-field/dot-edit-content-binary-field.component';
import { DotEditContentFieldsModule } from '../../fields/dot-edit-content-fields.module';
import { DotEditContentKeyValueComponent } from '../../fields/dot-edit-content-key-value/dot-edit-content-key-value.component';
+import { DotWYSIWYGFieldComponent } from '../../fields/dot-wysiwyg-field/dot-wysiwyg-field.component';
import { CALENDAR_FIELD_TYPES } from '../../models/dot-edit-content-field.constant';
import { FIELD_TYPES } from '../../models/dot-edit-content-field.enum';
@@ -33,7 +34,8 @@ import { FIELD_TYPES } from '../../models/dot-edit-content-field.enum';
DotFieldRequiredDirective,
BlockEditorModule,
DotEditContentBinaryFieldComponent,
- DotEditContentKeyValueComponent
+ DotEditContentKeyValueComponent,
+ DotWYSIWYGFieldComponent
]
})
export class DotEditContentFieldComponent {
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.html b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.html
new file mode 100644
index 000000000000..a12bdf282cbf
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.html
@@ -0,0 +1 @@
+
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.scss b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.scss
new file mode 100644
index 000000000000..b10802be3462
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.scss
@@ -0,0 +1,7 @@
+:host::ng-deep {
+ // Hide the promotion button
+ // This button redirect to the tinyMCE premium page
+ .tox-promotion {
+ display: none;
+ }
+}
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.spec.ts b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.spec.ts
new file mode 100644
index 000000000000..4e30beac1231
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.spec.ts
@@ -0,0 +1,50 @@
+import { Spectator, createComponentFactory } from '@ngneat/spectator';
+import { EditorComponent, EditorModule } from '@tinymce/tinymce-angular';
+import { MockComponent } from 'ng-mocks';
+
+import {
+ ControlContainer,
+ FormGroupDirective,
+ FormsModule,
+ ReactiveFormsModule
+} from '@angular/forms';
+
+import { DotWYSIWYGFieldComponent } from './dot-wysiwyg-field.component';
+
+import { WYSIWYG_MOCK, createFormGroupDirectiveMock } from '../../utils/mocks';
+
+const ALL_PLUGINS =
+ 'advlist autolink lists link image charmap preview anchor pagebreak searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking save table directionality emoticons template';
+const ALL_TOOLBAR_ITEMS =
+ 'paste print textpattern | insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image hr | preview | validation media | forecolor dotimageclipboard backcolor emoticons';
+
+describe('DotWYSIWYGFieldComponent', () => {
+ let spectator: Spectator;
+ const createComponent = createComponentFactory({
+ component: DotWYSIWYGFieldComponent,
+ imports: [EditorModule, FormsModule, ReactiveFormsModule],
+ declarations: [MockComponent(EditorComponent)],
+ componentViewProviders: [
+ {
+ provide: ControlContainer,
+ useValue: createFormGroupDirectiveMock()
+ }
+ ],
+ providers: [FormGroupDirective]
+ });
+
+ beforeEach(() => {
+ spectator = createComponent({
+ props: {
+ field: WYSIWYG_MOCK
+ }
+ });
+ });
+
+ it('should instance WYSIWYG editor and set the correct plugins and toolbar items', () => {
+ const editor = spectator.query(EditorComponent);
+ expect(editor).toBeTruthy();
+ expect(editor.plugins).toEqual(ALL_PLUGINS);
+ expect(editor.toolbar).toEqual(ALL_TOOLBAR_ITEMS);
+ });
+});
diff --git a/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.ts b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.ts
new file mode 100644
index 000000000000..7d01bc07171d
--- /dev/null
+++ b/core-web/libs/edit-content/src/lib/fields/dot-wysiwyg-field/dot-wysiwyg-field.component.ts
@@ -0,0 +1,32 @@
+import { EditorModule, TINYMCE_SCRIPT_SRC } from '@tinymce/tinymce-angular';
+
+import { ChangeDetectionStrategy, Component, Input, inject, signal } from '@angular/core';
+import { ControlContainer, FormsModule, ReactiveFormsModule } from '@angular/forms';
+
+import { DotCMSContentTypeField } from '@dotcms/dotcms-models';
+
+@Component({
+ selector: 'dot-wysiwyg-field',
+ standalone: true,
+ imports: [EditorModule, FormsModule, ReactiveFormsModule],
+ templateUrl: './dot-wysiwyg-field.component.html',
+ styleUrl: './dot-wysiwyg-field.component.scss',
+ changeDetection: ChangeDetectionStrategy.OnPush,
+ providers: [{ provide: TINYMCE_SCRIPT_SRC, useValue: 'tinymce/tinymce.min.js' }],
+ viewProviders: [
+ {
+ provide: ControlContainer,
+ useFactory: () => inject(ControlContainer, { skipSelf: true })
+ }
+ ]
+})
+export class DotWYSIWYGFieldComponent {
+ @Input() field!: DotCMSContentTypeField;
+
+ protected readonly plugins = signal(
+ 'advlist autolink lists link image charmap preview anchor pagebreak searchreplace wordcount visualblocks visualchars code fullscreen insertdatetime media nonbreaking save table directionality emoticons template'
+ );
+ protected readonly toolbar = signal(
+ 'paste print textpattern | insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image hr | preview | validation media | forecolor dotimageclipboard backcolor emoticons'
+ );
+}
diff --git a/core-web/libs/edit-content/src/lib/models/dot-edit-content-field.enum.ts b/core-web/libs/edit-content/src/lib/models/dot-edit-content-field.enum.ts
index e07f833a9dee..7fc7ecfb8683 100644
--- a/core-web/libs/edit-content/src/lib/models/dot-edit-content-field.enum.ts
+++ b/core-web/libs/edit-content/src/lib/models/dot-edit-content-field.enum.ts
@@ -23,5 +23,6 @@ export enum FIELD_TYPES {
BINARY = 'Binary',
CUSTOM_FIELD = 'Custom-Field',
JSON = 'JSON-Field',
- KEY_VALUE = 'Key-Value'
+ KEY_VALUE = 'Key-Value',
+ WYSIWYG = 'WYSIWYG'
}
diff --git a/core-web/libs/edit-content/src/lib/utils/mocks.ts b/core-web/libs/edit-content/src/lib/utils/mocks.ts
index 6b21333419ae..8fa94bc86b29 100644
--- a/core-web/libs/edit-content/src/lib/utils/mocks.ts
+++ b/core-web/libs/edit-content/src/lib/utils/mocks.ts
@@ -539,6 +539,30 @@ export const KEY_VALUE_MOCK: DotCMSContentTypeField = {
variable: 'KeyValue'
};
+export const WYSIWYG_MOCK: DotCMSContentTypeField = {
+ clazz: 'com.dotcms.contenttype.model.field.ImmutableWYSIWYGField',
+ contentTypeId: '93ebaff75f3e3887bea73ecd04588dc9',
+ dataType: 'TEXT',
+ fieldType: 'WYSIWYG',
+ fieldTypeLabel: 'WYSIWYG',
+ fieldVariables: [],
+ fixed: false,
+ hint: 'A hint text',
+ iDate: 1698291913000,
+ id: '96909fa20a00497cd3b766b52edac0ec',
+ indexed: false,
+ listed: false,
+ modDate: 1698291913000,
+ name: 'WYSIWYG',
+ readOnly: false,
+ required: false,
+ searchable: false,
+ sortOrder: 1,
+ unique: false,
+ values: 'HELLO
',
+ variable: 'WYSIWYG'
+};
+
export const FIELDS_MOCK: DotCMSContentTypeField[] = [
TEXT_FIELD_MOCK,
TEXT_AREA_FIELD_MOCK,
@@ -560,7 +584,8 @@ export const FIELDS_MOCK: DotCMSContentTypeField[] = [
BINARY_FIELD_MOCK,
CUSTOM_FIELD_MOCK,
JSON_FIELD_MOCK,
- KEY_VALUE_MOCK
+ KEY_VALUE_MOCK,
+ WYSIWYG_MOCK
];
export const FIELD_MOCK: DotCMSContentTypeField = TEXT_FIELD_MOCK;
diff --git a/core-web/package.json b/core-web/package.json
index 5ab9bc8ee8d9..a4d6128a2032 100644
--- a/core-web/package.json
+++ b/core-web/package.json
@@ -73,6 +73,7 @@
"@nx/angular": "18.0.4",
"@swc/helpers": "~0.5.2",
"@tarekraafat/autocomplete.js": "^10.2.6",
+ "@tinymce/tinymce-angular": "^7.0.0",
"@tiptap/core": "^2.0.0-beta.218",
"@tiptap/extension-bubble-menu": "^2.0.0-beta.218",
"@tiptap/extension-character-count": "^2.0.0-beta.218",
@@ -126,6 +127,7 @@
"superstruct": "^1.0.3",
"terser": "^5.28.1",
"test": "^0.6.0",
+ "tinymce": "^6.8.3",
"tslib": "^2.3.0",
"uuid": "^9.0.0",
"zone.js": "0.14.2"
diff --git a/core-web/yarn.lock b/core-web/yarn.lock
index a22f0d8615fd..10d511ad4a20 100644
--- a/core-web/yarn.lock
+++ b/core-web/yarn.lock
@@ -6122,6 +6122,14 @@
resolved "https://registry.npmjs.org/@thednp/shorty/-/shorty-2.0.0.tgz#e05e6754032c091f599ac0c4ed7efde6558bdedb"
integrity sha512-kwtLivCxYIoFfGIVU4NlZtfdA/zxZ6X8UcWaJrb7XqU3WQ4Q1p5IaZlLBfOVAO06WH5oWE87QUdK/dS56Wnfjg==
+"@tinymce/tinymce-angular@^7.0.0":
+ version "7.0.0"
+ resolved "https://registry.npmjs.org/@tinymce/tinymce-angular/-/tinymce-angular-7.0.0.tgz#010de497d5774a8bdc5d5936bf4fb976adf05f56"
+ integrity sha512-IKNaG/ihlxE1XCfq6lzULbnsqZO9KNJtlpu5jo6JDJDL9zcFzj/N2A16Kk7rTj1yfmDoB1IXAk/BpMOvgDY8cg==
+ dependencies:
+ tinymce "^6.0.0 || ^5.5.0"
+ tslib "^2.3.0"
+
"@tiptap/core@^2.0.0-beta.218", "@tiptap/core@^2.2.2":
version "2.2.2"
resolved "https://registry.npmjs.org/@tiptap/core/-/core-2.2.2.tgz#7664197dafee890a5f42ba03b50c202bdf1da761"
@@ -21994,6 +22002,11 @@ tiny-relative-date@^1.3.0:
resolved "https://registry.npmjs.org/tiny-relative-date/-/tiny-relative-date-1.3.0.tgz#fa08aad501ed730f31cc043181d995c39a935e07"
integrity sha512-MOQHpzllWxDCHHaDno30hhLfbouoYlOI8YlMNtvKe1zXbjEVhbcEovQxvZrPvtiYW630GQDoMMarCnjfyfHA+A==
+"tinymce@^6.0.0 || ^5.5.0", tinymce@^6.8.3:
+ version "6.8.3"
+ resolved "https://registry.npmjs.org/tinymce/-/tinymce-6.8.3.tgz#0025a4aaa4c24dc2a3e32e83dfda705d196fd802"
+ integrity sha512-3fCHKAeqT+xNwBVESf6iDbDV0VNwZNmfrkx9c/6Gz5iB8piMfaO6s7FvoiTrj1hf1gVbfyLTnz1DooI6DhgINQ==
+
tippy.js@^6.3.7:
version "6.3.7"
resolved "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"