Skip to content

Commit 86f71db

Browse files
authored
implementation (edit content): Enable “Revert” link to disable new content editor and reload old editor modal dotCMS#32123 (dotCMS#32188)
### Proposed Changes * Enable “Revert” link to disable new content editor and reload old editor modal ### Screenshots https://github.com/user-attachments/assets/382b59bc-8889-41c1-99c9-0ff06f7bab2d
1 parent 67a16fd commit 86f71db

File tree

7 files changed

+165
-10
lines changed

7 files changed

+165
-10
lines changed

core-web/libs/data-access/src/lib/dot-content-type/dot-content-type.service.spec.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,29 @@ describe('DotContentletService', () => {
212212
req.flush({ entity: [contenttypeA, contentTypeB] });
213213
});
214214

215+
it('should update the content type ', (done) => {
216+
const id = 'test-id-123';
217+
const payload = { title: 'Updated Content Type', description: 'Updated description' };
218+
const contentTypeExpected: DotCMSContentType = {
219+
...dotcmsContentTypeBasicMock,
220+
id,
221+
title: payload.title,
222+
description: payload.description
223+
};
224+
225+
dotContentTypeService
226+
.updateContentType(id, payload)
227+
.subscribe((contentType: DotCMSContentType) => {
228+
expect(contentType).toEqual(contentTypeExpected);
229+
done();
230+
});
231+
232+
const req = httpMock.expectOne(`/api/v1/contenttype/id/${id}`);
233+
expect(req.request.method).toBe('PUT');
234+
expect(req.request.body).toEqual(payload);
235+
req.flush({ entity: contentTypeExpected });
236+
});
237+
215238
afterEach(() => {
216239
httpMock.verify();
217240
});

core-web/libs/data-access/src/lib/dot-content-type/dot-content-type.service.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ export class DotContentTypeService {
158158
.pipe(pluck('entity'));
159159
}
160160

161+
/**
162+
* Updates a content type by its ID with the provided payload.
163+
*
164+
* This method allows updating any property of a content type by sending a partial or full payload.
165+
* The payload should match the expected structure for the content type update API.
166+
*
167+
* @param id The unique identifier of the content type to update.
168+
* @param payload The data to update the content type with. This can be a partial or full content type object.
169+
* @returns Observable<DotCMSContentType> The updated content type.
170+
* @memberof DotContentTypeService
171+
*/
172+
updateContentType(id: string, payload: unknown): Observable<DotCMSContentType> {
173+
return this.#httpClient
174+
.put<{ entity: DotCMSContentType }>(`/api/v1/contenttype/id/${id}`, payload)
175+
.pipe(pluck('entity'));
176+
}
177+
161178
private isRecentContentType(type: StructureTypeView): boolean {
162179
return type.name.startsWith('RECENT');
163180
}

core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.html

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,8 @@
3030
data-testId="edit-content-layout__beta-message-content">
3131
{{ 'edit.content.layout.back.to.old.edit.content' | dm }}
3232
<a
33-
[routerLink]="['/content-types-angular/edit', variable]"
34-
[queryParams]="{
35-
'open-config': 'true'
36-
}"
33+
href="#"
34+
(click)="$event.preventDefault(); $store.disableNewContentEditor()"
3735
data-testId="edit-content-layout__beta-message-link">
3836
{{ 'edit.content.layout.back.to.old.edit.content.switch' | dm }}
3937
</a>

core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.spec.ts

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,8 @@ describe('EditContentLayoutComponent', () => {
132132
activeSidebarTab: 0,
133133
isBetaMessageVisible: true
134134
});
135+
136+
dotContentTypeService.updateContentType.mockReturnValue(of(CONTENT_TYPE_MOCK));
135137
});
136138

137139
it('should have p-confirmDialog component', () => {
@@ -218,9 +220,22 @@ describe('EditContentLayoutComponent', () => {
218220
await spectator.fixture.whenStable();
219221
spectator.detectChanges();
220222

221-
expect(
222-
spectator.query(byTestId('edit-content-layout__beta-message-link'))
223-
).toExist();
223+
// Create a fake event with preventDefault
224+
const event = new MouseEvent('click');
225+
Object.defineProperty(event, 'preventDefault', { value: jest.fn() });
226+
227+
// Spy on the store method
228+
const disableNewContentEditorSpy = jest.spyOn(store, 'disableNewContentEditor');
229+
230+
const link = spectator.query(
231+
byTestId('edit-content-layout__beta-message-link')
232+
) as HTMLAnchorElement;
233+
234+
// Dispatch the event
235+
link.dispatchEvent(event);
236+
237+
expect(event.preventDefault).toHaveBeenCalled();
238+
expect(disableNewContentEditorSpy).toHaveBeenCalled();
224239
});
225240
});
226241

core-web/libs/edit-content/src/lib/components/dot-edit-content-layout/dot-edit-content.layout.component.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { ChangeDetectionStrategy, Component, inject, model } from '@angular/core';
2-
import { RouterLink } from '@angular/router';
32

43
import { ButtonModule } from 'primeng/button';
54
import { ConfirmDialogModule } from 'primeng/confirmdialog';
@@ -34,7 +33,6 @@ import { DotEditContentSidebarComponent } from '../dot-edit-content-sidebar/dot-
3433
ButtonModule,
3534
ToastModule,
3635
MessagesModule,
37-
RouterLink,
3836
DotEditContentFormComponent,
3937
DotEditContentSidebarComponent,
4038
ConfirmDialogModule

core-web/libs/edit-content/src/lib/store/features/content/content.feature.spec.ts

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable @typescript-eslint/no-explicit-any */
22

33
import { createServiceFactory, SpectatorService, SpyObject } from '@ngneat/spectator/jest';
4-
import { signalStore, withState } from '@ngrx/signals';
4+
import { signalStore, withState, patchState } from '@ngrx/signals';
55
import { of, throwError } from 'rxjs';
66

77
import { HttpErrorResponse } from '@angular/common/http';
@@ -386,4 +386,68 @@ describe('ContentFeature', () => {
386386
expect(store.initialContentletState()).toBe('reset');
387387
}));
388388
});
389+
390+
describe('disableNewContentEditor', () => {
391+
const mockContentlet = {
392+
inode: '123',
393+
stInode: 'st-123',
394+
contentType: 'testContentType'
395+
} as any;
396+
397+
beforeEach(() => {
398+
// Set up the store to have a contentlet
399+
patchState(store, { contentlet: mockContentlet });
400+
});
401+
402+
it('should call updateContentType and navigate to legacy edit page on success', fakeAsync(() => {
403+
// Arrange
404+
const workflow1 = {
405+
archived: false,
406+
creationDate: new Date(),
407+
defaultScheme: false,
408+
description: 'desc',
409+
entryActionId: null,
410+
id: 'workflow-1',
411+
mandatory: false,
412+
modDate: new Date(),
413+
name: 'Workflow 1',
414+
system: false
415+
};
416+
const workflow2 = {
417+
archived: false,
418+
creationDate: new Date(),
419+
defaultScheme: false,
420+
description: 'desc2',
421+
entryActionId: null,
422+
id: 'workflow-2',
423+
mandatory: false,
424+
modDate: new Date(),
425+
name: 'Workflow 2',
426+
system: false
427+
};
428+
const contentType = {
429+
...CONTENT_TYPE_MOCK,
430+
id: 'st-123',
431+
workflows: [workflow1, workflow2],
432+
metadata: { foo: 'bar', CONTENT_EDITOR2_ENABLED: true }
433+
};
434+
patchState(store, { contentType });
435+
contentTypeService.updateContentType.mockReturnValue(of(contentType));
436+
437+
// Act
438+
store.disableNewContentEditor();
439+
tick();
440+
441+
// Assert
442+
expect(contentTypeService.updateContentType).toHaveBeenCalledWith('st-123', {
443+
...contentType,
444+
metadata: {
445+
...contentType.metadata,
446+
CONTENT_EDITOR2_ENABLED: false
447+
},
448+
workflow: contentType.workflows.map((w: any) => w.id)
449+
});
450+
expect(router.navigate).toHaveBeenCalledWith([`/c/content/`, '123']);
451+
}));
452+
});
389453
});

core-web/libs/edit-content/src/lib/store/features/content/content.feature.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,46 @@ export function withContent() {
280280
);
281281
})
282282
)
283+
),
284+
285+
/**
286+
* Disables the new content editor for the current content type by updating the CONTENT_EDITOR2_ENABLED flag to false in the content type's metadata.
287+
*
288+
* This method retrieves the current contentlet from the store, constructs the appropriate payload, and calls the content type update API.
289+
* On success, it redirects the user to the legacy edit content page for the content type with the configuration panel open.
290+
* On error, it displays an error message using the DotHttpErrorManagerService.
291+
*/
292+
disableNewContentEditor: rxMethod<void>(
293+
pipe(
294+
switchMap(() => {
295+
// Access the contentlet from the store and its id before calling the API
296+
const contentlet = store.contentlet();
297+
const contentType = store.contentType();
298+
299+
const payload = {
300+
...contentType,
301+
metadata: {
302+
...contentType.metadata,
303+
CONTENT_EDITOR2_ENABLED: false
304+
},
305+
workflow: contentType.workflows.map((workflow) => workflow.id) // The Content Types endpoint returns workflows (plural) but receive workflow (singular)
306+
};
307+
308+
return dotContentTypeService
309+
.updateContentType(contentType.id, payload)
310+
.pipe(
311+
tapResponse({
312+
next: () => {
313+
// Redirect to legacy edit content page
314+
router.navigate([`/c/content/`, contentlet.inode]);
315+
},
316+
error: (error: HttpErrorResponse) => {
317+
dotHttpErrorManagerService.handle(error);
318+
}
319+
})
320+
);
321+
})
322+
)
283323
)
284324
})
285325
)

0 commit comments

Comments
 (0)